Merge "Add odm sepolicy support to SELinuxMMAC.java"
diff --git a/Android.bp b/Android.bp
index facc741..69fb459 100644
--- a/Android.bp
+++ b/Android.bp
@@ -200,6 +200,11 @@
         "core/java/android/nfc/INfcUnlockHandler.aidl",
         "core/java/android/nfc/INfcDta.aidl",
         "core/java/android/nfc/ITagRemovedCallback.aidl",
+        "core/java/android/se/omapi/ISecureElementService.aidl",
+        "core/java/android/se/omapi/ISecureElementListener.aidl",
+        "core/java/android/se/omapi/ISecureElementChannel.aidl",
+        "core/java/android/se/omapi/ISecureElementReader.aidl",
+        "core/java/android/se/omapi/ISecureElementSession.aidl",
         "core/java/android/os/IBatteryPropertiesListener.aidl",
         "core/java/android/os/IBatteryPropertiesRegistrar.aidl",
         "core/java/android/os/ICancellationSignal.aidl",
@@ -455,6 +460,8 @@
         "telecomm/java/com/android/internal/telecom/IInCallService.aidl",
         "telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
         "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
+	"telephony/java/android/telephony/data/IDataService.aidl",
+	"telephony/java/android/telephony/data/IDataServiceCallback.aidl",
         "telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl",
         "telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl",
         "telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl",
@@ -462,8 +469,6 @@
         "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl",
         "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl",
         "telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl",
         "telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl",
         "telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl",
 	"telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl",
@@ -473,6 +478,8 @@
         "telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
         "telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
         "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
+        "telephony/java/android/telephony/INetworkService.aidl",
+        "telephony/java/android/telephony/INetworkServiceCallback.aidl",
         "telephony/java/com/android/ims/internal/IImsCallSession.aidl",
         "telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl",
         "telephony/java/com/android/ims/internal/IImsConfig.aidl",
@@ -483,6 +490,8 @@
         "telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl",
         "telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl",
         "telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl",
+        "telephony/java/com/android/ims/internal/IImsRegistration.aidl",
+        "telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl",
         "telephony/java/com/android/ims/internal/IImsRcsFeature.aidl",
         "telephony/java/com/android/ims/internal/IImsService.aidl",
         "telephony/java/com/android/ims/internal/IImsServiceController.aidl",
@@ -510,9 +519,30 @@
         "telephony/java/com/android/internal/telephony/ITelephony.aidl",
         "telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl",
         "telephony/java/com/android/internal/telephony/IWapPushManager.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl",
         "telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl",
         "telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl",
         "telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
         "wifi/java/android/net/wifi/IWifiManager.aidl",
         "wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
         "wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl",
diff --git a/Android.mk b/Android.mk
index 2d74249..ea75b19 100644
--- a/Android.mk
+++ b/Android.mk
@@ -82,7 +82,7 @@
 # to document and check apis
 files_to_check_apis := \
   $(call find-other-java-files, \
-    legacy-test/src \
+    test-base/src \
     $(non_base_dirs) \
   )
 
@@ -280,6 +280,8 @@
 		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
 		-referenceonly \
 		-api $(INTERNAL_PLATFORM_API_FILE) \
+		-privateApi $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
+		-privateDexApi $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
 		-removedApi $(INTERNAL_PLATFORM_REMOVED_API_FILE) \
 		-nodocs
 
@@ -289,7 +291,9 @@
 
 include $(BUILD_DROIDDOC)
 
-$(INTERNAL_PLATFORM_API_FILE): $(full_target)
+$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_API_FILE) \
+                                          $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
+                                          $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
 
 # ====  the system api stubs ===================================
@@ -314,6 +318,8 @@
 		-referenceonly \
 		-showAnnotation android.annotation.SystemApi \
 		-api $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
+		-privateApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
+		-privateDexApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE) \
 		-removedApi $(INTERNAL_PLATFORM_SYSTEM_REMOVED_API_FILE) \
 		-exactApi $(INTERNAL_PLATFORM_SYSTEM_EXACT_API_FILE) \
 		-nodocs
@@ -324,7 +330,9 @@
 
 include $(BUILD_DROIDDOC)
 
-$(INTERNAL_PLATFORM_SYSTEM_API_FILE): $(full_target)
+$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
+                                          $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
+                                          $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
 
 # ====  the test api stubs ===================================
@@ -689,6 +697,38 @@
 include $(BUILD_HOST_JAVA_LIBRARY)
 
 
+# ==== hiddenapi lists =======================================
+
+# Copy blacklist and dark greylist over into the build folder.
+# This is for ART buildbots which need to mock these lists and have alternative
+# rules for building them. Other rules in the build system should depend on the
+# files in the build folder.
+
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
+                            $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\
+                            $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)))
+
+# Generate light greylist as private API minus (blacklist plus dark greylist).
+
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
+                                               $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
+                                               $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+	if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(DARK_GREYLIST))`" ]; then \
+		echo "There should be no overlap between $(BLACKLIST) and $(DARK_GREYLIST)" 1>&2; \
+		exit 1; \
+	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \
+		echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+		exit 2; \
+	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \
+		echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+		exit 3; \
+	fi
+	comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(DARK_GREYLIST)) > $@
+
 # Include subdirectory makefiles
 # ============================================================
 
@@ -698,4 +738,5 @@
 include $(call first-makefiles-under,$(LOCAL_PATH))
 endif
 
-endif # ANDROID_BUILD_EMBEDDED
\ No newline at end of file
+endif # ANDROID_BUILD_EMBEDDED
+
diff --git a/api/current.txt b/api/current.txt
index 392f4db..2b8b6f5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -92,6 +92,7 @@
     field public static final java.lang.String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
     field public static final java.lang.String MOUNT_UNMOUNT_FILESYSTEMS = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS";
     field public static final java.lang.String NFC = "android.permission.NFC";
+    field public static final java.lang.String NFC_TRANSACTION_EVENT = "android.permission.NFC_TRANSACTION_EVENT";
     field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
     field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
     field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
@@ -6949,6 +6950,7 @@
 
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
+    method public int getDefaultNetwork();
     method public long getEndTimeStamp();
     method public int getMetered();
     method public int getRoaming();
@@ -6960,6 +6962,9 @@
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
+    field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_NETWORK_NO = 1; // 0x1
+    field public static final int DEFAULT_NETWORK_YES = 2; // 0x2
     field public static final int METERED_ALL = -1; // 0xffffffff
     field public static final int METERED_NO = 1; // 0x1
     field public static final int METERED_YES = 2; // 0x2
@@ -19873,6 +19878,13 @@
     ctor public ICUUncheckedIOException(java.lang.String, java.lang.Throwable);
   }
 
+  public class IllformedLocaleException extends java.lang.RuntimeException {
+    ctor public IllformedLocaleException();
+    ctor public IllformedLocaleException(java.lang.String);
+    ctor public IllformedLocaleException(java.lang.String, int);
+    method public int getErrorIndex();
+  }
+
   public class IndianCalendar extends android.icu.util.Calendar {
     ctor public IndianCalendar();
     ctor public IndianCalendar(android.icu.util.TimeZone);
@@ -19957,6 +19969,32 @@
     field public static final int TAISHO;
   }
 
+  public final class LocaleData {
+    method public static android.icu.util.VersionInfo getCLDRVersion();
+    method public java.lang.String getDelimiter(int);
+    method public static final android.icu.util.LocaleData getInstance(android.icu.util.ULocale);
+    method public static final android.icu.util.LocaleData getInstance();
+    method public static final android.icu.util.LocaleData.MeasurementSystem getMeasurementSystem(android.icu.util.ULocale);
+    method public boolean getNoSubstitute();
+    method public static final android.icu.util.LocaleData.PaperSize getPaperSize(android.icu.util.ULocale);
+    method public void setNoSubstitute(boolean);
+    field public static final int ALT_QUOTATION_END = 3; // 0x3
+    field public static final int ALT_QUOTATION_START = 2; // 0x2
+    field public static final int QUOTATION_END = 1; // 0x1
+    field public static final int QUOTATION_START = 0; // 0x0
+  }
+
+  public static final class LocaleData.MeasurementSystem {
+    field public static final android.icu.util.LocaleData.MeasurementSystem SI;
+    field public static final android.icu.util.LocaleData.MeasurementSystem UK;
+    field public static final android.icu.util.LocaleData.MeasurementSystem US;
+  }
+
+  public static final class LocaleData.PaperSize {
+    method public int getHeight();
+    method public int getWidth();
+  }
+
   public class Measure {
     ctor public Measure(java.lang.Number, android.icu.util.MeasureUnit);
     method public java.lang.Number getNumber();
@@ -25781,12 +25819,18 @@
   }
 
   public final class IpSecManager {
-    method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
-    method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
-    method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+    method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
+    method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public void applyTransportModeTransform(java.net.Socket, int, android.net.IpSecTransform) throws java.io.IOException;
+    method public void applyTransportModeTransform(java.net.DatagramSocket, int, android.net.IpSecTransform) throws java.io.IOException;
+    method public void applyTransportModeTransform(java.io.FileDescriptor, int, android.net.IpSecTransform) throws java.io.IOException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
-    method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+    method public void removeTransportModeTransforms(java.net.Socket) throws java.io.IOException;
+    method public void removeTransportModeTransforms(java.net.DatagramSocket) throws java.io.IOException;
+    method public void removeTransportModeTransforms(java.io.FileDescriptor) throws java.io.IOException;
+    field public static final int DIRECTION_IN = 0; // 0x0
+    field public static final int DIRECTION_OUT = 1; // 0x1
   }
 
   public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
@@ -25809,18 +25853,15 @@
 
   public final class IpSecTransform implements java.lang.AutoCloseable {
     method public void close();
-    field public static final int DIRECTION_IN = 0; // 0x0
-    field public static final int DIRECTION_OUT = 1; // 0x1
   }
 
   public static class IpSecTransform.Builder {
     ctor public IpSecTransform.Builder(android.content.Context);
-    method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
-    method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(int, android.net.IpSecAlgorithm);
-    method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
-    method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+    method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(android.net.IpSecAlgorithm);
+    method public android.net.IpSecTransform.Builder setAuthentication(android.net.IpSecAlgorithm);
+    method public android.net.IpSecTransform.Builder setEncryption(android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
-    method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
   }
 
   public class LinkAddress implements android.os.Parcelable {
@@ -25840,7 +25881,9 @@
     method public android.net.ProxyInfo getHttpProxy();
     method public java.lang.String getInterfaceName();
     method public java.util.List<android.net.LinkAddress> getLinkAddresses();
+    method public java.lang.String getPrivateDnsServerName();
     method public java.util.List<android.net.RouteInfo> getRoutes();
+    method public boolean isPrivateDnsActive();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
   }
@@ -25903,10 +25946,10 @@
   }
 
   public final class MacAddress implements android.os.Parcelable {
-    method public int addressType();
     method public int describeContents();
     method public static android.net.MacAddress fromBytes(byte[]);
     method public static android.net.MacAddress fromString(java.lang.String);
+    method public int getAddressType();
     method public boolean isLocallyAssigned();
     method public byte[] toByteArray();
     method public java.lang.String toOuiString();
@@ -25916,7 +25959,6 @@
     field public static final int TYPE_BROADCAST = 3; // 0x3
     field public static final int TYPE_MULTICAST = 2; // 0x2
     field public static final int TYPE_UNICAST = 1; // 0x1
-    field public static final int TYPE_UNKNOWN = 0; // 0x0
   }
 
   public class MailTo {
@@ -30999,7 +31041,7 @@
   }
 
   public final class Debug {
-    method public static void attachJvmtiAgent(java.lang.String, java.lang.String) throws java.io.IOException;
+    method public static void attachJvmtiAgent(java.lang.String, java.lang.String, java.lang.ClassLoader) throws java.io.IOException;
     method public static deprecated void changeDebugPort(int);
     method public static void dumpHprofData(java.lang.String) throws java.io.IOException;
     method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]);
@@ -35683,6 +35725,7 @@
     field public static final int RESULT_SMS_HANDLED = 1; // 0x1
     field public static final int RESULT_SMS_OUT_OF_MEMORY = 3; // 0x3
     field public static final int RESULT_SMS_UNSUPPORTED = 4; // 0x4
+    field public static final java.lang.String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE";
     field public static final java.lang.String SIM_FULL_ACTION = "android.provider.Telephony.SIM_FULL";
     field public static final java.lang.String SMS_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_CB_RECEIVED";
     field public static final java.lang.String SMS_DELIVER_ACTION = "android.provider.Telephony.SMS_DELIVER";
@@ -36971,6 +37014,59 @@
 
 }
 
+package android.se.omapi {
+
+  public class Channel {
+    method public void close();
+    method public byte[] getSelectResponse();
+    method public android.se.omapi.Session getSession();
+    method public boolean isBasicChannel();
+    method public boolean isClosed();
+    method public boolean selectNext() throws java.io.IOException;
+    method public byte[] transmit(byte[]) throws java.io.IOException;
+  }
+
+  public abstract interface ISecureElementListener implements android.os.IInterface {
+    method public abstract void serviceConnected() throws android.os.RemoteException;
+  }
+
+  public static abstract class ISecureElementListener.Stub extends android.os.Binder implements android.se.omapi.ISecureElementListener {
+    ctor public ISecureElementListener.Stub();
+    method public android.os.IBinder asBinder();
+    method public static android.se.omapi.ISecureElementListener asInterface(android.os.IBinder);
+    method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
+  }
+
+  public class Reader {
+    method public void closeSessions();
+    method public java.lang.String getName();
+    method public android.se.omapi.SEService getSEService();
+    method public boolean isSecureElementPresent();
+    method public android.se.omapi.Session openSession() throws java.io.IOException;
+  }
+
+  public class SEService {
+    ctor public SEService(android.content.Context, android.se.omapi.ISecureElementListener);
+    method public android.se.omapi.Reader[] getReaders();
+    method public java.lang.String getVersion();
+    method public boolean isConnected();
+    method public void shutdown();
+  }
+
+  public class Session {
+    method public void close();
+    method public void closeChannels();
+    method public byte[] getATR();
+    method public android.se.omapi.Reader getReader();
+    method public boolean isClosed();
+    method public android.se.omapi.Channel openBasicChannel(byte[], byte) throws java.io.IOException;
+    method public android.se.omapi.Channel openBasicChannel(byte[]) throws java.io.IOException;
+    method public android.se.omapi.Channel openLogicalChannel(byte[], byte) throws java.io.IOException;
+    method public android.se.omapi.Channel openLogicalChannel(byte[]) throws java.io.IOException;
+  }
+
+}
+
 package android.security {
 
   public final class KeyChain {
@@ -38364,11 +38460,6 @@
     field public final int errno;
   }
 
-  public class Int32Ref {
-    ctor public Int32Ref(int);
-    field public int value;
-  }
-
   public class Int64Ref {
     ctor public Int64Ref(long);
     field public long value;
@@ -38468,7 +38559,6 @@
     method public static int umask(int);
     method public static android.system.StructUtsname uname();
     method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
-    method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
     method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -39848,6 +39938,7 @@
     field public static final int EUTRAN = 3; // 0x3
     field public static final int GERAN = 1; // 0x1
     field public static final int IWLAN = 5; // 0x5
+    field public static final int UNKNOWN = 0; // 0x0
     field public static final int UTRAN = 2; // 0x2
   }
 
@@ -39952,6 +40043,8 @@
     method public void notifyConfigChangedForSubId(int);
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
     field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
+    field public static final java.lang.String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
+    field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
     field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
     field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -40129,6 +40222,7 @@
   }
 
   public final class CellIdentityLte extends android.telephony.CellIdentity {
+    method public int getBandwidth();
     method public int getCi();
     method public int getEarfcn();
     method public deprecated int getMcc();
@@ -40172,8 +40266,13 @@
 
   public abstract class CellInfo implements android.os.Parcelable {
     method public int describeContents();
+    method public int getCellConnectionStatus();
     method public long getTimeStamp();
     method public boolean isRegistered();
+    field public static final int CONNECTION_NONE = 0; // 0x0
+    field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
+    field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
+    field public static final int CONNECTION_UNKNOWN = 2147483647; // 0x7fffffff
     field public static final android.os.Parcelable.Creator<android.telephony.CellInfo> CREATOR;
   }
 
@@ -40455,6 +40554,7 @@
     method public void onServiceStateChanged(android.telephony.ServiceState);
     method public deprecated void onSignalStrengthChanged(int);
     method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
+    method public void onUserMobileDataStateChanged(boolean);
     field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
     field public static final int LISTEN_CALL_STATE = 32; // 0x20
     field public static final int LISTEN_CELL_INFO = 1024; // 0x400
@@ -40466,6 +40566,7 @@
     field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
     field public static final deprecated int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
     field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
+    field public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000
   }
 
   public final class RadioAccessSpecifier implements android.os.Parcelable {
@@ -40484,6 +40585,9 @@
     ctor public ServiceState(android.os.Parcel);
     method protected void copyFrom(android.telephony.ServiceState);
     method public int describeContents();
+    method public int[] getCellBandwidths();
+    method public int getChannelNumber();
+    method public int getDuplexMode();
     method public boolean getIsManualSelection();
     method public int getNetworkId();
     method public java.lang.String getOperatorAlphaLong();
@@ -40500,6 +40604,9 @@
     method public void setStateOutOfService();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.ServiceState> CREATOR;
+    field public static final int DUPLEX_MODE_FDD = 1; // 0x1
+    field public static final int DUPLEX_MODE_TDD = 2; // 0x2
+    field public static final int DUPLEX_MODE_UNKNOWN = 0; // 0x0
     field public static final int STATE_EMERGENCY_ONLY = 2; // 0x2
     field public static final int STATE_IN_SERVICE = 0; // 0x0
     field public static final int STATE_OUT_OF_SERVICE = 1; // 0x1
@@ -40699,6 +40806,8 @@
     method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+    method public int getAndroidCarrierIdForSubscription();
+    method public java.lang.CharSequence getAndroidCarrierNameForSubscription();
     method public int getCallState();
     method public android.os.PersistableBundle getCarrierConfig();
     method public deprecated android.telephony.CellLocation getCellLocation();
@@ -40779,6 +40888,7 @@
     field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
     field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
     field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
+    field public static final java.lang.String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
     field public static final int APPTYPE_CSIM = 4; // 0x4
     field public static final int APPTYPE_ISIM = 5; // 0x5
     field public static final int APPTYPE_RUIM = 3; // 0x3
@@ -40799,6 +40909,8 @@
     field public static final int DATA_DISCONNECTED = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
     field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+    field public static final java.lang.String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
+    field public static final java.lang.String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
     field public static final java.lang.String EXTRA_HIDE_PUBLIC_SETTINGS = "android.telephony.extra.HIDE_PUBLIC_SETTINGS";
     field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
     field public static final java.lang.String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH";
@@ -40809,6 +40921,7 @@
     field public static final java.lang.String EXTRA_STATE_IDLE;
     field public static final java.lang.String EXTRA_STATE_OFFHOOK;
     field public static final java.lang.String EXTRA_STATE_RINGING;
+    field public static final java.lang.String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
     field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
     field public static final java.lang.String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
     field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
@@ -40844,6 +40957,7 @@
     field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3
     field public static final int SIM_STATE_READY = 5; // 0x5
     field public static final int SIM_STATE_UNKNOWN = 0; // 0x0
+    field public static final int UNKNOWN_CARRIER_ID = -1; // 0xffffffff
     field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe
     field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff
     field public static final java.lang.String VVM_TYPE_CVVM = "vvm_type_cvvm";
@@ -40943,6 +41057,7 @@
     method public java.net.InetAddress getMmsProxy();
     method public java.net.URL getMmsc();
     method public java.lang.String getMvnoType();
+    method public int getNetworkTypeBitmask();
     method public java.lang.String getOperatorNumeric();
     method public java.lang.String getPassword();
     method public int getPort();
@@ -40986,11 +41101,11 @@
     method public android.telephony.data.ApnSetting.Builder setAuthType(int);
     method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean);
     method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setId(int);
     method public android.telephony.data.ApnSetting.Builder setMmsPort(int);
     method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress);
     method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL);
     method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int);
     method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setPort(int);
@@ -44254,42 +44369,42 @@
     method public void previousMonth();
   }
 
-  public final class MutableBoolean {
+  public final deprecated class MutableBoolean {
     ctor public MutableBoolean(boolean);
     field public boolean value;
   }
 
-  public final class MutableByte {
+  public final deprecated class MutableByte {
     ctor public MutableByte(byte);
     field public byte value;
   }
 
-  public final class MutableChar {
+  public final deprecated class MutableChar {
     ctor public MutableChar(char);
     field public char value;
   }
 
-  public final class MutableDouble {
+  public final deprecated class MutableDouble {
     ctor public MutableDouble(double);
     field public double value;
   }
 
-  public final class MutableFloat {
+  public final deprecated class MutableFloat {
     ctor public MutableFloat(float);
     field public float value;
   }
 
-  public final class MutableInt {
+  public final deprecated class MutableInt {
     ctor public MutableInt(int);
     field public int value;
   }
 
-  public final class MutableLong {
+  public final deprecated class MutableLong {
     ctor public MutableLong(long);
     field public long value;
   }
 
-  public final class MutableShort {
+  public final deprecated class MutableShort {
     ctor public MutableShort(short);
     field public short value;
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 4eb5c08..041c213 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -697,6 +697,7 @@
     field public static final java.lang.String NETWORK_SCORE_SERVICE = "network_score";
     field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
     field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+    field public static final java.lang.String SECURE_ELEMENT_SERVICE = "secure_element";
     field public static final java.lang.String VR_SERVICE = "vrmanager";
     field public static final java.lang.String WIFI_RTT_SERVICE = "rttmanager";
     field public static final java.lang.String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -727,7 +728,7 @@
     field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
     field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
     field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
-    field public static final java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
+    field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
     field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
     field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
     field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
@@ -2577,9 +2578,33 @@
     method public void onTetheringStarted();
   }
 
+  public final class IpSecManager {
+    method public void applyTunnelModeTransform(android.net.IpSecManager.IpSecTunnelInterface, int, android.net.IpSecTransform) throws java.io.IOException;
+    method public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(java.net.InetAddress, java.net.InetAddress, android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+  }
+
+  public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+    method public void close();
+    method public java.lang.String getInterfaceName();
+  }
+
+  public final class IpSecTransform implements java.lang.AutoCloseable {
+    method public void startNattKeepalive(android.net.IpSecTransform.NattKeepaliveCallback, int, android.os.Handler) throws java.io.IOException;
+    method public void stopNattKeepalive();
+  }
+
   public static class IpSecTransform.Builder {
-    method public android.net.IpSecTransform.Builder setNattKeepalive(int);
-    method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network);
+    method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+  }
+
+  public static class IpSecTransform.NattKeepaliveCallback {
+    ctor public IpSecTransform.NattKeepaliveCallback();
+    method public void onError(int);
+    method public void onStarted();
+    method public void onStopped();
+    field public static final int ERROR_HARDWARE_ERROR = 3; // 0x3
+    field public static final int ERROR_HARDWARE_UNSUPPORTED = 2; // 0x2
+    field public static final int ERROR_INVALID_NETWORK = 1; // 0x1
   }
 
   public class NetworkKey implements android.os.Parcelable {
@@ -3937,6 +3962,12 @@
 
 package android.telephony {
 
+  public static final class AccessNetworkConstants.TransportType {
+    ctor public AccessNetworkConstants.TransportType();
+    field public static final int WLAN = 2; // 0x2
+    field public static final int WWAN = 1; // 0x1
+  }
+
   public class CarrierConfigManager {
     method public static android.os.PersistableBundle getDefaultConfig();
     method public void updateConfigForPhoneId(int, java.lang.String);
@@ -3951,6 +3982,65 @@
     field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
   }
 
+  public class NetworkRegistrationState implements android.os.Parcelable {
+    ctor public NetworkRegistrationState(int, int, int, int, int, boolean, int[], android.telephony.CellIdentity);
+    ctor protected NetworkRegistrationState(android.os.Parcel);
+    method public int describeContents();
+    method public int getAccessNetworkTechnology();
+    method public int[] getAvailableServices();
+    method public android.telephony.CellIdentity getCellIdentity();
+    method public int getDomain();
+    method public int getReasonForDenial();
+    method public int getRegState();
+    method public int getTransportType();
+    method public boolean isEmergencyEnabled();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationState> CREATOR;
+    field public static final int DOMAIN_CS = 1; // 0x1
+    field public static final int DOMAIN_PS = 2; // 0x2
+    field public static final int REG_STATE_DENIED = 3; // 0x3
+    field public static final int REG_STATE_HOME = 1; // 0x1
+    field public static final int REG_STATE_NOT_REG_NOT_SEARCHING = 0; // 0x0
+    field public static final int REG_STATE_NOT_REG_SEARCHING = 2; // 0x2
+    field public static final int REG_STATE_ROAMING = 5; // 0x5
+    field public static final int REG_STATE_UNKNOWN = 4; // 0x4
+    field public static final int SERVICE_TYPE_DATA = 2; // 0x2
+    field public static final int SERVICE_TYPE_EMERGENCY = 5; // 0x5
+    field public static final int SERVICE_TYPE_SMS = 3; // 0x3
+    field public static final int SERVICE_TYPE_VIDEO = 4; // 0x4
+    field public static final int SERVICE_TYPE_VOICE = 1; // 0x1
+  }
+
+  public abstract class NetworkService extends android.app.Service {
+    method protected abstract android.telephony.NetworkService.NetworkServiceProvider createNetworkServiceProvider(int);
+    field public static final java.lang.String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
+    field public static final java.lang.String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
+  }
+
+  public class NetworkService.NetworkServiceProvider {
+    ctor public NetworkService.NetworkServiceProvider(int);
+    method public void getNetworkRegistrationState(int, android.telephony.NetworkServiceCallback);
+    method public final int getSlotId();
+    method public final void notifyNetworkRegistrationStateChanged();
+    method protected void onDestroy();
+  }
+
+  public class NetworkServiceCallback {
+    method public void onGetNetworkRegistrationStateComplete(int, android.telephony.NetworkRegistrationState);
+    field public static final int RESULT_ERROR_BUSY = 3; // 0x3
+    field public static final int RESULT_ERROR_FAILED = 5; // 0x5
+    field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
+    field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
+    field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  public class ServiceState implements android.os.Parcelable {
+    method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
+    method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
+    method public android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+  }
+
   public final class SmsManager {
     method public void sendMultipartTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
     method public void sendTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
@@ -4034,7 +4124,10 @@
     method public int getCurrentPhoneType(int);
     method public deprecated boolean getDataEnabled();
     method public deprecated boolean getDataEnabled(int);
+    method public int getSimApplicationState();
+    method public int getSimCardState();
     method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
+    method public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
     method public android.os.Bundle getVisualVoicemailSettings();
     method public boolean handlePinMmi(java.lang.String);
     method public boolean handlePinMmiForSubscriber(int, java.lang.String);
@@ -4056,14 +4149,40 @@
     method public int[] supplyPinReportResult(java.lang.String);
     method public boolean supplyPuk(java.lang.String, java.lang.String);
     method public int[] supplyPukReportResult(java.lang.String, java.lang.String);
+    method public boolean switchSlots(int[]);
     method public void toggleRadioOnOff();
     method public void updateServiceLocation();
+    field public static final java.lang.String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
+    field public static final java.lang.String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
+    field public static final java.lang.String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+    field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
     field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
     field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+    field public static final int SIM_STATE_LOADED = 10; // 0xa
+    field public static final int SIM_STATE_PRESENT = 11; // 0xb
+  }
+
+  public class UiccSlotInfo implements android.os.Parcelable {
+    ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int);
+    method public int describeContents();
+    method public java.lang.String getCardId();
+    method public int getCardStateInfo();
+    method public boolean getIsActive();
+    method public boolean getIsEuicc();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
+    field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3
+    field public static final int CARD_STATE_INFO_PRESENT = 2; // 0x2
+    field public static final int CARD_STATE_INFO_RESTRICTED = 4; // 0x4
+    field public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR;
+    field public final java.lang.String cardId;
+    field public final int cardStateInfo;
+    field public final boolean isActive;
+    field public final boolean isEuicc;
   }
 
   public abstract class VisualVoicemailService extends android.app.Service {
@@ -4076,11 +4195,11 @@
 package android.telephony.data {
 
   public final class DataCallResponse implements android.os.Parcelable {
-    ctor public DataCallResponse(int, int, int, int, java.lang.String, java.lang.String, java.util.List<android.telephony.data.InterfaceAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.lang.String>, int);
+    ctor public DataCallResponse(int, int, int, int, java.lang.String, java.lang.String, java.util.List<android.net.LinkAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.lang.String>, int);
     ctor public DataCallResponse(android.os.Parcel);
     method public int describeContents();
     method public int getActive();
-    method public java.util.List<android.telephony.data.InterfaceAddress> getAddresses();
+    method public java.util.List<android.net.LinkAddress> getAddresses();
     method public int getCallId();
     method public java.util.List<java.net.InetAddress> getDnses();
     method public java.util.List<java.net.InetAddress> getGateways();
@@ -4123,15 +4242,39 @@
     field public static final int TYPE_COMMON = 0; // 0x0
   }
 
-  public final class InterfaceAddress implements android.os.Parcelable {
-    ctor public InterfaceAddress(java.net.InetAddress, int);
-    ctor public InterfaceAddress(java.lang.String, int) throws java.net.UnknownHostException;
-    ctor public InterfaceAddress(android.os.Parcel);
-    method public int describeContents();
-    method public java.net.InetAddress getAddress();
-    method public int getNetworkPrefixLength();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.data.InterfaceAddress> CREATOR;
+  public abstract class DataService extends android.app.Service {
+    method public abstract android.telephony.data.DataService.DataServiceProvider createDataServiceProvider(int);
+    field public static final java.lang.String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
+    field public static final java.lang.String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
+    field public static final int REQUEST_REASON_HANDOVER = 3; // 0x3
+    field public static final int REQUEST_REASON_NORMAL = 1; // 0x1
+    field public static final int REQUEST_REASON_SHUTDOWN = 2; // 0x2
+  }
+
+  public class DataService.DataServiceProvider {
+    ctor public DataService.DataServiceProvider(int);
+    method public void deactivateDataCall(int, int, android.telephony.data.DataServiceCallback);
+    method public void getDataCallList(android.telephony.data.DataServiceCallback);
+    method public final int getSlotId();
+    method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+    method protected void onDestroy();
+    method public void setDataProfile(java.util.List<android.telephony.data.DataProfile>, boolean, android.telephony.data.DataServiceCallback);
+    method public void setInitialAttachApn(android.telephony.data.DataProfile, boolean, android.telephony.data.DataServiceCallback);
+    method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, int, android.net.LinkProperties, android.telephony.data.DataServiceCallback);
+  }
+
+  public class DataServiceCallback {
+    method public void onDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+    method public void onDeactivateDataCallComplete(int);
+    method public void onGetDataCallListComplete(int, java.util.List<android.telephony.data.DataCallResponse>);
+    method public void onSetDataProfileComplete(int);
+    method public void onSetInitialAttachApnComplete(int);
+    method public void onSetupDataCallComplete(int, android.telephony.data.DataCallResponse);
+    field public static final int RESULT_ERROR_BUSY = 3; // 0x3
+    field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
+    field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
+    field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 0; // 0x0
   }
 
 }
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index adbe9d0..34f6d7d 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.hardware.usb.IUsbManager;
+import android.hardware.usb.UsbManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -38,6 +39,9 @@
                 + "\n"
                 + "usage: svc usb setFunction [function] [usbDataUnlocked=false]\n"
                 + "         Set the current usb function and optionally the data lock state.\n\n"
+                + "       svc usb setScreenUnlockedFunctions [function]\n"
+                + "         Sets the functions which, if the device was charging,"
+                    + " become current on screen unlock.\n"
                 + "       svc usb getFunction\n"
                 + "          Gets the list of currently enabled functions\n";
     }
@@ -62,6 +66,16 @@
             } else if ("getFunction".equals(args[1])) {
                 System.err.println(SystemProperties.get("sys.usb.config"));
                 return;
+            } else if ("setScreenUnlockedFunctions".equals(args[1])) {
+                IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
+                        Context.USB_SERVICE));
+                try {
+                    usbMgr.setScreenUnlockedFunctions((args.length >= 3 ? args[2] :
+                            UsbManager.USB_FUNCTION_NONE));
+                } catch (RemoteException e) {
+                    System.err.println("Error communicating with UsbManager: " + e);
+                }
+                return;
             }
         }
         System.err.println(longHelp());
diff --git a/legacy-test/api/legacy-test-removed.txt b/config/hiddenapi-blacklist.txt
similarity index 100%
copy from legacy-test/api/legacy-test-removed.txt
copy to config/hiddenapi-blacklist.txt
diff --git a/legacy-test/api/legacy-test-removed.txt b/config/hiddenapi-dark-greylist.txt
similarity index 100%
copy from legacy-test/api/legacy-test-removed.txt
copy to config/hiddenapi-dark-greylist.txt
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 42b0f6b..fb27f74 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -2,3 +2,8 @@
     name: "IKeyAttestationApplicationIdProvider.aidl",
     srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
 }
+
+filegroup {
+    name: "IDropBoxManagerService.aidl",
+    srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index dbdb81c..3abb24c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -128,6 +128,8 @@
 import com.android.internal.policy.DecorView;
 import com.android.internal.policy.PhoneWindow;
 
+import dalvik.system.VMRuntime;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -7039,11 +7041,12 @@
         mFragments.dispatchStart();
         mFragments.reportLoaderStart();
 
-        // This property is set for all builds except final release
-        boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
         boolean isAppDebuggable =
                 (mApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
 
+        // This property is set for all non-user builds except final release
+        boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
+
         if (isAppDebuggable || isDlwarningEnabled) {
             String dlwarning = getDlWarning();
             if (dlwarning != null) {
@@ -7064,6 +7067,28 @@
             }
         }
 
+        // This property is set for all non-user builds except final release
+        boolean isApiWarningEnabled = SystemProperties.getInt("ro.art.hiddenapi.warning", 0) == 1;
+
+        if (isAppDebuggable || isApiWarningEnabled) {
+            if (VMRuntime.getRuntime().hasUsedHiddenApi()) {
+                String appName = getApplicationInfo().loadLabel(getPackageManager())
+                        .toString();
+                String warning = "Detected problems with API compatiblity\n"
+                                 + "(please consult log for detail)";
+                if (isAppDebuggable) {
+                    new AlertDialog.Builder(this)
+                        .setTitle(appName)
+                        .setMessage(warning)
+                        .setPositiveButton(android.R.string.ok, null)
+                        .setCancelable(false)
+                        .show();
+                } else {
+                    Toast.makeText(this, appName + "\n" + warning, Toast.LENGTH_LONG).show();
+                }
+            }
+        }
+
         mActivityTransitionState.enterReady(this);
     }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 45f7eba..2ecd312 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -366,7 +366,7 @@
 
         ActivityInfo activityInfo;
         CompatibilityInfo compatInfo;
-        LoadedApk packageInfo;
+        LoadedApk loadedApk;
 
         List<ResultInfo> pendingResults;
         List<ReferrerIntent> pendingIntents;
@@ -542,7 +542,7 @@
     }
 
     static final class AppBindData {
-        LoadedApk info;
+        LoadedApk loadedApk;
         String processName;
         ApplicationInfo appInfo;
         List<ProviderInfo> providers;
@@ -1584,7 +1584,7 @@
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                     final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
 
-                    r.packageInfo = getPackageInfoNoCheck(
+                    r.loadedApk = getLoadedApkNoCheck(
                             r.activityInfo.applicationInfo, r.compatInfo);
                     handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -1832,9 +1832,11 @@
                     handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
                             (IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
                     break;
-                case ATTACH_AGENT:
-                    handleAttachAgent((String) msg.obj);
+                case ATTACH_AGENT: {
+                    Application app = getApplication();
+                    handleAttachAgent((String) msg.obj, app != null ? app.mLoadedApk : null);
                     break;
+                }
                 case APPLICATION_INFO_CHANGED:
                     mUpdatingSystemConfig = true;
                     try {
@@ -1971,13 +1973,13 @@
         return mH;
     }
 
-    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
-            int flags) {
-        return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
+    public final LoadedApk getLoadedApkForPackageName(String packageName,
+            CompatibilityInfo compatInfo, int flags) {
+        return getLoadedApkForPackageName(packageName, compatInfo, flags, UserHandle.myUserId());
     }
 
-    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
-            int flags, int userId) {
+    public final LoadedApk getLoadedApkForPackageName(String packageName,
+            CompatibilityInfo compatInfo, int flags, int userId) {
         final boolean differentUser = (UserHandle.myUserId() != userId);
         synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
@@ -1990,13 +1992,13 @@
                 ref = mResourcePackages.get(packageName);
             }
 
-            LoadedApk packageInfo = ref != null ? ref.get() : null;
-            //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
-            //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
-            //        + ": " + packageInfo.mResources.getAssets().isUpToDate());
-            if (packageInfo != null && (packageInfo.mResources == null
-                    || packageInfo.mResources.getAssets().isUpToDate())) {
-                if (packageInfo.isSecurityViolation()
+            LoadedApk loadedApk = ref != null ? ref.get() : null;
+            //Slog.i(TAG, "getLoadedApkForPackageName " + packageName + ": " + loadedApk);
+            //if (loadedApk != null) Slog.i(TAG, "isUptoDate " + loadedApk.mResDir
+            //        + ": " + loadedApk.mResources.getAssets().isUpToDate());
+            if (loadedApk != null && (loadedApk.mResources == null
+                    || loadedApk.mResources.getAssets().isUpToDate())) {
+                if (loadedApk.isSecurityViolation()
                         && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
                     throw new SecurityException(
                             "Requesting code from " + packageName
@@ -2004,7 +2006,7 @@
                             + mBoundApplication.processName
                             + "/" + mBoundApplication.appInfo.uid);
                 }
-                return packageInfo;
+                return loadedApk;
             }
         }
 
@@ -2019,13 +2021,13 @@
         }
 
         if (ai != null) {
-            return getPackageInfo(ai, compatInfo, flags);
+            return getLoadedApk(ai, compatInfo, flags);
         }
 
         return null;
     }
 
-    public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
+    public final LoadedApk getLoadedApk(ApplicationInfo ai, CompatibilityInfo compatInfo,
             int flags) {
         boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
         boolean securityViolation = includeCode && ai.uid != 0
@@ -2047,16 +2049,16 @@
                 throw new SecurityException(msg);
             }
         }
-        return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode,
+        return getLoadedApk(ai, compatInfo, null, securityViolation, includeCode,
                 registerPackage);
     }
 
-    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
+    public final LoadedApk getLoadedApkNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo) {
-        return getPackageInfo(ai, compatInfo, null, false, true, false);
+        return getLoadedApk(ai, compatInfo, null, false, true, false);
     }
 
-    public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
+    public final LoadedApk peekLoadedApk(String packageName, boolean includeCode) {
         synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
             if (includeCode) {
@@ -2068,7 +2070,7 @@
         }
     }
 
-    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
+    private LoadedApk getLoadedApk(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
             ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
             boolean registerPackage) {
         final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
@@ -2083,35 +2085,35 @@
                 ref = mResourcePackages.get(aInfo.packageName);
             }
 
-            LoadedApk packageInfo = ref != null ? ref.get() : null;
-            if (packageInfo == null || (packageInfo.mResources != null
-                    && !packageInfo.mResources.getAssets().isUpToDate())) {
+            LoadedApk loadedApk = ref != null ? ref.get() : null;
+            if (loadedApk == null || (loadedApk.mResources != null
+                    && !loadedApk.mResources.getAssets().isUpToDate())) {
                 if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                         : "Loading resource-only package ") + aInfo.packageName
                         + " (in " + (mBoundApplication != null
                                 ? mBoundApplication.processName : null)
                         + ")");
-                packageInfo =
+                loadedApk =
                     new LoadedApk(this, aInfo, compatInfo, baseLoader,
                             securityViolation, includeCode &&
                             (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
 
                 if (mSystemThread && "android".equals(aInfo.packageName)) {
-                    packageInfo.installSystemApplicationInfo(aInfo,
-                            getSystemContext().mPackageInfo.getClassLoader());
+                    loadedApk.installSystemApplicationInfo(aInfo,
+                            getSystemContext().mLoadedApk.getClassLoader());
                 }
 
                 if (differentUser) {
                     // Caching not supported across users
                 } else if (includeCode) {
                     mPackages.put(aInfo.packageName,
-                            new WeakReference<LoadedApk>(packageInfo));
+                            new WeakReference<LoadedApk>(loadedApk));
                 } else {
                     mResourcePackages.put(aInfo.packageName,
-                            new WeakReference<LoadedApk>(packageInfo));
+                            new WeakReference<LoadedApk>(loadedApk));
                 }
             }
-            return packageInfo;
+            return loadedApk;
         }
     }
 
@@ -2645,8 +2647,8 @@
         // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
 
         ActivityInfo aInfo = r.activityInfo;
-        if (r.packageInfo == null) {
-            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
+        if (r.loadedApk == null) {
+            r.loadedApk = getLoadedApk(aInfo.applicationInfo, r.compatInfo,
                     Context.CONTEXT_INCLUDE_CODE);
         }
 
@@ -2683,15 +2685,15 @@
         }
 
         try {
-            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
+            Application app = r.loadedApk.makeApplication(false, mInstrumentation);
 
             if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
             if (localLOGV) Slog.v(
                     TAG, r + ": app=" + app
                     + ", appName=" + app.getPackageName()
-                    + ", pkg=" + r.packageInfo.getPackageName()
+                    + ", pkg=" + r.loadedApk.getPackageName()
                     + ", comp=" + r.intent.getComponent().toShortString()
-                    + ", dir=" + r.packageInfo.getAppDir());
+                    + ", dir=" + r.loadedApk.getAppDir());
 
             if (activity != null) {
                 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
@@ -2809,7 +2811,7 @@
         }
 
         ContextImpl appContext = ContextImpl.createActivityContext(
-                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
+                this, r.loadedApk, r.activityInfo, r.token, displayId, r.overrideConfig);
 
         final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
         // For debugging purposes, if the activity's package name contains the value of
@@ -2817,7 +2819,7 @@
         // its content on a secondary display if there is one.
         String pkgName = SystemProperties.get("debug.second-display.pkg");
         if (pkgName != null && !pkgName.isEmpty()
-                && r.packageInfo.mPackageName.contains(pkgName)) {
+                && r.loadedApk.mPackageName.contains(pkgName)) {
             for (int id : dm.getDisplayIds()) {
                 if (id != Display.DEFAULT_DISPLAY) {
                     Display display =
@@ -3119,11 +3121,23 @@
         }
     }
 
-    static final void handleAttachAgent(String agent) {
+    private static boolean attemptAttachAgent(String agent, ClassLoader classLoader) {
         try {
-            VMDebug.attachAgent(agent);
+            VMDebug.attachAgent(agent, classLoader);
+            return true;
         } catch (IOException e) {
-            Slog.e(TAG, "Attaching agent failed: " + agent);
+            Slog.e(TAG, "Attaching agent with " + classLoader + " failed: " + agent);
+            return false;
+        }
+    }
+
+    static void handleAttachAgent(String agent, LoadedApk loadedApk) {
+        ClassLoader classLoader = loadedApk != null ? loadedApk.getClassLoader() : null;
+        if (attemptAttachAgent(agent, classLoader)) {
+            return;
+        }
+        if (classLoader != null) {
+            attemptAttachAgent(agent, null);
         }
     }
 
@@ -3145,7 +3159,7 @@
 
         String component = data.intent.getComponent().getClassName();
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(
+        LoadedApk loadedApk = getLoadedApkNoCheck(
                 data.info.applicationInfo, data.compatInfo);
 
         IActivityManager mgr = ActivityManager.getService();
@@ -3154,7 +3168,7 @@
         BroadcastReceiver receiver;
         ContextImpl context;
         try {
-            app = packageInfo.makeApplication(false, mInstrumentation);
+            app = loadedApk.makeApplication(false, mInstrumentation);
             context = (ContextImpl) app.getBaseContext();
             if (data.info.splitName != null) {
                 context = (ContextImpl) context.createContextForSplit(data.info.splitName);
@@ -3178,9 +3192,9 @@
                 TAG, "Performing receive of " + data.intent
                 + ": app=" + app
                 + ", appName=" + app.getPackageName()
-                + ", pkg=" + packageInfo.getPackageName()
+                + ", pkg=" + loadedApk.getPackageName()
                 + ", comp=" + data.intent.getComponent().toShortString()
-                + ", dir=" + packageInfo.getAppDir());
+                + ", dir=" + loadedApk.getAppDir());
 
             sCurrentBroadcastIntent.set(data.intent);
             receiver.setPendingResult(data);
@@ -3225,8 +3239,8 @@
         unscheduleGcIdler();
 
         // instantiate the BackupAgent class named in the manifest
-        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
-        String packageName = packageInfo.mPackageName;
+        LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+        String packageName = loadedApk.mPackageName;
         if (packageName == null) {
             Slog.d(TAG, "Asked to create backup agent for nonexistent package");
             return;
@@ -3252,11 +3266,11 @@
                 try {
                     if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
 
-                    java.lang.ClassLoader cl = packageInfo.getClassLoader();
+                    java.lang.ClassLoader cl = loadedApk.getClassLoader();
                     agent = (BackupAgent) cl.loadClass(classname).newInstance();
 
                     // set up the agent's context
-                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+                    ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
                     context.setOuterContext(agent);
                     agent.attach(context);
 
@@ -3292,8 +3306,8 @@
     private void handleDestroyBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
-        String packageName = packageInfo.mPackageName;
+        LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+        String packageName = loadedApk.mPackageName;
         BackupAgent agent = mBackupAgents.get(packageName);
         if (agent != null) {
             try {
@@ -3313,11 +3327,11 @@
         // we are back active so skip it.
         unscheduleGcIdler();
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(
+        LoadedApk loadedApk = getLoadedApkNoCheck(
                 data.info.applicationInfo, data.compatInfo);
         Service service = null;
         try {
-            java.lang.ClassLoader cl = packageInfo.getClassLoader();
+            java.lang.ClassLoader cl = loadedApk.getClassLoader();
             service = (Service) cl.loadClass(data.info.name).newInstance();
         } catch (Exception e) {
             if (!mInstrumentation.onException(service, e)) {
@@ -3330,10 +3344,10 @@
         try {
             if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
 
-            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+            ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
             context.setOuterContext(service);
 
-            Application app = packageInfo.makeApplication(false, mInstrumentation);
+            Application app = loadedApk.makeApplication(false, mInstrumentation);
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManager.getService());
             service.onCreate();
@@ -3943,7 +3957,7 @@
                 Bundle.dumpStats(pw, persistentState);
 
                 if (ex instanceof TransactionTooLargeException
-                        && activity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
+                        && activity.loadedApk.getTargetSdkVersion() < Build.VERSION_CODES.N) {
                     Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
                     return;
                 }
@@ -4238,11 +4252,11 @@
     }
 
     private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) {
-        LoadedApk apk = peekPackageInfo(data.pkg, false);
+        LoadedApk apk = peekLoadedApk(data.pkg, false);
         if (apk != null) {
             apk.setCompatibilityInfo(data.info);
         }
-        apk = peekPackageInfo(data.pkg, true);
+        apk = peekLoadedApk(data.pkg, true);
         if (apk != null) {
             apk.setCompatibilityInfo(data.info);
         }
@@ -4739,7 +4753,7 @@
                 if (a != null) {
                     Configuration thisConfig = applyConfigCompatMainThread(
                             mCurDefaultDisplayDpi, newConfig,
-                            ar.packageInfo.getCompatibilityInfo());
+                            ar.loadedApk.getCompatibilityInfo());
                     if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
                         // If the activity is currently resumed, its configuration
                         // needs to change right now.
@@ -5209,7 +5223,7 @@
     }
 
     final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
-        boolean hasPkgInfo = false;
+        boolean hasLoadedApk = false;
         switch (cmd) {
             case ApplicationThreadConstants.PACKAGE_REMOVED:
             case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL:
@@ -5220,14 +5234,14 @@
                 }
                 synchronized (mResourcesManager) {
                     for (int i = packages.length - 1; i >= 0; i--) {
-                        if (!hasPkgInfo) {
+                        if (!hasLoadedApk) {
                             WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
                             if (ref != null && ref.get() != null) {
-                                hasPkgInfo = true;
+                                hasLoadedApk = true;
                             } else {
                                 ref = mResourcePackages.get(packages[i]);
                                 if (ref != null && ref.get() != null) {
-                                    hasPkgInfo = true;
+                                    hasLoadedApk = true;
                                 }
                             }
                         }
@@ -5247,21 +5261,21 @@
                 synchronized (mResourcesManager) {
                     for (int i = packages.length - 1; i >= 0; i--) {
                         WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
-                        LoadedApk pkgInfo = ref != null ? ref.get() : null;
-                        if (pkgInfo != null) {
-                            hasPkgInfo = true;
+                        LoadedApk loadedApk = ref != null ? ref.get() : null;
+                        if (loadedApk != null) {
+                            hasLoadedApk = true;
                         } else {
                             ref = mResourcePackages.get(packages[i]);
-                            pkgInfo = ref != null ? ref.get() : null;
-                            if (pkgInfo != null) {
-                                hasPkgInfo = true;
+                            loadedApk = ref != null ? ref.get() : null;
+                            if (loadedApk != null) {
+                                hasLoadedApk = true;
                             }
                         }
                         // If the package is being replaced, yet it still has a valid
                         // LoadedApk object, the package was updated with _DONT_KILL.
                         // Adjust it's internal references to the application info and
                         // resources.
-                        if (pkgInfo != null) {
+                        if (loadedApk != null) {
                             try {
                                 final String packageName = packages[i];
                                 final ApplicationInfo aInfo =
@@ -5275,13 +5289,13 @@
                                         if (ar.activityInfo.applicationInfo.packageName
                                                 .equals(packageName)) {
                                             ar.activityInfo.applicationInfo = aInfo;
-                                            ar.packageInfo = pkgInfo;
+                                            ar.loadedApk = loadedApk;
                                         }
                                     }
                                 }
                                 final List<String> oldPaths =
                                         sPackageManager.getPreviousCodePaths(packageName);
-                                pkgInfo.updateApplicationInfo(aInfo, oldPaths);
+                                loadedApk.updateApplicationInfo(aInfo, oldPaths);
                             } catch (RemoteException e) {
                             }
                         }
@@ -5290,7 +5304,7 @@
                 break;
             }
         }
-        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo);
+        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk);
     }
 
     final void handleLowMemory() {
@@ -5441,12 +5455,16 @@
         mCompatConfiguration = new Configuration(data.config);
 
         mProfiler = new Profiler();
+        String agent = null;
         if (data.initProfilerInfo != null) {
             mProfiler.profileFile = data.initProfilerInfo.profileFile;
             mProfiler.profileFd = data.initProfilerInfo.profileFd;
             mProfiler.samplingInterval = data.initProfilerInfo.samplingInterval;
             mProfiler.autoStopProfiler = data.initProfilerInfo.autoStopProfiler;
             mProfiler.streamingOutput = data.initProfilerInfo.streamingOutput;
+            if (data.initProfilerInfo.attachAgentDuringBind) {
+                agent = data.initProfilerInfo.agent;
+            }
         }
 
         // send up app name; do this *before* waiting for debugger
@@ -5494,7 +5512,11 @@
             applyCompatConfiguration(mCurDefaultDisplayDpi);
         }
 
-        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+        data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+
+        if (agent != null) {
+            handleAttachAgent(agent, data.loadedApk);
+        }
 
         /**
          * Switch this process to density compatibility mode if needed.
@@ -5562,7 +5584,7 @@
             // XXX should have option to change the port.
             Debug.changeDebugPort(8100);
             if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
-                Slog.w(TAG, "Application " + data.info.getPackageName()
+                Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
                       + " is waiting for the debugger on port 8100...");
 
                 IActivityManager mgr = ActivityManager.getService();
@@ -5581,7 +5603,7 @@
                 }
 
             } else {
-                Slog.w(TAG, "Application " + data.info.getPackageName()
+                Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
                       + " can be debugged on port 8100...");
             }
         }
@@ -5629,14 +5651,14 @@
             mInstrumentationAppDir = ii.sourceDir;
             mInstrumentationSplitAppDirs = ii.splitSourceDirs;
             mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
-            mInstrumentedAppDir = data.info.getAppDir();
-            mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
-            mInstrumentedLibDir = data.info.getLibDir();
+            mInstrumentedAppDir = data.loadedApk.getAppDir();
+            mInstrumentedSplitAppDirs = data.loadedApk.getSplitAppDirs();
+            mInstrumentedLibDir = data.loadedApk.getLibDir();
         } else {
             ii = null;
         }
 
-        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
+        final ContextImpl appContext = ContextImpl.createAppContext(this, data.loadedApk);
         updateLocaleListFromAppContext(appContext,
                 mResourcesManager.getConfiguration().getLocales());
 
@@ -5666,9 +5688,9 @@
             final ApplicationInfo instrApp = new ApplicationInfo();
             ii.copyTo(instrApp);
             instrApp.initForUser(UserHandle.myUserId());
-            final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
+            final LoadedApk loadedApk = getLoadedApk(instrApp, data.compatInfo,
                     appContext.getClassLoader(), false, true, false);
-            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
+            final ContextImpl instrContext = ContextImpl.createAppContext(this, loadedApk);
 
             try {
                 final ClassLoader cl = instrContext.getClassLoader();
@@ -5712,7 +5734,7 @@
         try {
             // If the app is being launched for full backup or restore, bring it up in
             // a restricted environment with the base application class.
-            app = data.info.makeApplication(data.restrictedBackupMode, null);
+            app = data.loadedApk.makeApplication(data.restrictedBackupMode, null);
             mInitialApplication = app;
 
             // don't bring up providers in restricted mode; they may depend on the
@@ -5766,7 +5788,7 @@
                 final int preloadedFontsResource = info.metaData.getInt(
                         ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
                 if (preloadedFontsResource != 0) {
-                    data.info.getResources().preloadFonts(preloadedFontsResource);
+                    data.loadedApk.getResources().preloadFonts(preloadedFontsResource);
                 }
             }
         } catch (RemoteException e) {
@@ -6361,8 +6383,8 @@
             try {
                 mInstrumentation = new Instrumentation();
                 ContextImpl context = ContextImpl.createAppContext(
-                        this, getSystemContext().mPackageInfo);
-                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
+                        this, getSystemContext().mLoadedApk);
+                mInitialApplication = context.mLoadedApk.makeApplication(true, null);
                 mInitialApplication.onCreate();
             } catch (Exception e) {
                 throw new RuntimeException(
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 156df36..5822f5c 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -187,7 +187,7 @@
      */
     /* package */ final void attach(Context context) {
         attachBaseContext(context);
-        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
+        mLoadedApk = ContextImpl.getImpl(context).mLoadedApk;
     }
 
     /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0eafdec..4d47d82 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1379,7 +1379,7 @@
                     sameUid ? app.sourceDir : app.publicSourceDir,
                     sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
                     app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
-                    mContext.mPackageInfo);
+                    mContext.mLoadedApk);
         if (r != null) {
             return r;
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5f34322..64ddfbc 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -159,7 +159,7 @@
     private ArrayMap<String, File> mSharedPrefsPaths;
 
     final @NonNull ActivityThread mMainThread;
-    final @NonNull LoadedApk mPackageInfo;
+    final @NonNull LoadedApk mLoadedApk;
     private @Nullable ClassLoader mClassLoader;
 
     private final @Nullable IBinder mActivityToken;
@@ -252,8 +252,8 @@
 
     @Override
     public Context getApplicationContext() {
-        return (mPackageInfo != null) ?
-                mPackageInfo.getApplication() : mMainThread.getApplication();
+        return (mLoadedApk != null) ?
+                mLoadedApk.getApplication() : mMainThread.getApplication();
     }
 
     @Override
@@ -297,15 +297,15 @@
 
     @Override
     public ClassLoader getClassLoader() {
-        return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());
+        return mClassLoader != null ? mClassLoader : (mLoadedApk != null ? mLoadedApk.getClassLoader() : ClassLoader.getSystemClassLoader());
     }
 
     @Override
     public String getPackageName() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getPackageName();
+        if (mLoadedApk != null) {
+            return mLoadedApk.getPackageName();
         }
-        // No mPackageInfo means this is a Context for the system itself,
+        // No mLoadedApk means this is a Context for the system itself,
         // and this here is its name.
         return "android";
     }
@@ -324,24 +324,24 @@
 
     @Override
     public ApplicationInfo getApplicationInfo() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getApplicationInfo();
+        if (mLoadedApk != null) {
+            return mLoadedApk.getApplicationInfo();
         }
         throw new RuntimeException("Not supported in system context");
     }
 
     @Override
     public String getPackageResourcePath() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getResDir();
+        if (mLoadedApk != null) {
+            return mLoadedApk.getResDir();
         }
         throw new RuntimeException("Not supported in system context");
     }
 
     @Override
     public String getPackageCodePath() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getAppDir();
+        if (mLoadedApk != null) {
+            return mLoadedApk.getAppDir();
         }
         throw new RuntimeException("Not supported in system context");
     }
@@ -351,7 +351,7 @@
         // At least one application in the world actually passes in a null
         // name.  This happened to work because when we generated the file name
         // we would stringify it to "null.xml".  Nice.
-        if (mPackageInfo.getApplicationInfo().targetSdkVersion <
+        if (mLoadedApk.getApplicationInfo().targetSdkVersion <
                 Build.VERSION_CODES.KITKAT) {
             if (name == null) {
                 name = "null";
@@ -1093,11 +1093,11 @@
         warnIfCallingFromSystemProcess();
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mPackageInfo != null) {
+            if (mLoadedApk != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mPackageInfo.getReceiverDispatcher(
+                rd = mLoadedApk.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1197,11 +1197,11 @@
             Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mPackageInfo != null) {
+            if (mLoadedApk != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mPackageInfo.getReceiverDispatcher(
+                rd = mLoadedApk.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1251,11 +1251,11 @@
         warnIfCallingFromSystemProcess();
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mPackageInfo != null) {
+            if (mLoadedApk != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mPackageInfo.getReceiverDispatcher(
+                rd = mLoadedApk.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1333,11 +1333,11 @@
             Bundle initialExtras) {
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mPackageInfo != null) {
+            if (mLoadedApk != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mPackageInfo.getReceiverDispatcher(
+                rd = mLoadedApk.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1414,11 +1414,11 @@
             Handler scheduler, Context context, int flags) {
         IIntentReceiver rd = null;
         if (receiver != null) {
-            if (mPackageInfo != null && context != null) {
+            if (mLoadedApk != null && context != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mPackageInfo.getReceiverDispatcher(
+                rd = mLoadedApk.getReceiverDispatcher(
                     receiver, context, scheduler,
                     mMainThread.getInstrumentation(), true);
             } else {
@@ -1445,8 +1445,8 @@
 
     @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
-        if (mPackageInfo != null) {
-            IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
+        if (mLoadedApk != null) {
+            IIntentReceiver rd = mLoadedApk.forgetReceiverDispatcher(
                     getOuterContext(), receiver);
             try {
                 ActivityManager.getService().unregisterReceiver(rd);
@@ -1579,7 +1579,7 @@
     @Override
     public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
             int flags) {
-        return mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+        return mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
     }
 
     /** @hide */
@@ -1601,16 +1601,16 @@
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
         }
-        if (mPackageInfo != null) {
-            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+        if (mLoadedApk != null) {
+            sd = mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
         } else {
             throw new RuntimeException("Not supported in system context");
         }
         validateServiceIntent(service);
         try {
             IBinder token = getActivityToken();
-            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
-                    && mPackageInfo.getApplicationInfo().targetSdkVersion
+            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mLoadedApk != null
+                    && mLoadedApk.getApplicationInfo().targetSdkVersion
                     < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                 flags |= BIND_WAIVE_PRIORITY;
             }
@@ -1634,8 +1634,8 @@
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
         }
-        if (mPackageInfo != null) {
-            IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+        if (mLoadedApk != null) {
+            IServiceConnection sd = mLoadedApk.forgetServiceDispatcher(
                     getOuterContext(), conn);
             try {
                 ActivityManager.getService().unbindService(sd);
@@ -1980,40 +1980,20 @@
         }
     }
 
-    private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
-            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
-        final String[] splitResDirs;
-        final ClassLoader classLoader;
-        try {
-            splitResDirs = pi.getSplitPaths(splitName);
-            classLoader = pi.getSplitClassLoader(splitName);
-        } catch (NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-        return ResourcesManager.getInstance().getResources(activityToken,
-                pi.getResDir(),
-                splitResDirs,
-                pi.getOverlayDirs(),
-                pi.getApplicationInfo().sharedLibraryFiles,
-                displayId,
-                overrideConfig,
-                compatInfo,
-                classLoader);
-    }
-
     @Override
     public Context createApplicationContext(ApplicationInfo application, int flags)
             throws NameNotFoundException {
-        LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
+        LoadedApk loadedApk = mMainThread.getLoadedApk(application,
+                mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE);
-        if (pi != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
+        if (loadedApk != null) {
+            ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken,
                     new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
+            c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
@@ -2037,20 +2017,21 @@
         if (packageName.equals("system") || packageName.equals("android")) {
             // The system resources are loaded in every application, so we can safely copy
             // the context without reloading Resources.
-            return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
+            return new ContextImpl(this, mMainThread, mLoadedApk, null, mActivityToken, user,
                     flags, null);
         }
 
-        LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
+        LoadedApk loadedApk = mMainThread.getLoadedApkForPackageName(packageName,
+                mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
-        if (pi != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
+        if (loadedApk != null) {
+            ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken, user,
                     flags, null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
+            c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
@@ -2064,30 +2045,21 @@
 
     @Override
     public Context createContextForSplit(String splitName) throws NameNotFoundException {
-        if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
+        if (!mLoadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
             // All Splits are always loaded.
             return this;
         }
 
-        final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
-        final String[] paths = mPackageInfo.getSplitPaths(splitName);
+        final ClassLoader classLoader = mLoadedApk.getSplitClassLoader(splitName);
 
-        final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
+        final ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, splitName,
                 mActivityToken, mUser, mFlags, classLoader);
 
         final int displayId = mDisplay != null
                 ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-        context.setResources(ResourcesManager.getInstance().getResources(
-                mActivityToken,
-                mPackageInfo.getResDir(),
-                paths,
-                mPackageInfo.getOverlayDirs(),
-                mPackageInfo.getApplicationInfo().sharedLibraryFiles,
-                displayId,
-                null,
-                mPackageInfo.getCompatibilityInfo(),
-                classLoader));
+        context.setResources(mLoadedApk.getOrCreateResourcesForSplit(splitName,
+                mActivityToken, displayId));
         return context;
     }
 
@@ -2097,11 +2069,11 @@
             throw new IllegalArgumentException("overrideConfiguration must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+        ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
                 mActivityToken, mUser, mFlags, mClassLoader);
 
         final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
-        context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+        context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
                 overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         return context;
     }
@@ -2112,11 +2084,11 @@
             throw new IllegalArgumentException("display must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+        ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
                 mActivityToken, mUser, mFlags, mClassLoader);
 
         final int displayId = display.getDisplayId();
-        context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+        context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
                 null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         context.mDisplay = display;
         return context;
@@ -2126,7 +2098,7 @@
     public Context createDeviceProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+        return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
                 flags, mClassLoader);
     }
 
@@ -2134,7 +2106,7 @@
     public Context createCredentialProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
                 | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+        return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
                 flags, mClassLoader);
     }
 
@@ -2183,14 +2155,14 @@
 
     @Override
     public File getDataDir() {
-        if (mPackageInfo != null) {
+        if (mLoadedApk != null) {
             File res = null;
             if (isCredentialProtectedStorage()) {
-                res = mPackageInfo.getCredentialProtectedDataDirFile();
+                res = mLoadedApk.getCredentialProtectedDataDirFile();
             } else if (isDeviceProtectedStorage()) {
-                res = mPackageInfo.getDeviceProtectedDataDirFile();
+                res = mLoadedApk.getDeviceProtectedDataDirFile();
             } else {
-                res = mPackageInfo.getDataDirFile();
+                res = mLoadedApk.getDataDirFile();
             }
 
             if (res != null) {
@@ -2241,10 +2213,10 @@
     }
 
     static ContextImpl createSystemContext(ActivityThread mainThread) {
-        LoadedApk packageInfo = new LoadedApk(mainThread);
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
+        LoadedApk loadedApk = new LoadedApk(mainThread);
+        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
                 null);
-        context.setResources(packageInfo.getResources());
+        context.setResources(loadedApk.getResources());
         context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                 context.mResourcesManager.getDisplayMetrics());
         return context;
@@ -2255,35 +2227,35 @@
      * Make sure that the created system UI context shares the same LoadedApk as the system context.
      */
     static ContextImpl createSystemUiContext(ContextImpl systemContext) {
-        final LoadedApk packageInfo = systemContext.mPackageInfo;
-        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
+        final LoadedApk loadedApk = systemContext.mLoadedApk;
+        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, loadedApk, null,
                 null, null, 0, null);
-        context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null,
-                packageInfo.getCompatibilityInfo()));
+        context.setResources(loadedApk.createResources(null, null, Display.DEFAULT_DISPLAY, null,
+                loadedApk.getCompatibilityInfo()));
         return context;
     }
 
-    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
-        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
+    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk loadedApk) {
+        if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
+        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
                 null);
-        context.setResources(packageInfo.getResources());
+        context.setResources(loadedApk.getResources());
         return context;
     }
 
     static ContextImpl createActivityContext(ActivityThread mainThread,
-            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
+            LoadedApk loadedApk, ActivityInfo activityInfo, IBinder activityToken, int displayId,
             Configuration overrideConfiguration) {
-        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
+        if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
 
-        String[] splitDirs = packageInfo.getSplitResDirs();
-        ClassLoader classLoader = packageInfo.getClassLoader();
+        String[] splitDirs = loadedApk.getSplitResDirs();
+        ClassLoader classLoader = loadedApk.getClassLoader();
 
-        if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
+        if (loadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
             Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
             try {
-                classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
-                splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
+                classLoader = loadedApk.getSplitClassLoader(activityInfo.splitName);
+                splitDirs = loadedApk.getSplitPaths(activityInfo.splitName);
             } catch (NameNotFoundException e) {
                 // Nothing above us can handle a NameNotFoundException, better crash.
                 throw new RuntimeException(e);
@@ -2292,14 +2264,14 @@
             }
         }
 
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
+        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, activityInfo.splitName,
                 activityToken, null, 0, classLoader);
 
         // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
         displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
 
         final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
-                ? packageInfo.getCompatibilityInfo()
+                ? loadedApk.getCompatibilityInfo()
                 : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
 
         final ResourcesManager resourcesManager = ResourcesManager.getInstance();
@@ -2307,10 +2279,10 @@
         // Create the base resources for which all configuration contexts for this Activity
         // will be rebased upon.
         context.setResources(resourcesManager.createBaseActivityResources(activityToken,
-                packageInfo.getResDir(),
+                loadedApk.getResDir(),
                 splitDirs,
-                packageInfo.getOverlayDirs(),
-                packageInfo.getApplicationInfo().sharedLibraryFiles,
+                loadedApk.getOverlayDirs(),
+                loadedApk.getApplicationInfo().sharedLibraryFiles,
                 displayId,
                 overrideConfiguration,
                 compatInfo,
@@ -2321,7 +2293,7 @@
     }
 
     private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
-            @NonNull LoadedApk packageInfo, @Nullable String splitName,
+            @NonNull LoadedApk loadedApk, @Nullable String splitName,
             @Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
             @Nullable ClassLoader classLoader) {
         mOuterContext = this;
@@ -2330,10 +2302,10 @@
         // location for application.
         if ((flags & (Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE)) == 0) {
-            final File dataDir = packageInfo.getDataDirFile();
-            if (Objects.equals(dataDir, packageInfo.getCredentialProtectedDataDirFile())) {
+            final File dataDir = loadedApk.getDataDirFile();
+            if (Objects.equals(dataDir, loadedApk.getCredentialProtectedDataDirFile())) {
                 flags |= Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-            } else if (Objects.equals(dataDir, packageInfo.getDeviceProtectedDataDirFile())) {
+            } else if (Objects.equals(dataDir, loadedApk.getDeviceProtectedDataDirFile())) {
                 flags |= Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
             }
         }
@@ -2347,7 +2319,7 @@
         }
         mUser = user;
 
-        mPackageInfo = packageInfo;
+        mLoadedApk = loadedApk;
         mSplitName = splitName;
         mClassLoader = classLoader;
         mResourcesManager = ResourcesManager.getInstance();
@@ -2358,8 +2330,8 @@
             setResources(container.mResources);
             mDisplay = container.mDisplay;
         } else {
-            mBasePackageName = packageInfo.mPackageName;
-            ApplicationInfo ainfo = packageInfo.getApplicationInfo();
+            mBasePackageName = loadedApk.mPackageName;
+            ApplicationInfo ainfo = loadedApk.getApplicationInfo();
             if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) {
                 // Special case: system components allow themselves to be loaded in to other
                 // processes.  For purposes of app ops, we must then consider the context as
@@ -2382,7 +2354,7 @@
     }
 
     void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
-        mPackageInfo.installSystemApplicationInfo(info, classLoader);
+        mLoadedApk.installSystemApplicationInfo(info, classLoader);
     }
 
     final void scheduleFinalCleanup(String who, String what) {
@@ -2391,7 +2363,7 @@
 
     final void performFinalCleanup(String who, String what) {
         //Log.i(TAG, "Cleanup up context: " + this);
-        mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+        mLoadedApk.removeContextRegistrations(getOuterContext(), who, what);
     }
 
     final Context getReceiverRestrictedContext() {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 1811748..7b1afa5 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -137,6 +137,7 @@
     void publishService(in IBinder token, in Intent intent, in IBinder service);
     void activityResumed(in IBinder token);
     void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
+    void setAgentApp(in String packageName, @nullable String agent);
     void setAlwaysFinish(boolean enabled);
     boolean startInstrumentation(in ComponentName className, in String profileFile,
             int flags, in Bundle arguments, in IInstrumentationWatcher watcher,
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f6d9710..236a42c 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -31,6 +31,7 @@
 import android.content.pm.split.SplitDependencyLoader;
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
@@ -48,15 +49,13 @@
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.LogPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
-
 import com.android.internal.util.ArrayUtils;
-
 import dalvik.system.VMRuntime;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -948,14 +947,78 @@
                 throw new AssertionError("null split not found");
             }
 
-            mResources = ResourcesManager.getInstance().getResources(null, mResDir,
-                    splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
-                    Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
+            mResources = ResourcesManager.getInstance().getResources(
+                    null,
+                    mResDir,
+                    splitPaths,
+                    mOverlayDirs,
+                    mApplicationInfo.sharedLibraryFiles,
+                    Display.DEFAULT_DISPLAY,
+                    null,
+                    getCompatibilityInfo(),
                     getClassLoader());
         }
         return mResources;
     }
 
+    public Resources getOrCreateResourcesForSplit(@NonNull String splitName,
+            @Nullable IBinder activityToken, int displayId) throws NameNotFoundException {
+        return ResourcesManager.getInstance().getResources(
+                activityToken,
+                mResDir,
+                getSplitPaths(splitName),
+                mOverlayDirs,
+                mApplicationInfo.sharedLibraryFiles,
+                displayId,
+                null,
+                getCompatibilityInfo(),
+                getSplitClassLoader(splitName));
+    }
+
+    /**
+     * Creates the top level resources for the given package. Will return an existing
+     * Resources if one has already been created.
+     */
+    public Resources getOrCreateTopLevelResources(@NonNull ApplicationInfo appInfo) {
+        // Request for this app, short circuit
+        if (appInfo.uid == Process.myUid()) {
+            return getResources();
+        }
+
+        // Get resources for a different package
+        return ResourcesManager.getInstance().getResources(
+                null,
+                appInfo.publicSourceDir,
+                appInfo.splitPublicSourceDirs,
+                appInfo.resourceDirs,
+                appInfo.sharedLibraryFiles,
+                Display.DEFAULT_DISPLAY,
+                null,
+                getCompatibilityInfo(),
+                getClassLoader());
+    }
+
+    public Resources createResources(IBinder activityToken, String splitName,
+            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+        final String[] splitResDirs;
+        final ClassLoader classLoader;
+        try {
+            splitResDirs = getSplitPaths(splitName);
+            classLoader = getSplitClassLoader(splitName);
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        return ResourcesManager.getInstance().getResources(activityToken,
+                mResDir,
+                splitResDirs,
+                mOverlayDirs,
+                mApplicationInfo.sharedLibraryFiles,
+                displayId,
+                overrideConfig,
+                compatInfo,
+                classLoader);
+    }
+
     public Application makeApplication(boolean forceDefaultAppClass,
             Instrumentation instrumentation) {
         if (mApplication != null) {
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index fad4798..8f718df 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -54,14 +54,24 @@
      */
     public final String agent;
 
+    /**
+     * Whether the {@link agent} should be attached early (before bind-application) or during
+     * bind-application. Agents attached prior to binding cannot be loaded from the app's APK
+     * directly and must be given as an absolute path (or available in the default LD_LIBRARY_PATH).
+     * Agents attached during bind-application will miss early setup (e.g., resource initialization
+     * and classloader generation), but are searched in the app's library search path.
+     */
+    public final boolean attachAgentDuringBind;
+
     public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop,
-            boolean streaming, String agent) {
+            boolean streaming, String agent, boolean attachAgentDuringBind) {
         profileFile = filename;
         profileFd = fd;
         samplingInterval = interval;
         autoStopProfiler = autoStop;
         streamingOutput = streaming;
         this.agent = agent;
+        this.attachAgentDuringBind = attachAgentDuringBind;
     }
 
     public ProfilerInfo(ProfilerInfo in) {
@@ -71,6 +81,16 @@
         autoStopProfiler = in.autoStopProfiler;
         streamingOutput = in.streamingOutput;
         agent = in.agent;
+        attachAgentDuringBind = in.attachAgentDuringBind;
+    }
+
+    /**
+     * Return a new ProfilerInfo instance, with fields populated from this object,
+     * and {@link agent} and {@link attachAgentDuringBind} as given.
+     */
+    public ProfilerInfo setAgent(String agent, boolean attachAgentDuringBind) {
+        return new ProfilerInfo(this.profileFile, this.profileFd, this.samplingInterval,
+                this.autoStopProfiler, this.streamingOutput, agent, attachAgentDuringBind);
     }
 
     /**
@@ -109,6 +129,7 @@
         out.writeInt(autoStopProfiler ? 1 : 0);
         out.writeInt(streamingOutput ? 1 : 0);
         out.writeString(agent);
+        out.writeBoolean(attachAgentDuringBind);
     }
 
     public static final Parcelable.Creator<ProfilerInfo> CREATOR =
@@ -131,5 +152,6 @@
         autoStopProfiler = in.readInt() != 0;
         streamingOutput = in.readInt() != 0;
         agent = in.readString();
+        attachAgentDuringBind = in.readBoolean();
     }
 }
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 222e9a0..d33af4f 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -215,6 +215,30 @@
          */
         public static final int ROAMING_YES = 0x2;
 
+        /** @hide */
+        @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
+                DEFAULT_NETWORK_ALL,
+                DEFAULT_NETWORK_NO,
+                DEFAULT_NETWORK_YES
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface DefaultNetwork {}
+
+        /**
+         * Combined usage for this network regardless of whether it was the active default network.
+         */
+        public static final int DEFAULT_NETWORK_ALL = -1;
+
+        /**
+         * Usage that occurs while this network is not the active default network.
+         */
+        public static final int DEFAULT_NETWORK_NO = 0x1;
+
+        /**
+         * Usage that occurs while this network is the active default network.
+         */
+        public static final int DEFAULT_NETWORK_YES = 0x2;
+
         /**
          * Special TAG value for total data across all tags
          */
@@ -223,6 +247,7 @@
         private int mUid;
         private int mTag;
         private int mState;
+        private int mDefaultNetwork;
         private int mMetered;
         private int mRoaming;
         private long mBeginTimeStamp;
@@ -274,6 +299,15 @@
             return 0;
         }
 
+        private static @DefaultNetwork int convertDefaultNetwork(int defaultNetwork) {
+            switch (defaultNetwork) {
+                case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL;
+                case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO;
+                case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES;
+            }
+            return 0;
+        }
+
         public Bucket() {
         }
 
@@ -339,6 +373,21 @@
         }
 
         /**
+         * Default network state. One of the following values:<p/>
+         * <ul>
+         * <li>{@link #DEFAULT_NETWORK_ALL}</li>
+         * <li>{@link #DEFAULT_NETWORK_NO}</li>
+         * <li>{@link #DEFAULT_NETWORK_YES}</li>
+         * </ul>
+         * <p>Indicates whether the network usage occurred on the system default network for this
+         * type of traffic, or whether the application chose to send this traffic on a network that
+         * was not the one selected by the system.
+         */
+        public @DefaultNetwork int getDefaultNetwork() {
+            return mDefaultNetwork;
+        }
+
+        /**
          * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
          * {@link java.lang.System#currentTimeMillis}.
          * @return Start of interval.
@@ -539,6 +588,8 @@
         bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
         bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
         bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
+        bucketOut.mDefaultNetwork = Bucket.convertDefaultNetwork(
+                mRecycledSummaryEntry.defaultNetwork);
         bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
         bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
         bucketOut.mBeginTimeStamp = mStartTimeStamp;
@@ -588,6 +639,7 @@
                 bucketOut.mUid = Bucket.convertUid(getUid());
                 bucketOut.mTag = Bucket.convertTag(mTag);
                 bucketOut.mState = Bucket.STATE_ALL;
+                bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL;
                 bucketOut.mMetered = Bucket.METERED_ALL;
                 bucketOut.mRoaming = Bucket.ROAMING_ALL;
                 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 853b003..5576e86 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -60,10 +60,11 @@
  * {@link #queryDetailsForUid} <p />
  * {@link #queryDetails} <p />
  * These queries do not aggregate over time but do aggregate over state, metered and roaming.
- * Therefore there can be multiple buckets for a particular key but all Bucket's state is going to
- * be {@link NetworkStats.Bucket#STATE_ALL}, all Bucket's metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and all Bucket's roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * Therefore there can be multiple buckets for a particular key. However, all Buckets will have
+ * {@code state} {@link NetworkStats.Bucket#STATE_ALL},
+ * {@code defaultNetwork} {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * {@code metered } {@link NetworkStats.Bucket#METERED_ALL},
+ * {@code roaming} {@link NetworkStats.Bucket#ROAMING_ALL}.
  * <p />
  * <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the
  * calling app requires the permission {@link android.Manifest.permission#PACKAGE_USAGE_STATS},
@@ -130,13 +131,26 @@
         }
     }
 
+    /** @hide */
+    public Bucket querySummaryForDevice(NetworkTemplate template,
+            long startTime, long endTime) throws SecurityException, RemoteException {
+        Bucket bucket = null;
+        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        bucket = stats.getDeviceSummaryForNetwork();
+
+        stats.close();
+        return bucket;
+    }
+
     /**
      * Query network usage statistics summaries. Result is summarised data usage for the whole
      * device. Result is a single Bucket aggregated over time, state, uid, tag, metered, and
      * roaming. This means the bucket's start and end timestamp are going to be the same as the
      * 'startTime' and 'endTime' parameters. State is going to be
      * {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL},
-     * tag {@link NetworkStats.Bucket#TAG_NONE}, metered {@link NetworkStats.Bucket#METERED_ALL},
+     * tag {@link NetworkStats.Bucket#TAG_NONE},
+     * default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+     * metered {@link NetworkStats.Bucket#METERED_ALL},
      * and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
@@ -160,12 +174,7 @@
             return null;
         }
 
-        Bucket bucket = null;
-        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
-        bucket = stats.getDeviceSummaryForNetwork();
-
-        stats.close();
-        return bucket;
+        return querySummaryForDevice(template, startTime, endTime);
     }
 
     /**
@@ -209,10 +218,10 @@
     /**
      * Query network usage statistics summaries. Result filtered to include only uids belonging to
      * calling user. Result is aggregated over time, hence all buckets will have the same start and
-     * end timestamps. Not aggregated over state, uid, metered, or roaming. This means buckets'
-     * start and end timestamps are going to be the same as the 'startTime' and 'endTime'
-     * parameters. State, uid, metered, and roaming are going to vary, and tag is going to be the
-     * same.
+     * end timestamps. Not aggregated over state, uid, default network, metered, or roaming. This
+     * means buckets' start and end timestamps are going to be the same as the 'startTime' and
+     * 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
+     * be the same.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -258,9 +267,10 @@
      * belonging to calling user. Result is aggregated over state but not aggregated over time.
      * This means buckets' start and end timestamps are going to be between 'startTime' and
      * 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid the
-     * same as the 'uid' parameter and tag the same as 'tag' parameter. metered is going to be
-     * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
-     * {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * same as the 'uid' parameter and tag the same as 'tag' parameter.
+     * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+     * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
+     * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
      * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
      * interpolate across partial buckets. Since bucket length is in the order of hours, this
      * method cannot be used to measure data usage on a fine grained time scale.
@@ -301,9 +311,10 @@
      * metered, nor roaming. This means buckets' start and end timestamps are going to be between
      * 'startTime' and 'endTime' parameters. State is going to be
      * {@link NetworkStats.Bucket#STATE_ALL}, uid will vary,
-     * tag {@link NetworkStats.Bucket#TAG_NONE}, metered is going to be
-     * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
-     * {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * tag {@link NetworkStats.Bucket#TAG_NONE},
+     * default network is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+     * metered is going to be {@link NetworkStats.Bucket#METERED_ALL},
+     * and roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
      * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
      * interpolate across partial buckets. Since bucket length is in the order of hours, this
      * method cannot be used to measure data usage on a fine grained time scale.
@@ -335,6 +346,37 @@
         return result;
     }
 
+    /** @hide */
+    public void registerUsageCallback(NetworkTemplate template, int networkType,
+            long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
+        checkNotNull(callback, "UsageCallback cannot be null");
+
+        final Looper looper;
+        if (handler == null) {
+            looper = Looper.myLooper();
+        } else {
+            looper = handler.getLooper();
+        }
+
+        DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
+                template, thresholdBytes);
+        try {
+            CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
+                    template.getSubscriberId(), callback);
+            callback.request = mService.registerUsageCallback(
+                    mContext.getOpPackageName(), request, new Messenger(callbackHandler),
+                    new Binder());
+            if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
+
+            if (callback.request == null) {
+                Log.e(TAG, "Request from callback is null; should not happen");
+            }
+        } catch (RemoteException e) {
+            if (DBG) Log.d(TAG, "Remote exception when registering callback");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Registers to receive notifications about data usage on specified networks.
      *
@@ -363,15 +405,7 @@
      */
     public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
             UsageCallback callback, @Nullable Handler handler) {
-        checkNotNull(callback, "UsageCallback cannot be null");
-
-        final Looper looper;
-        if (handler == null) {
-            looper = Looper.myLooper();
-        } else {
-            looper = handler.getLooper();
-        }
-
+        NetworkTemplate template = createTemplate(networkType, subscriberId);
         if (DBG) {
             Log.d(TAG, "registerUsageCallback called with: {"
                 + " networkType=" + networkType
@@ -379,25 +413,7 @@
                 + " thresholdBytes=" + thresholdBytes
                 + " }");
         }
-
-        NetworkTemplate template = createTemplate(networkType, subscriberId);
-        DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
-                template, thresholdBytes);
-        try {
-            CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
-                    subscriberId, callback);
-            callback.request = mService.registerUsageCallback(
-                    mContext.getOpPackageName(), request, new Messenger(callbackHandler),
-                    new Binder());
-            if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
-
-            if (callback.request == null) {
-                Log.e(TAG, "Request from callback is null; should not happen");
-            }
-        } catch (RemoteException e) {
-            if (DBG) Log.d(TAG, "Remote exception when registering callback");
-            throw e.rethrowFromSystemServer();
-        }
+        registerUsageCallback(template, networkType, thresholdBytes, callback, handler);
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 35a21a4..b255a43 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -300,11 +300,7 @@
     }
 
     /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> Currently, the system supports only 1 connection to the
-     * A2DP profile. The API will automatically disconnect connected
-     * devices before connecting.
+     * Initiate connection to a profile of the remote Bluetooth device.
      *
      * <p> This API returns false in scenarios like the profile on the
      * device is already connected or Bluetooth is not turned on.
@@ -699,15 +695,17 @@
     /**
      * Gets the current codec status (configuration and capability).
      *
+     * @param device the remote Bluetooth device. If null, use the current
+     * active A2DP Bluetooth device.
      * @return the current codec status
      * @hide
      */
-    public BluetoothCodecStatus getCodecStatus() {
-        if (DBG) Log.d(TAG, "getCodecStatus");
+    public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
         try {
             mServiceLock.readLock().lock();
             if (mService != null && isEnabled()) {
-                return mService.getCodecStatus();
+                return mService.getCodecStatus(device);
             }
             if (mService == null) {
                 Log.w(TAG, "Proxy not attached to service");
@@ -724,15 +722,18 @@
     /**
      * Sets the codec configuration preference.
      *
+     * @param device the remote Bluetooth device. If null, use the current
+     * active A2DP Bluetooth device.
      * @param codecConfig the codec configuration preference
      * @hide
      */
-    public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
-        if (DBG) Log.d(TAG, "setCodecConfigPreference");
+    public void setCodecConfigPreference(BluetoothDevice device,
+                                         BluetoothCodecConfig codecConfig) {
+        if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
         try {
             mServiceLock.readLock().lock();
             if (mService != null && isEnabled()) {
-                mService.setCodecConfigPreference(codecConfig);
+                mService.setCodecConfigPreference(device, codecConfig);
             }
             if (mService == null) Log.w(TAG, "Proxy not attached to service");
             return;
@@ -747,36 +748,42 @@
     /**
      * Enables the optional codecs.
      *
+     * @param device the remote Bluetooth device. If null, use the currect
+     * active A2DP Bluetooth device.
      * @hide
      */
-    public void enableOptionalCodecs() {
-        if (DBG) Log.d(TAG, "enableOptionalCodecs");
-        enableDisableOptionalCodecs(true);
+    public void enableOptionalCodecs(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
+        enableDisableOptionalCodecs(device, true);
     }
 
     /**
      * Disables the optional codecs.
      *
+     * @param device the remote Bluetooth device. If null, use the currect
+     * active A2DP Bluetooth device.
      * @hide
      */
-    public void disableOptionalCodecs() {
-        if (DBG) Log.d(TAG, "disableOptionalCodecs");
-        enableDisableOptionalCodecs(false);
+    public void disableOptionalCodecs(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
+        enableDisableOptionalCodecs(device, false);
     }
 
     /**
      * Enables or disables the optional codecs.
      *
+     * @param device the remote Bluetooth device. If null, use the currect
+     * active A2DP Bluetooth device.
      * @param enable if true, enable the optional codecs, other disable them
      */
-    private void enableDisableOptionalCodecs(boolean enable) {
+    private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
         try {
             mServiceLock.readLock().lock();
             if (mService != null && isEnabled()) {
                 if (enable) {
-                    mService.enableOptionalCodecs();
+                    mService.enableOptionalCodecs(device);
                 } else {
-                    mService.disableOptionalCodecs();
+                    mService.disableOptionalCodecs(device);
                 }
             }
             if (mService == null) Log.w(TAG, "Proxy not attached to service");
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index c7be0f3..6c8fe2e 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -79,8 +79,9 @@
  * {@link BluetoothDevice} objects representing all paired devices with
  * {@link #getBondedDevices()}; start device discovery with
  * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
- * listen for incoming connection requests with
- * {@link #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
+ * listen for incoming RFComm connection requests with {@link
+ * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
+ * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for
  * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
  * </p>
  * <p>This class is thread safe.</p>
@@ -210,6 +211,14 @@
     public static final int STATE_BLE_TURNING_OFF = 16;
 
     /**
+     * UUID of the GATT Read Characteristics for LE_PSM value.
+     *
+     * @hide
+     */
+    public static final UUID LE_PSM_CHARACTERISTIC_UUID =
+            UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
+
+    /**
      * Human-readable string helper for AdapterState
      *
      * @hide
@@ -1671,6 +1680,27 @@
     }
 
     /**
+     * Get the maximum number of connected audio devices.
+     *
+     * @return the maximum number of connected audio devices
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public int getMaxConnectedAudioDevices() {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                return mService.getMaxConnectedAudioDevices();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return 1;
+    }
+
+    /**
      * Return true if hardware has entries available for matching beacons
      *
      * @return true if there are hw entries available for matching beacons
@@ -2135,7 +2165,9 @@
                         min16DigitPin);
         int errno = socket.mSocket.bindListen();
         if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            socket.setChannel(socket.mSocket.getPort());
+            int assignedChannel = socket.mSocket.getPort();
+            if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel);
+            socket.setChannel(assignedChannel);
         }
         if (errno != 0) {
             //TODO(BT): Throw the same exception error code
@@ -2176,12 +2208,18 @@
      * @hide
      */
     public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
+        Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port);
         BluetoothServerSocket socket =
                 new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
-                        false);
+                                          false);
         int errno = socket.mSocket.bindListen();
         if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            socket.setChannel(socket.mSocket.getPort());
+            int assignedChannel = socket.mSocket.getPort();
+            if (DBG) {
+                Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to "
+                        + assignedChannel);
+            }
+            socket.setChannel(assignedChannel);
         }
         if (errno != 0) {
             //TODO(BT): Throw the same exception error code
@@ -2740,4 +2778,103 @@
             scanner.stopScan(scanCallback);
         }
     }
+
+    /**
+     * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
+     * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
+     * for incoming connections.
+     * <p>A remote device connecting to this socket will be authenticated and communication on this
+     * socket will be encrypted.
+     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
+     * {@link BluetoothServerSocket}.
+     * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {#link
+     * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is
+     * closed, Bluetooth is turned off, or the application exits unexpectedly.
+     * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
+     * defined and performed by the application.
+     * <p>Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server
+     * socket from another Android device that is given the PSM value.
+     *
+     * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
+     * @return an L2CAP CoC BluetoothServerSocket
+     * @throws IOException on error, for example Bluetooth not available, or insufficient
+     * permissions, or unable to start this CoC
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothServerSocket listenUsingL2capCoc(int transport)
+            throws IOException {
+        if (transport != BluetoothDevice.TRANSPORT_LE) {
+            throw new IllegalArgumentException("Unsupported transport: " + transport);
+        }
+        BluetoothServerSocket socket =
+                            new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
+                                      SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
+        int errno = socket.mSocket.bindListen();
+        if (errno != 0) {
+            throw new IOException("Error: " + errno);
+        }
+
+        int assignedPsm = socket.mSocket.getPort();
+        if (assignedPsm == 0) {
+            throw new IOException("Error: Unable to assign PSM value");
+        }
+        if (DBG) {
+            Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to "
+                    + assignedPsm);
+        }
+        socket.setChannel(assignedPsm);
+
+        return socket;
+    }
+
+    /**
+     * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
+     * assign a dynamic PSM value. This socket can be used to listen for incoming connections.
+     * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
+     * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and
+     * authenticated communication channel is desired.
+     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
+     * {@link BluetoothServerSocket}.
+     * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value
+     * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released
+     * when this server socket is closed, Bluetooth is turned off, or the application exits
+     * unexpectedly.
+     * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
+     * defined and performed by the application.
+     * <p>Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this
+     * server socket from another Android device that is given the PSM value.
+     *
+     * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
+     * @return an L2CAP CoC BluetoothServerSocket
+     * @throws IOException on error, for example Bluetooth not available, or insufficient
+     * permissions, or unable to start this CoC
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+            throws IOException {
+        if (transport != BluetoothDevice.TRANSPORT_LE) {
+            throw new IllegalArgumentException("Unsupported transport: " + transport);
+        }
+        BluetoothServerSocket socket =
+                            new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
+                                      SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
+        int errno = socket.mSocket.bindListen();
+        if (errno != 0) {
+            throw new IOException("Error: " + errno);
+        }
+
+        int assignedPsm = socket.mSocket.getPort();
+        if (assignedPsm == 0) {
+            throw new IOException("Error: Unable to assign PSM value");
+        }
+        if (DBG) {
+            Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to "
+                    + assignedPsm);
+        }
+        socket.setChannel(assignedPsm);
+
+        return socket;
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 9b736b7..ac21395 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1921,4 +1921,75 @@
         }
         return null;
     }
+
+    /**
+     * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
+     * be used to start a secure outgoing connection to the remote device with the same dynamic
+     * protocol/service multiplexer (PSM) value.
+     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for
+     * peer-peer Bluetooth applications.
+     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
+     * <p>Application using this API is responsible for obtaining PSM value from remote device.
+     * <p>The remote device will be authenticated and communication on this socket will be
+     * encrypted.
+     * <p> Use this socket if an authenticated socket link is possible. Authentication refers
+     * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a
+     * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
+     * int)}.
+     *
+     * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
+     * @param psm dynamic PSM value from remote device
+     * @return a CoC #BluetoothSocket ready for an outgoing connection
+     * @throws IOException on error, for example Bluetooth not available, or insufficient
+     * permissions
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+        if (!isBluetoothEnabled()) {
+            Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled");
+            throw new IOException();
+        }
+        if (transport != BluetoothDevice.TRANSPORT_LE) {
+            throw new IllegalArgumentException("Unsupported transport: " + transport);
+        }
+        if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm);
+        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
+                null);
+    }
+
+    /**
+     * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
+     * be used to start a secure outgoing connection to the remote device with the same dynamic
+     * protocol/service multiplexer (PSM) value.
+     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)}
+     * for peer-peer Bluetooth applications.
+     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
+     * <p>Application using this API is responsible for obtaining PSM value from remote device.
+     * <p> The communication channel may not have an authenticated link key, i.e. it may be subject
+     * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and
+     * authenticated communication channel is possible.
+     *
+     * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
+     * @param psm dynamic PSM value from remote device
+     * @return a CoC #BluetoothSocket ready for an outgoing connection
+     * @throws IOException on error, for example Bluetooth not available, or insufficient
+     * permissions
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException {
+        if (!isBluetoothEnabled()) {
+            Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled");
+            throw new IOException();
+        }
+        if (transport != BluetoothDevice.TRANSPORT_LE) {
+            throw new IllegalArgumentException("Unsupported transport: " + transport);
+        }
+        if (DBG) {
+            Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm);
+        }
+        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
+                null);
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
index dc00d63..d46b2e3 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
@@ -73,17 +73,18 @@
     private final boolean mOutgoing;
     private final UUID mUUID;
     private final long mCreationElapsedMilli;
+    private final boolean mInBandRing;
 
     /**
      * Creates BluetoothHeadsetClientCall instance.
      */
     public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number,
-            boolean multiParty, boolean outgoing) {
-        this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing);
+            boolean multiParty, boolean outgoing, boolean inBandRing) {
+        this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing, inBandRing);
     }
 
     public BluetoothHeadsetClientCall(BluetoothDevice device, int id, UUID uuid, int state,
-            String number, boolean multiParty, boolean outgoing) {
+            String number, boolean multiParty, boolean outgoing, boolean inBandRing) {
         mDevice = device;
         mId = id;
         mUUID = uuid;
@@ -91,6 +92,7 @@
         mNumber = number != null ? number : "";
         mMultiParty = multiParty;
         mOutgoing = outgoing;
+        mInBandRing = inBandRing;
         mCreationElapsedMilli = SystemClock.elapsedRealtime();
     }
 
@@ -200,6 +202,16 @@
         return mOutgoing;
     }
 
+    /**
+     * Checks if the ringtone will be generated by the connected phone
+     *
+     * @return <code>true</code> if in band ring is enabled, <code>false</code> otherwise.
+     */
+    public boolean isInBandRing() {
+        return mInBandRing;
+    }
+
+
     @Override
     public String toString() {
         return toString(false);
@@ -253,6 +265,8 @@
         builder.append(mMultiParty);
         builder.append(", mOutgoing: ");
         builder.append(mOutgoing);
+        builder.append(", mInBandRing: ");
+        builder.append(mInBandRing);
         builder.append("}");
         return builder.toString();
     }
@@ -266,7 +280,8 @@
                 public BluetoothHeadsetClientCall createFromParcel(Parcel in) {
                     return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null),
                             in.readInt(), UUID.fromString(in.readString()), in.readInt(),
-                            in.readString(), in.readInt() == 1, in.readInt() == 1);
+                            in.readString(), in.readInt() == 1, in.readInt() == 1,
+                            in.readInt() == 1);
                 }
 
                 @Override
@@ -284,6 +299,7 @@
         out.writeString(mNumber);
         out.writeInt(mMultiParty ? 1 : 0);
         out.writeInt(mOutgoing ? 1 : 0);
+        out.writeInt(mInBandRing ? 1 : 0);
     }
 
     @Override
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 58d090d..ebb7f18 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -68,6 +68,7 @@
 public final class BluetoothServerSocket implements Closeable {
 
     private static final String TAG = "BluetoothServerSocket";
+    private static final boolean DBG = false;
     /*package*/ final BluetoothSocket mSocket;
     private Handler mHandler;
     private int mMessage;
@@ -169,6 +170,7 @@
      * close any {@link BluetoothSocket} received from {@link #accept()}.
      */
     public void close() throws IOException {
+        if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel);
         synchronized (this) {
             if (mHandler != null) {
                 mHandler.obtainMessage(mMessage).sendToTarget();
@@ -197,6 +199,20 @@
     }
 
     /**
+     * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
+     * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
+     * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link
+     * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this
+     * method is called on non-L2CAP server sockets.
+     *
+     * @return the assigned PSM or LE_PSM value depending on transport
+     * @hide
+     */
+    public int getPsm() {
+        return mChannel;
+    }
+
+    /**
      * Sets the channel on which future sockets are bound.
      * Currently used only when a channel is auto generated.
      */
@@ -227,6 +243,10 @@
                 sb.append("TYPE_L2CAP");
                 break;
             }
+            case BluetoothSocket.TYPE_L2CAP_LE: {
+                sb.append("TYPE_L2CAP_LE");
+                break;
+            }
             case BluetoothSocket.TYPE_SCO: {
                 sb.append("TYPE_SCO");
                 break;
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 0569913..09f9684 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -99,6 +99,16 @@
     /** L2CAP socket */
     public static final int TYPE_L2CAP = 3;
 
+    /** L2CAP socket on BR/EDR transport
+     * @hide
+     */
+    public static final int TYPE_L2CAP_BREDR = TYPE_L2CAP;
+
+    /** L2CAP socket on LE transport
+     * @hide
+     */
+    public static final int TYPE_L2CAP_LE = 4;
+
     /*package*/ static final int EBADFD = 77;
     /*package*/ static final int EADDRINUSE = 98;
 
@@ -417,6 +427,7 @@
             return -1;
         }
         try {
+            if (DBG) Log.d(TAG, "bindListen(): mPort=" + mPort + ", mType=" + mType);
             mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
                     mUuid, mPort, getSecurityFlags());
         } catch (RemoteException e) {
@@ -451,7 +462,7 @@
                     mSocketState = SocketState.LISTENING;
                 }
             }
-            if (DBG) Log.d(TAG, "channel: " + channel);
+            if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort);
             if (mPort <= -1) {
                 mPort = channel;
             } // else ASSERT(mPort == channel)
@@ -515,7 +526,7 @@
     /*package*/ int read(byte[] b, int offset, int length) throws IOException {
         int ret = 0;
         if (VDBG) Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
-        if (mType == TYPE_L2CAP) {
+        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
             int bytesToRead = length;
             if (VDBG) {
                 Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
@@ -558,7 +569,7 @@
         //      Rfcomm uses dynamic allocation, and should not have any bindings
         //      to the actual message length.
         if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
-        if (mType == TYPE_L2CAP) {
+        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
             if (length <= mMaxTxPacketSize) {
                 mSocketOS.write(b, offset, length);
             } else {
@@ -702,7 +713,7 @@
     }
 
     private void createL2capRxBuffer() {
-        if (mType == TYPE_L2CAP) {
+        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
             // Allocate the buffer to use for reads.
             if (VDBG) Log.v(TAG, "  Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
             mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 70087da..2859326 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4062,6 +4062,16 @@
     public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.se.omapi.ISecureElementService}
+     * for accessing the SecureElementService.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String SECURE_ELEMENT_SERVICE = "secure_element";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8ff6699..c865dd7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3511,7 +3511,10 @@
      * For more details see TelephonyIntents.ACTION_SIM_STATE_CHANGED. This is here
      * because TelephonyIntents is an internal class.
      * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or
+     * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
      */
+    @Deprecated
     @SystemApi
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 664bcbca..80fc8e3 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1458,6 +1458,13 @@
     /**
      * @hide
      */
+    public boolean isAllowedToUseHiddenApi() {
+        return isSystemApp();
+    }
+
+    /**
+     * @hide
+     */
     public boolean isForwardLocked() {
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
     }
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 90bf896..a2bc91e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -996,7 +996,12 @@
                 return;
             }
 
-            Integer oldStatus = mDeviceStatus.put(id, status);
+            Integer oldStatus;
+            if (status == ICameraServiceListener.STATUS_NOT_PRESENT) {
+                oldStatus = mDeviceStatus.remove(id);
+            } else {
+                oldStatus = mDeviceStatus.put(id, status);
+            }
 
             if (oldStatus != null && oldStatus == status) {
                 if (DEBUG) {
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 025d46d..4e8c45d 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -96,6 +96,11 @@
      */
     void setCurrentFunction(String function, boolean usbDataUnlocked);
 
+    /* Sets the screen unlocked USB function(s), which will be set automatically
+     * when the screen is unlocked.
+     */
+    void setScreenUnlockedFunctions(String function);
+
     /* Allow USB debugging from the attached host. If alwaysAllow is true, add the
      * the public key to list of host keys that the user has approved.
      */
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index d73d3d8..48e8d34 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -590,6 +590,32 @@
     }
 
     /**
+     * Sets the screen unlocked functions, which are persisted and set as the current functions
+     * whenever the screen is unlocked.
+     * <p>
+     * The allowed values are: {@link #USB_FUNCTION_NONE},
+     * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
+     * or {@link #USB_FUNCTION_RNDIS}.
+     * {@link #USB_FUNCTION_NONE} has the effect of switching off this feature, so functions
+     * no longer change on screen unlock.
+     * </p><p>
+     * Note: When the screen is on, this method will apply given functions as current functions,
+     * which is asynchronous and may fail silently without applying the requested changes.
+     * </p>
+     *
+     * @param function function to set as default
+     *
+     * {@hide}
+     */
+    public void setScreenUnlockedFunctions(String function) {
+        try {
+            mService.setScreenUnlockedFunctions(function);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns a list of physical USB ports on the device.
      * <p>
      * This list is guaranteed to contain all dual-role USB Type C ports but it might
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index d9b57db..3ce0283 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -21,6 +21,7 @@
 import android.net.IpSecUdpEncapResponse;
 import android.net.IpSecSpiResponse;
 import android.net.IpSecTransformResponse;
+import android.net.IpSecTunnelInterfaceResponse;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -31,7 +32,7 @@
 interface IIpSecService
 {
     IpSecSpiResponse allocateSecurityParameterIndex(
-            int direction, in String remoteAddress, int requestedSpi, in IBinder binder);
+            in String destinationAddress, int requestedSpi, in IBinder binder);
 
     void releaseSecurityParameterIndex(int resourceId);
 
@@ -39,11 +40,29 @@
 
     void closeUdpEncapsulationSocket(int resourceId);
 
-    IpSecTransformResponse createTransportModeTransform(in IpSecConfig c, in IBinder binder);
+    IpSecTunnelInterfaceResponse createTunnelInterface(
+            in String localAddr,
+            in String remoteAddr,
+            in Network underlyingNetwork,
+            in IBinder binder);
 
-    void deleteTransportModeTransform(int transformId);
+    void addAddressToTunnelInterface(
+            int tunnelResourceId,
+            String localAddr);
 
-    void applyTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+    void removeAddressFromTunnelInterface(
+            int tunnelResourceId,
+            String localAddr);
 
-    void removeTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+    void deleteTunnelInterface(int resourceId);
+
+    IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder);
+
+    void deleteTransform(int transformId);
+
+    void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
+
+    void applyTunnelModeTransform(int tunnelResourceId, int direction, int transformResourceId);
+
+    void removeTransportModeTransforms(in ParcelFileDescriptor socket);
 }
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index 005dd6e..10667ae 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -18,10 +18,9 @@
 
 /** {@hide} */
 oneway interface INetworkPolicyListener {
-
     void onUidRulesChanged(int uid, int uidRules);
     void onMeteredIfacesChanged(in String[] meteredIfaces);
     void onRestrictBackgroundChanged(boolean restrictBackground);
     void onUidPoliciesChanged(int uid, int uidPolicies);
-
+    void onSubscriptionOverride(int subId, int overrideMask, int overrideValue);
 }
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 95e7f60..90e3ffd 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -18,6 +18,7 @@
 
 import android.net.DataUsageRequest;
 import android.net.INetworkStatsSession;
+import android.net.Network;
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
@@ -53,7 +54,7 @@
     void setUidForeground(int uid, boolean uidForeground);
 
     /** Force update of ifaces. */
-    void forceUpdateIfaces();
+    void forceUpdateIfaces(in Network[] defaultNetworks);
     /** Force update of statistics. */
     void forceUpdate();
 
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 7d752e8..c69a4d4 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -256,13 +256,19 @@
         return getName().equals(AUTH_CRYPT_AES_GCM);
     }
 
+    // Because encryption keys are sensitive and userdebug builds are used by large user pools
+    // such as beta testers, we only allow sensitive info such as keys on eng builds.
+    private static boolean isUnsafeBuild() {
+        return Build.IS_DEBUGGABLE && Build.IS_ENG;
+    }
+
     @Override
     public String toString() {
         return new StringBuilder()
                 .append("{mName=")
                 .append(mName)
                 .append(", mKey=")
-                .append(Build.IS_DEBUGGABLE ? HexDump.toHexString(mKey) : "<hidden>")
+                .append(isUnsafeBuild() ? HexDump.toHexString(mKey) : "<hidden>")
                 .append(", mTruncLenBits=")
                 .append(mTruncLenBits)
                 .append("}")
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index f54ceb5..6a262e2 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -32,59 +32,29 @@
     // MODE_TRANSPORT or MODE_TUNNEL
     private int mMode = IpSecTransform.MODE_TRANSPORT;
 
-    // Needs to be valid only for tunnel mode
     // Preventing this from being null simplifies Java->Native binder
-    private String mLocalAddress = "";
+    private String mSourceAddress = "";
 
     // Preventing this from being null simplifies Java->Native binder
-    private String mRemoteAddress = "";
+    private String mDestinationAddress = "";
 
     // The underlying Network that represents the "gateway" Network
     // for outbound packets. It may also be used to select packets.
     private Network mNetwork;
 
-    /**
-     * This class captures the parameters that specifically apply to inbound or outbound traffic.
-     */
-    public static class Flow {
-        // Minimum requirements for identifying a transform
-        // SPI identifying the IPsec flow in packet processing
-        // and a remote IP address
-        private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
+    // Minimum requirements for identifying a transform
+    // SPI identifying the IPsec SA in packet processing
+    // and a destination IP address
+    private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
 
-        // Encryption Algorithm
-        private IpSecAlgorithm mEncryption;
+    // Encryption Algorithm
+    private IpSecAlgorithm mEncryption;
 
-        // Authentication Algorithm
-        private IpSecAlgorithm mAuthentication;
+    // Authentication Algorithm
+    private IpSecAlgorithm mAuthentication;
 
-        // Authenticated Encryption Algorithm
-        private IpSecAlgorithm mAuthenticatedEncryption;
-
-        @Override
-        public String toString() {
-            return new StringBuilder()
-                    .append("{mSpiResourceId=")
-                    .append(mSpiResourceId)
-                    .append(", mEncryption=")
-                    .append(mEncryption)
-                    .append(", mAuthentication=")
-                    .append(mAuthentication)
-                    .append(", mAuthenticatedEncryption=")
-                    .append(mAuthenticatedEncryption)
-                    .append("}")
-                    .toString();
-        }
-
-        static boolean equals(IpSecConfig.Flow lhs, IpSecConfig.Flow rhs) {
-            if (lhs == null || rhs == null) return (lhs == rhs);
-            return (lhs.mSpiResourceId == rhs.mSpiResourceId
-                    && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
-                    && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
-        }
-    }
-
-    private final Flow[] mFlow = new Flow[] {new Flow(), new Flow()};
+    // Authenticated Encryption Algorithm
+    private IpSecAlgorithm mAuthenticatedEncryption;
 
     // For tunnel mode IPv4 UDP Encapsulation
     // IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
@@ -95,41 +65,46 @@
     // An interval, in seconds between the NattKeepalive packets
     private int mNattKeepaliveInterval;
 
+    // XFRM mark and mask
+    private int mMarkValue;
+    private int mMarkMask;
+
     /** Set the mode for this IPsec transform */
     public void setMode(int mode) {
         mMode = mode;
     }
 
-    /** Set the local IP address for Tunnel mode */
-    public void setLocalAddress(String localAddress) {
-        mLocalAddress = localAddress;
+    /** Set the source IP addres for this IPsec transform */
+    public void setSourceAddress(String sourceAddress) {
+        mSourceAddress = sourceAddress;
     }
 
-    /** Set the remote IP address for this IPsec transform */
-    public void setRemoteAddress(String remoteAddress) {
-        mRemoteAddress = remoteAddress;
+    /** Set the destination IP address for this IPsec transform */
+    public void setDestinationAddress(String destinationAddress) {
+        mDestinationAddress = destinationAddress;
     }
 
-    /** Set the SPI for a given direction by resource ID */
-    public void setSpiResourceId(int direction, int resourceId) {
-        mFlow[direction].mSpiResourceId = resourceId;
+    /** Set the SPI by resource ID */
+    public void setSpiResourceId(int resourceId) {
+        mSpiResourceId = resourceId;
     }
 
-    /** Set the encryption algorithm for a given direction */
-    public void setEncryption(int direction, IpSecAlgorithm encryption) {
-        mFlow[direction].mEncryption = encryption;
+    /** Set the encryption algorithm */
+    public void setEncryption(IpSecAlgorithm encryption) {
+        mEncryption = encryption;
     }
 
-    /** Set the authentication algorithm for a given direction */
-    public void setAuthentication(int direction, IpSecAlgorithm authentication) {
-        mFlow[direction].mAuthentication = authentication;
+    /** Set the authentication algorithm */
+    public void setAuthentication(IpSecAlgorithm authentication) {
+        mAuthentication = authentication;
     }
 
-    /** Set the authenticated encryption algorithm for a given direction */
-    public void setAuthenticatedEncryption(int direction, IpSecAlgorithm authenticatedEncryption) {
-        mFlow[direction].mAuthenticatedEncryption = authenticatedEncryption;
+    /** Set the authenticated encryption algorithm */
+    public void setAuthenticatedEncryption(IpSecAlgorithm authenticatedEncryption) {
+        mAuthenticatedEncryption = authenticatedEncryption;
     }
 
+    /** Set the underlying network that will carry traffic for this transform */
     public void setNetwork(Network network) {
         mNetwork = network;
     }
@@ -150,33 +125,41 @@
         mNattKeepaliveInterval = interval;
     }
 
+    public void setMarkValue(int mark) {
+        mMarkValue = mark;
+    }
+
+    public void setMarkMask(int mask) {
+        mMarkMask = mask;
+    }
+
     // Transport or Tunnel
     public int getMode() {
         return mMode;
     }
 
-    public String getLocalAddress() {
-        return mLocalAddress;
+    public String getSourceAddress() {
+        return mSourceAddress;
     }
 
-    public int getSpiResourceId(int direction) {
-        return mFlow[direction].mSpiResourceId;
+    public int getSpiResourceId() {
+        return mSpiResourceId;
     }
 
-    public String getRemoteAddress() {
-        return mRemoteAddress;
+    public String getDestinationAddress() {
+        return mDestinationAddress;
     }
 
-    public IpSecAlgorithm getEncryption(int direction) {
-        return mFlow[direction].mEncryption;
+    public IpSecAlgorithm getEncryption() {
+        return mEncryption;
     }
 
-    public IpSecAlgorithm getAuthentication(int direction) {
-        return mFlow[direction].mAuthentication;
+    public IpSecAlgorithm getAuthentication() {
+        return mAuthentication;
     }
 
-    public IpSecAlgorithm getAuthenticatedEncryption(int direction) {
-        return mFlow[direction].mAuthenticatedEncryption;
+    public IpSecAlgorithm getAuthenticatedEncryption() {
+        return mAuthenticatedEncryption;
     }
 
     public Network getNetwork() {
@@ -199,6 +182,14 @@
         return mNattKeepaliveInterval;
     }
 
+    public int getMarkValue() {
+        return mMarkValue;
+    }
+
+    public int getMarkMask() {
+        return mMarkMask;
+    }
+
     // Parcelable Methods
 
     @Override
@@ -209,21 +200,19 @@
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mMode);
-        out.writeString(mLocalAddress);
-        out.writeString(mRemoteAddress);
+        out.writeString(mSourceAddress);
+        out.writeString(mDestinationAddress);
         out.writeParcelable(mNetwork, flags);
-        out.writeInt(mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId);
-        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mEncryption, flags);
-        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthentication, flags);
-        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption, flags);
-        out.writeInt(mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId);
-        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mEncryption, flags);
-        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication, flags);
-        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption, flags);
+        out.writeInt(mSpiResourceId);
+        out.writeParcelable(mEncryption, flags);
+        out.writeParcelable(mAuthentication, flags);
+        out.writeParcelable(mAuthenticatedEncryption, flags);
         out.writeInt(mEncapType);
         out.writeInt(mEncapSocketResourceId);
         out.writeInt(mEncapRemotePort);
         out.writeInt(mNattKeepaliveInterval);
+        out.writeInt(mMarkValue);
+        out.writeInt(mMarkMask);
     }
 
     @VisibleForTesting
@@ -231,27 +220,22 @@
 
     private IpSecConfig(Parcel in) {
         mMode = in.readInt();
-        mLocalAddress = in.readString();
-        mRemoteAddress = in.readString();
+        mSourceAddress = in.readString();
+        mDestinationAddress = in.readString();
         mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
-        mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId = in.readInt();
-        mFlow[IpSecTransform.DIRECTION_IN].mEncryption =
+        mSpiResourceId = in.readInt();
+        mEncryption =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        mFlow[IpSecTransform.DIRECTION_IN].mAuthentication =
+        mAuthentication =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption =
-                (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId = in.readInt();
-        mFlow[IpSecTransform.DIRECTION_OUT].mEncryption =
-                (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication =
-                (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption =
+        mAuthenticatedEncryption =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
         mEncapType = in.readInt();
         mEncapSocketResourceId = in.readInt();
         mEncapRemotePort = in.readInt();
         mNattKeepaliveInterval = in.readInt();
+        mMarkValue = in.readInt();
+        mMarkMask = in.readInt();
     }
 
     @Override
@@ -260,10 +244,10 @@
         strBuilder
                 .append("{mMode=")
                 .append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
-                .append(", mLocalAddress=")
-                .append(mLocalAddress)
-                .append(", mRemoteAddress=")
-                .append(mRemoteAddress)
+                .append(", mSourceAddress=")
+                .append(mSourceAddress)
+                .append(", mDestinationAddress=")
+                .append(mDestinationAddress)
                 .append(", mNetwork=")
                 .append(mNetwork)
                 .append(", mEncapType=")
@@ -274,10 +258,18 @@
                 .append(mEncapRemotePort)
                 .append(", mNattKeepaliveInterval=")
                 .append(mNattKeepaliveInterval)
-                .append(", mFlow[OUT]=")
-                .append(mFlow[IpSecTransform.DIRECTION_OUT])
-                .append(", mFlow[IN]=")
-                .append(mFlow[IpSecTransform.DIRECTION_IN])
+                .append("{mSpiResourceId=")
+                .append(mSpiResourceId)
+                .append(", mEncryption=")
+                .append(mEncryption)
+                .append(", mAuthentication=")
+                .append(mAuthentication)
+                .append(", mAuthenticatedEncryption=")
+                .append(mAuthenticatedEncryption)
+                .append(", mMarkValue=")
+                .append(mMarkValue)
+                .append(", mMarkMask=")
+                .append(mMarkMask)
                 .append("}");
 
         return strBuilder.toString();
@@ -299,17 +291,20 @@
     public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) {
         if (lhs == null || rhs == null) return (lhs == rhs);
         return (lhs.mMode == rhs.mMode
-                && lhs.mLocalAddress.equals(rhs.mLocalAddress)
-                && lhs.mRemoteAddress.equals(rhs.mRemoteAddress)
+                && lhs.mSourceAddress.equals(rhs.mSourceAddress)
+                && lhs.mDestinationAddress.equals(rhs.mDestinationAddress)
                 && ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork))
                         || (lhs.mNetwork == rhs.mNetwork))
                 && lhs.mEncapType == rhs.mEncapType
                 && lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId
                 && lhs.mEncapRemotePort == rhs.mEncapRemotePort
                 && lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
-                && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_OUT],
-                        rhs.mFlow[IpSecTransform.DIRECTION_OUT])
-                && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_IN],
-                        rhs.mFlow[IpSecTransform.DIRECTION_IN]));
+                && lhs.mSpiResourceId == rhs.mSpiResourceId
+                && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
+                && IpSecAlgorithm.equals(
+                        lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
+                && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication)
+                && lhs.mMarkValue == rhs.mMarkValue
+                && lhs.mMarkMask == rhs.mMarkMask);
     }
 }
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 34cfa9b..24a078f 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -17,7 +17,9 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
@@ -33,6 +35,8 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.net.Socket;
@@ -53,6 +57,23 @@
     private static final String TAG = "IpSecManager";
 
     /**
+     * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
+     * applies to traffic towards the host.
+     */
+    public static final int DIRECTION_IN = 0;
+
+    /**
+     * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
+     * applies to traffic from the host.
+     */
+    public static final int DIRECTION_OUT = 1;
+
+    /** @hide */
+    @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PolicyDirection {}
+
+    /**
      * The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
      *
      * <p>No IPsec packet may contain an SPI of 0.
@@ -125,7 +146,7 @@
      */
     public static final class SecurityParameterIndex implements AutoCloseable {
         private final IIpSecService mService;
-        private final InetAddress mRemoteAddress;
+        private final InetAddress mDestinationAddress;
         private final CloseGuard mCloseGuard = CloseGuard.get();
         private int mSpi = INVALID_SECURITY_PARAMETER_INDEX;
         private int mResourceId = INVALID_RESOURCE_ID;
@@ -164,14 +185,14 @@
         }
 
         private SecurityParameterIndex(
-                @NonNull IIpSecService service, int direction, InetAddress remoteAddress, int spi)
+                @NonNull IIpSecService service, InetAddress destinationAddress, int spi)
                 throws ResourceUnavailableException, SpiUnavailableException {
             mService = service;
-            mRemoteAddress = remoteAddress;
+            mDestinationAddress = destinationAddress;
             try {
                 IpSecSpiResponse result =
                         mService.allocateSecurityParameterIndex(
-                                direction, remoteAddress.getHostAddress(), spi, new Binder());
+                                destinationAddress.getHostAddress(), spi, new Binder());
 
                 if (result == null) {
                     throw new NullPointerException("Received null response from IpSecService");
@@ -216,25 +237,23 @@
     }
 
     /**
-     * Reserve a random SPI for traffic bound to or from the specified remote address.
+     * Reserve a random SPI for traffic bound to or from the specified destination address.
      *
      * <p>If successful, this SPI is guaranteed available until released by a call to {@link
      * SecurityParameterIndex#close()}.
      *
-     * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
-     * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress
+     * @param destinationAddress the destination address for traffic bearing the requested SPI.
+     *     For inbound traffic, the destination should be an address currently assigned on-device.
      * @return the reserved SecurityParameterIndex
-     * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
-     *     for this user
-     * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
+     * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
+     *     currently allocated for this user
      */
-    public SecurityParameterIndex allocateSecurityParameterIndex(
-            int direction, InetAddress remoteAddress) throws ResourceUnavailableException {
+    public SecurityParameterIndex allocateSecurityParameterIndex(InetAddress destinationAddress)
+            throws ResourceUnavailableException {
         try {
             return new SecurityParameterIndex(
                     mService,
-                    direction,
-                    remoteAddress,
+                    destinationAddress,
                     IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
         } catch (SpiUnavailableException unlikely) {
             throw new ResourceUnavailableException("No SPIs available");
@@ -242,26 +261,27 @@
     }
 
     /**
-     * Reserve the requested SPI for traffic bound to or from the specified remote address.
+     * Reserve the requested SPI for traffic bound to or from the specified destination address.
      *
      * <p>If successful, this SPI is guaranteed available until released by a call to {@link
      * SecurityParameterIndex#close()}.
      *
-     * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
-     * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress
+     * @param destinationAddress the destination address for traffic bearing the requested SPI.
+     *     For inbound traffic, the destination should be an address currently assigned on-device.
      * @param requestedSpi the requested SPI, or '0' to allocate a random SPI
      * @return the reserved SecurityParameterIndex
-     * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
-     *     for this user
-     * @throws SpiUnavailableException indicating that the requested SPI could not be reserved
+     * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
+     *     currently allocated for this user
+     * @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be
+     *     reserved
      */
     public SecurityParameterIndex allocateSecurityParameterIndex(
-            int direction, InetAddress remoteAddress, int requestedSpi)
+            InetAddress destinationAddress, int requestedSpi)
             throws SpiUnavailableException, ResourceUnavailableException {
         if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
             throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
         }
-        return new SecurityParameterIndex(mService, direction, remoteAddress, requestedSpi);
+        return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
     }
 
     /**
@@ -269,14 +289,14 @@
      *
      * <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
      * socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
-     * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+     * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
      * unprotected traffic can resume on that socket.
      *
      * <p>For security reasons, the destination address of any traffic on the socket must match the
      * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
      * other IP address will result in an IOException. In addition, reads and writes on the socket
      * will throw IOException if the user deactivates the transform (by calling {@link
-     * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+     * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
      *
      * <h4>Rekey Procedure</h4>
      *
@@ -287,15 +307,14 @@
      * in-flight packets have been received.
      *
      * @param socket a stream socket
+     * @param direction the policy direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
-     * @hide
      */
-    public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
+    public void applyTransportModeTransform(
+            Socket socket, int direction, IpSecTransform transform)
             throws IOException {
-        try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
-            applyTransportModeTransform(pfd, transform);
-        }
+        applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
     }
 
     /**
@@ -303,14 +322,14 @@
      *
      * <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
      * socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
-     * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+     * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
      * unprotected traffic can resume on that socket.
      *
      * <p>For security reasons, the destination address of any traffic on the socket must match the
      * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
      * other IP address will result in an IOException. In addition, reads and writes on the socket
      * will throw IOException if the user deactivates the transform (by calling {@link
-     * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+     * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
      *
      * <h4>Rekey Procedure</h4>
      *
@@ -321,15 +340,13 @@
      * in-flight packets have been received.
      *
      * @param socket a datagram socket
+     * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
-     * @hide
      */
-    public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
-            throws IOException {
-        try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
-            applyTransportModeTransform(pfd, transform);
-        }
+    public void applyTransportModeTransform(
+            DatagramSocket socket, int direction, IpSecTransform transform) throws IOException {
+        applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
     }
 
     /**
@@ -337,14 +354,14 @@
      *
      * <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
      * socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
-     * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+     * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
      * unprotected traffic can resume on that socket.
      *
      * <p>For security reasons, the destination address of any traffic on the socket must match the
      * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
      * other IP address will result in an IOException. In addition, reads and writes on the socket
      * will throw IOException if the user deactivates the transform (by calling {@link
-     * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+     * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
      *
      * <h4>Rekey Procedure</h4>
      *
@@ -355,24 +372,17 @@
      * in-flight packets have been received.
      *
      * @param socket a socket file descriptor
+     * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
      */
-    public void applyTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
+    public void applyTransportModeTransform(
+            FileDescriptor socket, int direction, IpSecTransform transform)
             throws IOException {
         // We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor()
-        // constructor takes control and closes the user's FD when we exit the method
-        // This is behaviorally the same as the other versions, but the PFD constructor does not
-        // dup() automatically, whereas PFD.fromSocket() and PDF.fromDatagramSocket() do dup().
+        // constructor takes control and closes the user's FD when we exit the method.
         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
-            applyTransportModeTransform(pfd, transform);
-        }
-    }
-
-    /* Call down to activate a transform */
-    private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
-        try {
-            mService.applyTransportModeTransform(pfd, transform.getResourceId());
+            mService.applyTransportModeTransform(pfd, direction, transform.getResourceId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -396,75 +406,56 @@
     /**
      * Remove an IPsec transform from a stream socket.
      *
-     * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
-     * regardless of the state of the transform. Removing a transform from a socket allows the
-     * socket to be reused for communication in the clear.
+     * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+     * socket allows the socket to be reused for communication in the clear.
      *
      * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
      * {@link IpSecTransform#close()}, then communication on the socket will fail until this method
      * is called.
      *
      * @param socket a socket that previously had a transform applied to it
-     * @param transform the IPsec Transform that was previously applied to the given socket
      * @throws IOException indicating that the transform could not be removed from the socket
-     * @hide
      */
-    public void removeTransportModeTransform(Socket socket, IpSecTransform transform)
+    public void removeTransportModeTransforms(Socket socket)
             throws IOException {
-        try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
-            removeTransportModeTransform(pfd, transform);
-        }
+        removeTransportModeTransforms(socket.getFileDescriptor$());
     }
 
     /**
      * Remove an IPsec transform from a datagram socket.
      *
-     * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
-     * regardless of the state of the transform. Removing a transform from a socket allows the
-     * socket to be reused for communication in the clear.
+     * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+     * socket allows the socket to be reused for communication in the clear.
      *
      * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
      * {@link IpSecTransform#close()}, then communication on the socket will fail until this method
      * is called.
      *
      * @param socket a socket that previously had a transform applied to it
-     * @param transform the IPsec Transform that was previously applied to the given socket
      * @throws IOException indicating that the transform could not be removed from the socket
-     * @hide
      */
-    public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
+    public void removeTransportModeTransforms(DatagramSocket socket)
             throws IOException {
-        try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
-            removeTransportModeTransform(pfd, transform);
-        }
+        removeTransportModeTransforms(socket.getFileDescriptor$());
     }
 
     /**
      * Remove an IPsec transform from a socket.
      *
-     * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
-     * regardless of the state of the transform. Removing a transform from a socket allows the
-     * socket to be reused for communication in the clear.
+     * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+     * socket allows the socket to be reused for communication in the clear.
      *
      * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
      * {@link IpSecTransform#close()}, then communication on the socket will fail until this method
      * is called.
      *
      * @param socket a socket that previously had a transform applied to it
-     * @param transform the IPsec Transform that was previously applied to the given socket
      * @throws IOException indicating that the transform could not be removed from the socket
      */
-    public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
+    public void removeTransportModeTransforms(FileDescriptor socket)
             throws IOException {
         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
-            removeTransportModeTransform(pfd, transform);
-        }
-    }
-
-    /* Call down to remove a transform */
-    private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
-        try {
-            mService.removeTransportModeTransform(pfd, transform.getResourceId());
+            mService.removeTransportModeTransforms(pfd);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -635,6 +626,170 @@
     }
 
     /**
+     * This class represents an IpSecTunnelInterface
+     *
+     * <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
+     * local endpoints for IPsec tunnels.
+     *
+     * <p>Creating an IpSecTunnelInterface creates a device to which IpSecTransforms may be
+     * applied to provide IPsec security to packets sent through the tunnel. While a tunnel
+     * cannot be used in standalone mode within Android, the higher layers may use the tunnel
+     * to create Network objects which are accessible to the Android system.
+     * @hide
+     */
+    @SystemApi
+    public static final class IpSecTunnelInterface implements AutoCloseable {
+        private final IIpSecService mService;
+        private final InetAddress mRemoteAddress;
+        private final InetAddress mLocalAddress;
+        private final Network mUnderlyingNetwork;
+        private final CloseGuard mCloseGuard = CloseGuard.get();
+        private String mInterfaceName;
+        private int mResourceId = INVALID_RESOURCE_ID;
+
+        /** Get the underlying SPI held by this object. */
+        public String getInterfaceName() {
+            return mInterfaceName;
+        }
+
+        /**
+         * Add an address to the IpSecTunnelInterface
+         *
+         * <p>Add an address which may be used as the local inner address for
+         * tunneled traffic.
+         *
+         * @param address the local address for traffic inside the tunnel
+         * @throws IOException if the address could not be added
+         * @hide
+         */
+        public void addAddress(LinkAddress address) throws IOException {
+        }
+
+        /**
+         * Remove an address from the IpSecTunnelInterface
+         *
+         * <p>Remove an address which was previously added to the IpSecTunnelInterface
+         *
+         * @param address to be removed
+         * @throws IOException if the address could not be removed
+         * @hide
+         */
+        public void removeAddress(LinkAddress address) throws IOException {
+        }
+
+        private IpSecTunnelInterface(@NonNull IIpSecService service,
+                @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress,
+                @NonNull Network underlyingNetwork)
+                throws ResourceUnavailableException, IOException {
+            mService = service;
+            mLocalAddress = localAddress;
+            mRemoteAddress = remoteAddress;
+            mUnderlyingNetwork = underlyingNetwork;
+
+            try {
+                IpSecTunnelInterfaceResponse result =
+                        mService.createTunnelInterface(
+                                localAddress.getHostAddress(),
+                                remoteAddress.getHostAddress(),
+                                underlyingNetwork,
+                                new Binder());
+                switch (result.status) {
+                    case Status.OK:
+                        break;
+                    case Status.RESOURCE_UNAVAILABLE:
+                        throw new ResourceUnavailableException(
+                                "No more tunnel interfaces may be allocated by this requester.");
+                    default:
+                        throw new RuntimeException(
+                                "Unknown status returned by IpSecService: " + result.status);
+                }
+                mResourceId = result.resourceId;
+                mInterfaceName = result.interfaceName;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mCloseGuard.open("constructor");
+        }
+
+        /**
+         * Delete an IpSecTunnelInterface
+         *
+         * <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
+         * resources. Any packets bound for this interface either inbound or outbound will
+         * all be lost.
+         */
+        @Override
+        public void close() {
+            try {
+                mService.deleteTunnelInterface(mResourceId);
+                mResourceId = INVALID_RESOURCE_ID;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mCloseGuard.close();
+        }
+
+        /** Check that the Interface was closed properly. */
+        @Override
+        protected void finalize() throws Throwable {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            close();
+        }
+
+        /** @hide */
+        @VisibleForTesting
+        public int getResourceId() {
+            return mResourceId;
+        }
+    }
+
+    /**
+     * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
+     *
+     * <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
+     * underlying network goes away, and the onLost() callback is received.
+     *
+     * @param localAddress The local addres of the tunnel
+     * @param remoteAddress The local addres of the tunnel
+     * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
+     *        This network should almost certainly be a network such as WiFi with an L2 address.
+     * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
+     * @throws IOException indicating that the socket could not be opened or bound
+     * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
+     * @hide
+     */
+    @SystemApi
+    public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
+            @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
+            throws ResourceUnavailableException, IOException {
+        return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork);
+    }
+
+    /**
+     * Apply a transform to the IpSecTunnelInterface
+     *
+     * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
+     *        transform.
+     * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+     *        the transform will be used.
+     * @param transform an {@link IpSecTransform} created in tunnel mode
+     * @throws IOException indicating that the transform could not be applied due to a lower
+     *         layer failure.
+     * @hide
+     */
+    @SystemApi
+    public void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
+            IpSecTransform transform) throws IOException {
+        try {
+            mService.applyTunnelModeTransform(
+                    tunnel.getResourceId(), direction, transform.getResourceId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+    /**
      * Construct an instance of IpSecManager within an application context.
      *
      * @param context the application context for this manager
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 102ba6d..9ccdbe2 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -17,11 +17,14 @@
 
 import static android.net.IpSecManager.INVALID_RESOURCE_ID;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -38,13 +41,11 @@
 import java.net.InetAddress;
 
 /**
- * This class represents an IPsec transform, which comprises security associations in one or both
- * directions.
+ * This class represents a transform, which roughly corresponds to an IPsec Security Association.
  *
  * <p>Transforms are created using {@link IpSecTransform.Builder}. Each {@code IpSecTransform}
- * object encapsulates the properties and state of an inbound and outbound IPsec security
- * association. That includes, but is not limited to, algorithm choice, key material, and allocated
- * system resources.
+ * object encapsulates the properties and state of an IPsec security association. That includes,
+ * but is not limited to, algorithm choice, key material, and allocated system resources.
  *
  * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
  *     Internet Protocol</a>
@@ -52,23 +53,6 @@
 public final class IpSecTransform implements AutoCloseable {
     private static final String TAG = "IpSecTransform";
 
-    /**
-     * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
-     * applies to traffic towards the host.
-     */
-    public static final int DIRECTION_IN = 0;
-
-    /**
-     * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
-     * applies to traffic from the host.
-     */
-    public static final int DIRECTION_OUT = 1;
-
-    /** @hide */
-    @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TransformDirection {}
-
     /** @hide */
     public static final int MODE_TRANSPORT = 0;
 
@@ -143,18 +127,10 @@
         synchronized (this) {
             try {
                 IIpSecService svc = getIpSecService();
-                IpSecTransformResponse result =
-                        svc.createTransportModeTransform(mConfig, new Binder());
+                IpSecTransformResponse result = svc.createTransform(mConfig, new Binder());
                 int status = result.status;
                 checkResultStatus(status);
                 mResourceId = result.resourceId;
-
-                /* Keepalive will silently fail if not needed by the config; but, if needed and
-                 * it fails to start, we need to bail because a transform will not be reliable
-                 * to use if keepalive is expected to offload and fails.
-                 */
-                // FIXME: if keepalive fails, we need to fail spectacularly
-                startKeepalive(mContext);
                 Log.d(TAG, "Added Transform with Id " + mResourceId);
                 mCloseGuard.open("build");
             } catch (RemoteException e) {
@@ -170,7 +146,7 @@
      *
      * <p>Deactivating a transform while it is still applied to a socket will result in errors on
      * that socket. Make sure to remove transforms by calling {@link
-     * IpSecManager#removeTransportModeTransform}. Note, removing an {@code IpSecTransform} from a
+     * IpSecManager#removeTransportModeTransforms}. Note, removing an {@code IpSecTransform} from a
      * socket will not deactivate it (because one transform may be applied to multiple sockets).
      *
      * <p>It is safe to call this method on a transform that has already been deactivated.
@@ -184,13 +160,9 @@
             return;
         }
         try {
-            /* Order matters here because the keepalive is best-effort but could fail in some
-             * horrible way to be removed if the wifi (or cell) subsystem has crashed, and we
-             * still want to clear out the transform.
-             */
             IIpSecService svc = getIpSecService();
-            svc.deleteTransportModeTransform(mResourceId);
-            stopKeepalive();
+            svc.deleteTransform(mResourceId);
+            stopNattKeepalive();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         } finally {
@@ -218,42 +190,35 @@
     private final Context mContext;
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private ConnectivityManager.PacketKeepalive mKeepalive;
-    private int mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
-    private Object mKeepaliveSyncLock = new Object();
-    private ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
+    private Handler mCallbackHandler;
+    private final ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
             new ConnectivityManager.PacketKeepaliveCallback() {
 
                 @Override
                 public void onStarted() {
-                    synchronized (mKeepaliveSyncLock) {
-                        mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS;
-                        mKeepaliveSyncLock.notifyAll();
+                    synchronized (this) {
+                        mCallbackHandler.post(() -> mUserKeepaliveCallback.onStarted());
                     }
                 }
 
                 @Override
                 public void onStopped() {
-                    synchronized (mKeepaliveSyncLock) {
-                        mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
-                        mKeepaliveSyncLock.notifyAll();
+                    synchronized (this) {
+                        mKeepalive = null;
+                        mCallbackHandler.post(() -> mUserKeepaliveCallback.onStopped());
                     }
                 }
 
                 @Override
                 public void onError(int error) {
-                    synchronized (mKeepaliveSyncLock) {
-                        mKeepaliveStatus = error;
-                        mKeepaliveSyncLock.notifyAll();
+                    synchronized (this) {
+                        mKeepalive = null;
+                        mCallbackHandler.post(() -> mUserKeepaliveCallback.onError(error));
                     }
                 }
             };
 
-    /* Package */
-    void startKeepalive(Context c) {
-        if (mConfig.getNattKeepaliveInterval() != 0) {
-            Log.wtf(TAG, "Keepalive not yet supported.");
-        }
-    }
+    private NattKeepaliveCallback mUserKeepaliveCallback;
 
     /** @hide */
     @VisibleForTesting
@@ -261,9 +226,93 @@
         return mResourceId;
     }
 
-    /* Package */
-    void stopKeepalive() {
-        return;
+    /**
+     * A callback class to provide status information regarding a NAT-T keepalive session
+     *
+     * <p>Use this callback to receive status information regarding a NAT-T keepalive session
+     * by registering it when calling {@link #startNattKeepalive}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static class NattKeepaliveCallback {
+        /** The specified {@code Network} is not connected. */
+        public static final int ERROR_INVALID_NETWORK = 1;
+        /** The hardware does not support this request. */
+        public static final int ERROR_HARDWARE_UNSUPPORTED = 2;
+        /** The hardware returned an error. */
+        public static final int ERROR_HARDWARE_ERROR = 3;
+
+        /** The requested keepalive was successfully started. */
+        public void onStarted() {}
+        /** The keepalive was successfully stopped. */
+        public void onStopped() {}
+        /** An error occurred. */
+        public void onError(int error) {}
+    }
+
+    /**
+     * Start a NAT-T keepalive session for the current transform.
+     *
+     * For a transform that is using UDP encapsulated IPv4, NAT-T offloading provides
+     * a power efficient mechanism of sending NAT-T packets at a specified interval.
+     *
+     * @param userCallback a {@link #NattKeepaliveCallback} to receive asynchronous status
+     *      information about the requested NAT-T keepalive session.
+     * @param intervalSeconds the interval between NAT-T keepalives being sent. The
+     *      the allowed range is between 20 and 3600 seconds.
+     * @param handler a handler on which to post callbacks when received.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback,
+            int intervalSeconds, @NonNull Handler handler) throws IOException {
+        checkNotNull(userCallback);
+        if (intervalSeconds < 20 || intervalSeconds > 3600) {
+            throw new IllegalArgumentException("Invalid NAT-T keepalive interval");
+        }
+        checkNotNull(handler);
+        if (mResourceId == INVALID_RESOURCE_ID) {
+            throw new IllegalStateException(
+                    "Packet keepalive cannot be started for an inactive transform");
+        }
+
+        synchronized (mKeepaliveCallback) {
+            if (mKeepaliveCallback != null) {
+                throw new IllegalStateException("Keepalive already active");
+            }
+
+            mUserKeepaliveCallback = userCallback;
+            ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+                    Context.CONNECTIVITY_SERVICE);
+            mKeepalive = cm.startNattKeepalive(
+                    mConfig.getNetwork(), intervalSeconds, mKeepaliveCallback,
+                    NetworkUtils.numericToInetAddress(mConfig.getSourceAddress()),
+                    4500, // FIXME urgently, we need to get the port number from the Encap socket
+                    NetworkUtils.numericToInetAddress(mConfig.getDestinationAddress()));
+            mCallbackHandler = handler;
+        }
+    }
+
+    /**
+     * Stop an ongoing NAT-T keepalive session.
+     *
+     * Calling this API will request that an ongoing NAT-T keepalive session be terminated.
+     * If this API is not called when a Transform is closed, the underlying NAT-T session will
+     * be terminated automatically.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void stopNattKeepalive() {
+        synchronized (mKeepaliveCallback) {
+            if (mKeepalive == null) {
+                Log.e(TAG, "No active keepalive to stop");
+                return;
+            }
+            mKeepalive.stop();
+        }
     }
 
     /** This class is used to build {@link IpSecTransform} objects. */
@@ -272,99 +321,49 @@
         private IpSecConfig mConfig;
 
         /**
-         * Set the encryption algorithm for the given direction.
-         *
-         * <p>If encryption is set for a direction without also providing an SPI for that direction,
-         * creation of an {@code IpSecTransform} will fail when attempting to build the transform.
+         * Set the encryption algorithm.
          *
          * <p>Encryption is mutually exclusive with authenticated encryption.
          *
-         * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
          * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
          */
-        public IpSecTransform.Builder setEncryption(
-                @TransformDirection int direction, IpSecAlgorithm algo) {
+        public IpSecTransform.Builder setEncryption(@NonNull IpSecAlgorithm algo) {
             // TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
-            mConfig.setEncryption(direction, algo);
+            Preconditions.checkNotNull(algo);
+            mConfig.setEncryption(algo);
             return this;
         }
 
         /**
-         * Set the authentication (integrity) algorithm for the given direction.
-         *
-         * <p>If authentication is set for a direction without also providing an SPI for that
-         * direction, creation of an {@code IpSecTransform} will fail when attempting to build the
-         * transform.
+         * Set the authentication (integrity) algorithm.
          *
          * <p>Authentication is mutually exclusive with authenticated encryption.
          *
-         * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
          * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
          */
-        public IpSecTransform.Builder setAuthentication(
-                @TransformDirection int direction, IpSecAlgorithm algo) {
+        public IpSecTransform.Builder setAuthentication(@NonNull IpSecAlgorithm algo) {
             // TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
-            mConfig.setAuthentication(direction, algo);
+            Preconditions.checkNotNull(algo);
+            mConfig.setAuthentication(algo);
             return this;
         }
 
         /**
-         * Set the authenticated encryption algorithm for the given direction.
+         * Set the authenticated encryption algorithm.
          *
-         * <p>If an authenticated encryption algorithm is set for a given direction without also
-         * providing an SPI for that direction, creation of an {@code IpSecTransform} will fail when
-         * attempting to build the transform.
-         *
-         * <p>The Authenticated Encryption (AE) class of algorithms are also known as Authenticated
-         * Encryption with Associated Data (AEAD) algorithms, or Combined mode algorithms (as
-         * referred to in <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
+         * <p>The Authenticated Encryption (AE) class of algorithms are also known as
+         * Authenticated Encryption with Associated Data (AEAD) algorithms, or Combined mode
+         * algorithms (as referred to in
+         * <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
          *
          * <p>Authenticated encryption is mutually exclusive with encryption and authentication.
          *
-         * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
          * @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
          *     be applied.
          */
-        public IpSecTransform.Builder setAuthenticatedEncryption(
-                @TransformDirection int direction, IpSecAlgorithm algo) {
-            mConfig.setAuthenticatedEncryption(direction, algo);
-            return this;
-        }
-
-        /**
-         * Set the SPI for the given direction.
-         *
-         * <p>Because IPsec operates at the IP layer, this 32-bit identifier uniquely identifies
-         * packets to a given destination address. To prevent SPI collisions, values should be
-         * reserved by calling {@link IpSecManager#allocateSecurityParameterIndex}.
-         *
-         * <p>If the SPI and algorithms are omitted for one direction, traffic in that direction
-         * will not be encrypted or authenticated.
-         *
-         * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
-         * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
-         *     traffic
-         */
-        public IpSecTransform.Builder setSpi(
-                @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
-            if (spi.getResourceId() == INVALID_RESOURCE_ID) {
-                throw new IllegalArgumentException("Invalid SecurityParameterIndex");
-            }
-            mConfig.setSpiResourceId(direction, spi.getResourceId());
-            return this;
-        }
-
-        /**
-         * Set the {@link Network} which will carry tunneled traffic.
-         *
-         * <p>Restricts the transformed traffic to a particular {@link Network}. This is required
-         * for tunnel mode, otherwise tunneled traffic would be sent on the default network.
-         *
-         * @hide
-         */
-        @SystemApi
-        public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
-            mConfig.setNetwork(net);
+        public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
+            Preconditions.checkNotNull(algo);
+            mConfig.setAuthenticatedEncryption(algo);
             return this;
         }
 
@@ -382,7 +381,8 @@
          *     encapsulated traffic. In the case of IKEv2, this should be port 4500.
          */
         public IpSecTransform.Builder setIpv4Encapsulation(
-                IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
+                @NonNull IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
+            Preconditions.checkNotNull(localSocket);
             mConfig.setEncapType(ENCAP_ESPINUDP);
             if (localSocket.getResourceId() == INVALID_RESOURCE_ID) {
                 throw new IllegalArgumentException("Invalid UdpEncapsulationSocket");
@@ -392,26 +392,6 @@
             return this;
         }
 
-        // TODO: Decrease the minimum keepalive to maybe 10?
-        // TODO: Probably a better exception to throw for NATTKeepalive failure
-        // TODO: Specify the needed NATT keepalive permission.
-        /**
-         * Set NAT-T keepalives to be sent with a given interval.
-         *
-         * <p>This will set power-efficient keepalive packets to be sent by the system. If NAT-T
-         * keepalive is requested but cannot be activated, then creation of an {@link
-         * IpSecTransform} will fail when calling the build method.
-         *
-         * @param intervalSeconds the maximum number of seconds between keepalive packets. Must be
-         *     between 20s and 3600s.
-         * @hide
-         */
-        @SystemApi
-        public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
-            mConfig.setNattKeepaliveInterval(intervalSeconds);
-            return this;
-        }
-
         /**
          * Build a transport mode {@link IpSecTransform}.
          *
@@ -419,24 +399,33 @@
          * will not affect any network traffic until it has been applied to one or more sockets.
          *
          * @see IpSecManager#applyTransportModeTransform
-         * @param remoteAddress the remote {@code InetAddress} of traffic on sockets that will use
-         *     this transform
+         * @param sourceAddress the source {@code InetAddress} of traffic on sockets that will use
+         *     this transform; this address must belong to the Network used by all sockets that
+         *     utilize this transform; if provided, then only traffic originating from the
+         *     specified source address will be processed.
+         * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+         *     traffic
          * @throws IllegalArgumentException indicating that a particular combination of transform
          *     properties is invalid
-         * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms are
-         *     active
+         * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
+         *     are active
          * @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
          *     collides with an existing transform
          * @throws IOException indicating other errors
          */
-        public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
+        public IpSecTransform buildTransportModeTransform(
+                @NonNull InetAddress sourceAddress,
+                @NonNull IpSecManager.SecurityParameterIndex spi)
                 throws IpSecManager.ResourceUnavailableException,
                         IpSecManager.SpiUnavailableException, IOException {
-            if (remoteAddress == null) {
-                throw new IllegalArgumentException("Remote address may not be null or empty!");
+            Preconditions.checkNotNull(sourceAddress);
+            Preconditions.checkNotNull(spi);
+            if (spi.getResourceId() == INVALID_RESOURCE_ID) {
+                throw new IllegalArgumentException("Invalid SecurityParameterIndex");
             }
             mConfig.setMode(MODE_TRANSPORT);
-            mConfig.setRemoteAddress(remoteAddress.getHostAddress());
+            mConfig.setSourceAddress(sourceAddress.getHostAddress());
+            mConfig.setSpiResourceId(spi.getResourceId());
             // FIXME: modifying a builder after calling build can change the built transform.
             return new IpSecTransform(mContext, mConfig).activate();
         }
@@ -445,26 +434,34 @@
          * Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
          * parameters have interdependencies that are checked at build time.
          *
-         * @param localAddress the {@link InetAddress} that provides the local endpoint for this
+         * @param sourceAddress the {@link InetAddress} that provides the source address for this
          *     IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
          *     that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
-         * @param remoteAddress the {@link InetAddress} representing the remote endpoint of this
-         *     IPsec tunnel.
+         * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+         *     traffic
          * @throws IllegalArgumentException indicating that a particular combination of transform
          *     properties is invalid.
+         * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
+         *     are active
+         * @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
+         *     collides with an existing transform
+         * @throws IOException indicating other errors
          * @hide
          */
+        @SystemApi
         public IpSecTransform buildTunnelModeTransform(
-                InetAddress localAddress, InetAddress remoteAddress) {
-            if (localAddress == null) {
-                throw new IllegalArgumentException("Local address may not be null or empty!");
+                @NonNull InetAddress sourceAddress,
+                @NonNull IpSecManager.SecurityParameterIndex spi)
+                throws IpSecManager.ResourceUnavailableException,
+                        IpSecManager.SpiUnavailableException, IOException {
+            Preconditions.checkNotNull(sourceAddress);
+            Preconditions.checkNotNull(spi);
+            if (spi.getResourceId() == INVALID_RESOURCE_ID) {
+                throw new IllegalArgumentException("Invalid SecurityParameterIndex");
             }
-            if (remoteAddress == null) {
-                throw new IllegalArgumentException("Remote address may not be null or empty!");
-            }
-            mConfig.setLocalAddress(localAddress.getHostAddress());
-            mConfig.setRemoteAddress(remoteAddress.getHostAddress());
             mConfig.setMode(MODE_TUNNEL);
+            mConfig.setSourceAddress(sourceAddress.getHostAddress());
+            mConfig.setSpiResourceId(spi.getResourceId());
             return new IpSecTransform(mContext, mConfig);
         }
 
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/core/java/android/net/IpSecTunnelInterfaceResponse.aidl
similarity index 82%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to core/java/android/net/IpSecTunnelInterfaceResponse.aidl
index d750363..7239221 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/core/java/android/net/IpSecTunnelInterfaceResponse.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-/** @hide */
-package android.telephony.data;
+package android.net;
 
-parcelable InterfaceAddress;
+/** @hide */
+parcelable IpSecTunnelInterfaceResponse;
diff --git a/core/java/android/net/IpSecTunnelInterfaceResponse.java b/core/java/android/net/IpSecTunnelInterfaceResponse.java
new file mode 100644
index 0000000..c23d831
--- /dev/null
+++ b/core/java/android/net/IpSecTunnelInterfaceResponse.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class is used to return an IpSecTunnelInterface resource Id and and corresponding status
+ * from the IpSecService to an IpSecTunnelInterface object.
+ *
+ * @hide
+ */
+public final class IpSecTunnelInterfaceResponse implements Parcelable {
+    private static final String TAG = "IpSecTunnelInterfaceResponse";
+
+    public final int resourceId;
+    public final String interfaceName;
+    public final int status;
+    // Parcelable Methods
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(status);
+        out.writeInt(resourceId);
+        out.writeString(interfaceName);
+    }
+
+    public IpSecTunnelInterfaceResponse(int inStatus) {
+        if (inStatus == IpSecManager.Status.OK) {
+            throw new IllegalArgumentException("Valid status implies other args must be provided");
+        }
+        status = inStatus;
+        resourceId = IpSecManager.INVALID_RESOURCE_ID;
+        interfaceName = "";
+    }
+
+    public IpSecTunnelInterfaceResponse(int inStatus, int inResourceId, String inInterfaceName) {
+        status = inStatus;
+        resourceId = inResourceId;
+        interfaceName = inInterfaceName;
+    }
+
+    private IpSecTunnelInterfaceResponse(Parcel in) {
+        status = in.readInt();
+        resourceId = in.readInt();
+        interfaceName = in.readString();
+    }
+
+    public static final Parcelable.Creator<IpSecTunnelInterfaceResponse> CREATOR =
+            new Parcelable.Creator<IpSecTunnelInterfaceResponse>() {
+                public IpSecTunnelInterfaceResponse createFromParcel(Parcel in) {
+                    return new IpSecTunnelInterfaceResponse(in);
+                }
+
+                public IpSecTunnelInterfaceResponse[] newArray(int size) {
+                    return new IpSecTunnelInterfaceResponse[size];
+                }
+            };
+}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 4e474c8..f525b1f 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -50,6 +50,8 @@
     private String mIfaceName;
     private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
     private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
+    private boolean mUsePrivateDns;
+    private String mPrivateDnsServerName;
     private String mDomains;
     private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
     private ProxyInfo mHttpProxy;
@@ -165,6 +167,8 @@
             mIfaceName = source.getInterfaceName();
             for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
             for (InetAddress i : source.getDnsServers()) mDnses.add(i);
+            mUsePrivateDns = source.mUsePrivateDns;
+            mPrivateDnsServerName = source.mPrivateDnsServerName;
             mDomains = source.getDomains();
             for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
             mHttpProxy = (source.getHttpProxy() == null)  ?
@@ -391,6 +395,59 @@
     }
 
     /**
+     * Set whether private DNS is currently in use on this network.
+     *
+     * @param usePrivateDns The private DNS state.
+     * @hide
+     */
+    public void setUsePrivateDns(boolean usePrivateDns) {
+        mUsePrivateDns = usePrivateDns;
+    }
+
+    /**
+     * Returns whether private DNS is currently in use on this network. When
+     * private DNS is in use, applications must not send unencrypted DNS
+     * queries as doing so could reveal private user information. Furthermore,
+     * if private DNS is in use and {@link #getPrivateDnsServerName} is not
+     * {@code null}, DNS queries must be sent to the specified DNS server.
+     *
+     * @return {@code true} if private DNS is in use, {@code false} otherwise.
+     */
+    public boolean isPrivateDnsActive() {
+        return mUsePrivateDns;
+    }
+
+    /**
+     * Set the name of the private DNS server to which private DNS queries
+     * should be sent when in strict mode. This value should be {@code null}
+     * when private DNS is off or in opportunistic mode.
+     *
+     * @param privateDnsServerName The private DNS server name.
+     * @hide
+     */
+    public void setPrivateDnsServerName(@Nullable String privateDnsServerName) {
+        mPrivateDnsServerName = privateDnsServerName;
+    }
+
+    /**
+     * Returns the private DNS server name that is in use. If not {@code null},
+     * private DNS is in strict mode. In this mode, applications should ensure
+     * that all DNS queries are encrypted and sent to this hostname and that
+     * queries are only sent if the hostname's certificate is valid. If
+     * {@code null} and {@link #isPrivateDnsActive} is {@code true}, private
+     * DNS is in opportunistic mode, and applications should ensure that DNS
+     * queries are encrypted and sent to a DNS server returned by
+     * {@link #getDnsServers}. System DNS will handle each of these cases
+     * correctly, but applications implementing their own DNS lookups must make
+     * sure to follow these requirements.
+     *
+     * @return The private DNS server name.
+     */
+    public @Nullable String getPrivateDnsServerName() {
+        return mPrivateDnsServerName;
+    }
+
+    /**
      * Sets the DNS domain search path used on this link.
      *
      * @param domains A {@link String} listing in priority order the comma separated
@@ -622,6 +679,8 @@
         mIfaceName = null;
         mLinkAddresses.clear();
         mDnses.clear();
+        mUsePrivateDns = false;
+        mPrivateDnsServerName = null;
         mDomains = null;
         mRoutes.clear();
         mHttpProxy = null;
@@ -649,6 +708,13 @@
         for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
         dns += "] ";
 
+        String usePrivateDns = "UsePrivateDns: " + mUsePrivateDns + " ";
+
+        String privateDnsServerName = "";
+        if (privateDnsServerName != null) {
+            privateDnsServerName = "PrivateDnsServerName: " + mPrivateDnsServerName + " ";
+        }
+
         String domainName = "Domains: " + mDomains;
 
         String mtu = " MTU: " + mMtu;
@@ -671,8 +737,9 @@
             }
             stacked += "] ";
         }
-        return "{" + ifaceName + linkAddresses + routes + dns + domainName + mtu
-            + tcpBuffSizes + proxy + stacked + "}";
+        return "{" + ifaceName + linkAddresses + routes + dns + usePrivateDns
+            + privateDnsServerName + domainName + mtu + tcpBuffSizes + proxy
+            + stacked + "}";
     }
 
     /**
@@ -896,6 +963,20 @@
     }
 
     /**
+     * Compares this {@code LinkProperties} private DNS settings against the
+     * target.
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isIdenticalPrivateDns(LinkProperties target) {
+        return (isPrivateDnsActive() == target.isPrivateDnsActive()
+                && TextUtils.equals(getPrivateDnsServerName(),
+                target.getPrivateDnsServerName()));
+    }
+
+    /**
      * Compares this {@code LinkProperties} Routes against the target
      *
      * @param target LinkProperties to compare.
@@ -989,14 +1070,15 @@
          * stacked interfaces are not so much a property of the link as a
          * description of connections between links.
          */
-        return isIdenticalInterfaceName(target) &&
-                isIdenticalAddresses(target) &&
-                isIdenticalDnses(target) &&
-                isIdenticalRoutes(target) &&
-                isIdenticalHttpProxy(target) &&
-                isIdenticalStackedLinks(target) &&
-                isIdenticalMtu(target) &&
-                isIdenticalTcpBufferSizes(target);
+        return isIdenticalInterfaceName(target)
+                && isIdenticalAddresses(target)
+                && isIdenticalDnses(target)
+                && isIdenticalPrivateDns(target)
+                && isIdenticalRoutes(target)
+                && isIdenticalHttpProxy(target)
+                && isIdenticalStackedLinks(target)
+                && isIdenticalMtu(target)
+                && isIdenticalTcpBufferSizes(target);
     }
 
     /**
@@ -1091,7 +1173,9 @@
                 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
                 + mStackedLinks.hashCode() * 47)
                 + mMtu * 51
-                + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode());
+                + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
+                + (mUsePrivateDns ? 57 : 0)
+                + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode());
     }
 
     /**
@@ -1108,6 +1192,8 @@
         for(InetAddress d : mDnses) {
             dest.writeByteArray(d.getAddress());
         }
+        dest.writeBoolean(mUsePrivateDns);
+        dest.writeString(mPrivateDnsServerName);
         dest.writeString(mDomains);
         dest.writeInt(mMtu);
         dest.writeString(mTcpBufferSizes);
@@ -1148,6 +1234,8 @@
                         netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray()));
                     } catch (UnknownHostException e) { }
                 }
+                netProp.setUsePrivateDns(in.readBoolean());
+                netProp.setPrivateDnsServerName(in.readString());
                 netProp.setDomains(in.readString());
                 netProp.setMtu(in.readInt());
                 netProp.setTcpBufferSizes(in.readString());
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index d6992aa..287bdc8 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -60,7 +61,7 @@
     })
     public @interface MacAddressType { }
 
-    /** Indicates a MAC address of unknown type. */
+    /** @hide Indicates a MAC address of unknown type. */
     public static final int TYPE_UNKNOWN = 0;
     /** Indicates a MAC address is a unicast address. */
     public static final int TYPE_UNICAST = 1;
@@ -92,7 +93,7 @@
      *
      * @return the int constant representing the MAC address type of this MacAddress.
      */
-    public @MacAddressType int addressType() {
+    public @MacAddressType int getAddressType() {
         if (equals(BROADCAST_ADDRESS)) {
             return TYPE_BROADCAST;
         }
@@ -120,12 +121,12 @@
     /**
      * @return a byte array representation of this MacAddress.
      */
-    public byte[] toByteArray() {
+    public @NonNull byte[] toByteArray() {
         return byteAddrFromLongAddr(mAddr);
     }
 
     @Override
-    public String toString() {
+    public @NonNull String toString() {
         return stringAddrFromLongAddr(mAddr);
     }
 
@@ -133,7 +134,7 @@
      * @return a String representation of the OUI part of this MacAddress made of 3 hexadecimal
      * numbers in [0,ff] joined by ':' characters.
      */
-    public String toOuiString() {
+    public @NonNull String toOuiString() {
         return String.format(
                 "%02x:%02x:%02x", (mAddr >> 40) & 0xff, (mAddr >> 32) & 0xff, (mAddr >> 24) & 0xff);
     }
@@ -197,7 +198,7 @@
         if (!isMacAddress(addr)) {
             return TYPE_UNKNOWN;
         }
-        return MacAddress.fromBytes(addr).addressType();
+        return MacAddress.fromBytes(addr).getAddressType();
     }
 
     /**
@@ -211,7 +212,7 @@
      *
      * @hide
      */
-    public static byte[] byteAddrFromStringAddr(String addr) {
+    public static @NonNull byte[] byteAddrFromStringAddr(String addr) {
         Preconditions.checkNotNull(addr);
         String[] parts = addr.split(":");
         if (parts.length != ETHER_ADDR_LEN) {
@@ -239,7 +240,7 @@
      *
      * @hide
      */
-    public static String stringAddrFromByteAddr(byte[] addr) {
+    public static @NonNull String stringAddrFromByteAddr(byte[] addr) {
         if (!isMacAddress(addr)) {
             return null;
         }
@@ -291,7 +292,7 @@
 
     // Internal conversion function equivalent to stringAddrFromByteAddr(byteAddrFromLongAddr(addr))
     // that avoids the allocation of an intermediary byte[].
-    private static String stringAddrFromLongAddr(long addr) {
+    private static @NonNull String stringAddrFromLongAddr(long addr) {
         return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
                 (addr >> 40) & 0xff,
                 (addr >> 32) & 0xff,
@@ -310,7 +311,7 @@
      * @return the MacAddress corresponding to the given String representation.
      * @throws IllegalArgumentException if the given String is not a valid representation.
      */
-    public static MacAddress fromString(String addr) {
+    public static @NonNull MacAddress fromString(@NonNull String addr) {
         return new MacAddress(longAddrFromStringAddr(addr));
     }
 
@@ -322,7 +323,7 @@
      * @return the MacAddress corresponding to the given byte array representation.
      * @throws IllegalArgumentException if the given byte array is not a valid representation.
      */
-    public static MacAddress fromBytes(byte[] addr) {
+    public static @NonNull MacAddress fromBytes(@NonNull byte[] addr) {
         return new MacAddress(longAddrFromByteAddr(addr));
     }
 
@@ -336,7 +337,7 @@
      *
      * @hide
      */
-    public static MacAddress createRandomUnicastAddress() {
+    public static @NonNull MacAddress createRandomUnicastAddress() {
         return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random());
     }
 
@@ -352,7 +353,7 @@
      *
      * @hide
      */
-    public static MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
+    public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
         long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
         addr = addr | LOCALLY_ASSIGNED_MASK;
         addr = addr & ~MULTICAST_MASK;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 903b602..3683d34 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -356,13 +356,13 @@
         // Multiple Provisioning Domains API recommendations, as made by the
         // IETF mif working group.
         //
-        // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+        // The handleMagic value MUST be kept in sync with the corresponding
         // value in the native/android/net.c NDK implementation.
         if (netId == 0) {
             return 0L;  // make this zero condition obvious for debugging
         }
-        final long HANDLE_MAGIC = 0xfacade;
-        return (((long) netId) << 32) | HANDLE_MAGIC;
+        final long handleMagic = 0xcafed00dL;
+        return (((long) netId) << 32) | handleMagic;
     }
 
     // implement the Parcelable interface
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 2dacf8f..52a2354 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.content.Context;
+import android.net.ConnectivityManager.PacketKeepalive;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -26,7 +27,6 @@
 
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
-import android.net.ConnectivityManager.PacketKeepalive;
 
 import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -101,20 +101,6 @@
     public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
 
     /**
-     * Sent by the NetworkAgent to ConnectivityService to add new UID ranges
-     * to be forced into this Network.  For VPNs only.
-     * obj = UidRange[] to forward
-     */
-    public static final int EVENT_UID_RANGES_ADDED = BASE + 5;
-
-    /**
-     * Sent by the NetworkAgent to ConnectivityService to remove UID ranges
-     * from being forced into this Network.  For VPNs only.
-     * obj = UidRange[] to stop forwarding
-     */
-    public static final int EVENT_UID_RANGES_REMOVED = BASE + 6;
-
-    /**
      * Sent by ConnectivityService to the NetworkAgent to inform the agent of the
      * networks status - whether we could use the network or could not, due to
      * either a bad network configuration (no internet link) or captive portal.
@@ -390,22 +376,6 @@
     }
 
     /**
-     * Called by the VPN code when it wants to add ranges of UIDs to be routed
-     * through the VPN network.
-     */
-    public void addUidRanges(UidRange[] ranges) {
-        queueOrSendMessage(EVENT_UID_RANGES_ADDED, ranges);
-    }
-
-    /**
-     * Called by the VPN code when it wants to remove ranges of UIDs from being routed
-     * through the VPN network.
-     */
-    public void removeUidRanges(UidRange[] ranges) {
-        queueOrSendMessage(EVENT_UID_RANGES_REMOVED, ranges);
-    }
-
-    /**
      * Called by the bearer to indicate this network was manually selected by the user.
      * This should be called before the NetworkInfo is marked CONNECTED so that this
      * Network can be given special treatment at that time. If {@code acceptUnvalidated} is
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 7c897de..e81ed9a 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -20,6 +20,8 @@
 import android.net.ConnectivityManager.NetworkCallback;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.BitUtils;
@@ -28,6 +30,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
+import java.util.Set;
 import java.util.StringJoiner;
 
 /**
@@ -46,6 +49,7 @@
  */
 public final class NetworkCapabilities implements Parcelable {
     private static final String TAG = "NetworkCapabilities";
+    private static final int INVALID_UID = -1;
 
     /**
      * @hide
@@ -63,6 +67,8 @@
             mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
             mNetworkSpecifier = nc.mNetworkSpecifier;
             mSignalStrength = nc.mSignalStrength;
+            mUids = nc.mUids;
+            mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
         }
     }
 
@@ -76,6 +82,8 @@
         mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
         mNetworkSpecifier = null;
         mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
+        mUids = null;
+        mEstablishingVpnAppUid = INVALID_UID;
     }
 
     /**
@@ -107,6 +115,7 @@
             NET_CAPABILITY_CAPTIVE_PORTAL,
             NET_CAPABILITY_NOT_ROAMING,
             NET_CAPABILITY_FOREGROUND,
+            NET_CAPABILITY_NOT_CONGESTED,
     })
     public @interface NetCapability { }
 
@@ -234,8 +243,17 @@
      */
     public static final int NET_CAPABILITY_FOREGROUND = 19;
 
+    /**
+     * Indicates that this network is not congested.
+     * <p>
+     * When a network is congested, the device should defer network traffic that
+     * can be done at a later time without breaking developer contracts.
+     * @hide
+     */
+    public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_FOREGROUND;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_CONGESTED;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -248,7 +266,8 @@
             (1 << NET_CAPABILITY_VALIDATED) |
             (1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
             (1 << NET_CAPABILITY_NOT_ROAMING) |
-            (1 << NET_CAPABILITY_FOREGROUND);
+            (1 << NET_CAPABILITY_FOREGROUND) |
+            (1 << NET_CAPABILITY_NOT_CONGESTED);
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -386,12 +405,9 @@
      * @hide
      */
     public String describeFirstNonRequestableCapability() {
-        if (hasCapability(NET_CAPABILITY_VALIDATED)) return "NET_CAPABILITY_VALIDATED";
-        if (hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return "NET_CAPABILITY_CAPTIVE_PORTAL";
-        if (hasCapability(NET_CAPABILITY_FOREGROUND)) return "NET_CAPABILITY_FOREGROUND";
-        // This cannot happen unless the preceding checks are incomplete.
-        if ((mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES) != 0) {
-            return "unknown non-requestable capabilities " + Long.toHexString(mNetworkCapabilities);
+        final long nonRequestable = (mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES);
+        if (nonRequestable != 0) {
+            return capabilityNameOf(BitUtils.unpackBits(nonRequestable)[0]);
         }
         if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
         if (hasSignalStrength()) return "signalStrength";
@@ -610,6 +626,29 @@
     }
 
     /**
+     * UID of the app that manages this network, or INVALID_UID if none/unknown.
+     *
+     * This field keeps track of the UID of the app that created this network and is in charge
+     * of managing it. In the practice, it is used to store the UID of VPN apps so it is named
+     * accordingly, but it may be renamed if other mechanisms are offered for third party apps
+     * to create networks.
+     *
+     * Because this field is only used in the services side (and to avoid apps being able to
+     * set this to whatever they want), this field is not parcelled and will not be conserved
+     * across the IPC boundary.
+     * @hide
+     */
+    private int mEstablishingVpnAppUid = INVALID_UID;
+
+    /**
+     * Set the UID of the managing app.
+     * @hide
+     */
+    public void setEstablishingVpnAppUid(final int uid) {
+        mEstablishingVpnAppUid = uid;
+    }
+
+    /**
      * Value indicating that link bandwidth is unspecified.
      * @hide
      */
@@ -828,6 +867,174 @@
     }
 
     /**
+     * List of UIDs this network applies to. No restriction if null.
+     * <p>
+     * This is typically (and at this time, only) used by VPN. This network is only available to
+     * the UIDs in this list, and it is their default network. Apps in this list that wish to
+     * bypass the VPN can do so iff the VPN app allows them to or if they are privileged. If this
+     * member is null, then the network is not restricted by app UID. If it's an empty list, then
+     * it means nobody can use it.
+     * As a special exception, the app managing this network (as identified by its UID stored in
+     * mEstablishingVpnAppUid) can always see this network. This is embodied by a special check in
+     * satisfiedByUids. That still does not mean the network necessarily <strong>applies</strong>
+     * to the app that manages it as determined by #appliesToUid.
+     * <p>
+     * Please note that in principle a single app can be associated with multiple UIDs because
+     * each app will have a different UID when it's run as a different (macro-)user. A single
+     * macro user can only have a single active VPN app at any given time however.
+     * <p>
+     * Also please be aware this class does not try to enforce any normalization on this. Callers
+     * can only alter the UIDs by setting them wholesale : this class does not provide any utility
+     * to add or remove individual UIDs or ranges. If callers have any normalization needs on
+     * their own (like requiring sortedness or no overlap) they need to enforce it
+     * themselves. Some of the internal methods also assume this is normalized as in no adjacent
+     * or overlapping ranges are present.
+     *
+     * @hide
+     */
+    private ArraySet<UidRange> mUids = null;
+
+    /**
+     * Convenience method to set the UIDs this network applies to to a single UID.
+     * @hide
+     */
+    public NetworkCapabilities setSingleUid(int uid) {
+        final ArraySet<UidRange> identity = new ArraySet<>(1);
+        identity.add(new UidRange(uid, uid));
+        setUids(identity);
+        return this;
+    }
+
+    /**
+     * Set the list of UIDs this network applies to.
+     * This makes a copy of the set so that callers can't modify it after the call.
+     * @hide
+     */
+    public NetworkCapabilities setUids(Set<UidRange> uids) {
+        if (null == uids) {
+            mUids = null;
+        } else {
+            mUids = new ArraySet<>(uids);
+        }
+        return this;
+    }
+
+    /**
+     * Get the list of UIDs this network applies to.
+     * This returns a copy of the set so that callers can't modify the original object.
+     * @hide
+     */
+    public Set<UidRange> getUids() {
+        return null == mUids ? null : new ArraySet<>(mUids);
+    }
+
+    /**
+     * Test whether this network applies to this UID.
+     * @hide
+     */
+    public boolean appliesToUid(int uid) {
+        if (null == mUids) return true;
+        for (UidRange range : mUids) {
+            if (range.contains(uid)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Tests if the set of UIDs that this network applies to is the same of the passed set of UIDs.
+     * <p>
+     * This test only checks whether equal range objects are in both sets. It will
+     * return false if the ranges are not exactly the same, even if the covered UIDs
+     * are for an equivalent result.
+     * <p>
+     * Note that this method is not very optimized, which is fine as long as it's not used very
+     * often.
+     * <p>
+     * nc is assumed nonnull.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean equalsUids(NetworkCapabilities nc) {
+        Set<UidRange> comparedUids = nc.mUids;
+        if (null == comparedUids) return null == mUids;
+        if (null == mUids) return false;
+        // Make a copy so it can be mutated to check that all ranges in mUids
+        // also are in uids.
+        final Set<UidRange> uids = new ArraySet<>(mUids);
+        for (UidRange range : comparedUids) {
+            if (!uids.contains(range)) {
+                return false;
+            }
+            uids.remove(range);
+        }
+        return uids.isEmpty();
+    }
+
+    /**
+     * Test whether the passed NetworkCapabilities satisfies the UIDs this capabilities require.
+     *
+     * This method is called on the NetworkCapabilities embedded in a request with the
+     * capabilities of an available network. It checks whether all the UIDs from this listen
+     * (representing the UIDs that must have access to the network) are satisfied by the UIDs
+     * in the passed nc (representing the UIDs that this network is available to).
+     * <p>
+     * As a special exception, the UID that created the passed network (as represented by its
+     * mEstablishingVpnAppUid field) always satisfies a NetworkRequest requiring it (of LISTEN
+     * or REQUEST types alike), even if the network does not apply to it. That is so a VPN app
+     * can see its own network when it listens for it.
+     * <p>
+     * nc is assumed nonnull. Else, NPE.
+     * @see #appliesToUid
+     * @hide
+     */
+    public boolean satisfiedByUids(NetworkCapabilities nc) {
+        if (null == nc.mUids) return true; // The network satisfies everything.
+        if (null == mUids) return false; // Not everything allowed but requires everything
+        for (UidRange requiredRange : mUids) {
+            if (requiredRange.contains(nc.mEstablishingVpnAppUid)) return true;
+            if (!nc.appliesToUidRange(requiredRange)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns whether this network applies to the passed ranges.
+     * This assumes that to apply, the passed range has to be entirely contained
+     * within one of the ranges this network applies to. If the ranges are not normalized,
+     * this method may return false even though all required UIDs are covered because no
+     * single range contained them all.
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean appliesToUidRange(UidRange requiredRange) {
+        if (null == mUids) return true;
+        for (UidRange uidRange : mUids) {
+            if (uidRange.containsRange(requiredRange)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Combine the UIDs this network currently applies to with the UIDs the passed
+     * NetworkCapabilities apply to.
+     * nc is assumed nonnull.
+     */
+    private void combineUids(NetworkCapabilities nc) {
+        if (null == nc.mUids || null == mUids) {
+            mUids = null;
+            return;
+        }
+        mUids.addAll(nc.mUids);
+    }
+
+    /**
      * Combine a set of Capabilities to this one.  Useful for coming up with the complete set
      * @hide
      */
@@ -837,6 +1044,7 @@
         combineLinkBandwidths(nc);
         combineSpecifiers(nc);
         combineSignalStrength(nc);
+        combineUids(nc);
     }
 
     /**
@@ -849,12 +1057,13 @@
      * @hide
      */
     private boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc, boolean onlyImmutable) {
-        return (nc != null &&
-                satisfiedByNetCapabilities(nc, onlyImmutable) &&
-                satisfiedByTransportTypes(nc) &&
-                (onlyImmutable || satisfiedByLinkBandwidths(nc)) &&
-                satisfiedBySpecifier(nc) &&
-                (onlyImmutable || satisfiedBySignalStrength(nc)));
+        return (nc != null
+                && satisfiedByNetCapabilities(nc, onlyImmutable)
+                && satisfiedByTransportTypes(nc)
+                && (onlyImmutable || satisfiedByLinkBandwidths(nc))
+                && satisfiedBySpecifier(nc)
+                && (onlyImmutable || satisfiedBySignalStrength(nc))
+                && (onlyImmutable || satisfiedByUids(nc)));
     }
 
     /**
@@ -937,24 +1146,26 @@
     @Override
     public boolean equals(Object obj) {
         if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
-        NetworkCapabilities that = (NetworkCapabilities)obj;
-        return (equalsNetCapabilities(that) &&
-                equalsTransportTypes(that) &&
-                equalsLinkBandwidths(that) &&
-                equalsSignalStrength(that) &&
-                equalsSpecifier(that));
+        NetworkCapabilities that = (NetworkCapabilities) obj;
+        return (equalsNetCapabilities(that)
+                && equalsTransportTypes(that)
+                && equalsLinkBandwidths(that)
+                && equalsSignalStrength(that)
+                && equalsSpecifier(that)
+                && equalsUids(that));
     }
 
     @Override
     public int hashCode() {
-        return ((int)(mNetworkCapabilities & 0xFFFFFFFF) +
-                ((int)(mNetworkCapabilities >> 32) * 3) +
-                ((int)(mTransportTypes & 0xFFFFFFFF) * 5) +
-                ((int)(mTransportTypes >> 32) * 7) +
-                (mLinkUpBandwidthKbps * 11) +
-                (mLinkDownBandwidthKbps * 13) +
-                Objects.hashCode(mNetworkSpecifier) * 17 +
-                (mSignalStrength * 19));
+        return ((int) (mNetworkCapabilities & 0xFFFFFFFF)
+                + ((int) (mNetworkCapabilities >> 32) * 3)
+                + ((int) (mTransportTypes & 0xFFFFFFFF) * 5)
+                + ((int) (mTransportTypes >> 32) * 7)
+                + (mLinkUpBandwidthKbps * 11)
+                + (mLinkDownBandwidthKbps * 13)
+                + Objects.hashCode(mNetworkSpecifier) * 17
+                + (mSignalStrength * 19)
+                + Objects.hashCode(mUids) * 23);
     }
 
     @Override
@@ -969,6 +1180,7 @@
         dest.writeInt(mLinkDownBandwidthKbps);
         dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
         dest.writeInt(mSignalStrength);
+        dest.writeArraySet(mUids);
     }
 
     public static final Creator<NetworkCapabilities> CREATOR =
@@ -983,6 +1195,8 @@
                 netCap.mLinkDownBandwidthKbps = in.readInt();
                 netCap.mNetworkSpecifier = in.readParcelable(null);
                 netCap.mSignalStrength = in.readInt();
+                netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
+                        null /* ClassLoader, null for default */);
                 return netCap;
             }
             @Override
@@ -1015,7 +1229,12 @@
 
         String signalStrength = (hasSignalStrength() ? " SignalStrength: " + mSignalStrength : "");
 
-        return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength + "]";
+        String uids = (null != mUids ? " Uids: <" + mUids + ">" : "");
+
+        String establishingAppUid = " EstablishingAppUid: " + mEstablishingVpnAppUid;
+
+        return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength
+            + uids + establishingAppUid + "]";
     }
 
     /**
@@ -1056,6 +1275,7 @@
             case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL";
             case NET_CAPABILITY_NOT_ROAMING:    return "NOT_ROAMING";
             case NET_CAPABILITY_FOREGROUND:     return "FOREGROUND";
+            case NET_CAPABILITY_NOT_CONGESTED:  return "NOT_CONGESTED";
             default:                            return Integer.toString(capability);
         }
     }
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index d3b3599..ce2de85 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -58,21 +58,24 @@
     final String mNetworkId;
     final boolean mRoaming;
     final boolean mMetered;
+    final boolean mDefaultNetwork;
 
     public NetworkIdentity(
             int type, int subType, String subscriberId, String networkId, boolean roaming,
-            boolean metered) {
+            boolean metered, boolean defaultNetwork) {
         mType = type;
         mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType;
         mSubscriberId = subscriberId;
         mNetworkId = networkId;
         mRoaming = roaming;
         mMetered = metered;
+        mDefaultNetwork = defaultNetwork;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered);
+        return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered,
+                mDefaultNetwork);
     }
 
     @Override
@@ -82,7 +85,8 @@
             return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
                     && Objects.equals(mSubscriberId, ident.mSubscriberId)
                     && Objects.equals(mNetworkId, ident.mNetworkId)
-                    && mMetered == ident.mMetered;
+                    && mMetered == ident.mMetered
+                    && mDefaultNetwork == ident.mDefaultNetwork;
         }
         return false;
     }
@@ -109,6 +113,7 @@
             builder.append(", ROAMING");
         }
         builder.append(", metered=").append(mMetered);
+        builder.append(", defaultNetwork=").append(mDefaultNetwork);
         return builder.append("}").toString();
     }
 
@@ -125,6 +130,7 @@
         proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
         proto.write(NetworkIdentityProto.ROAMING, mRoaming);
         proto.write(NetworkIdentityProto.METERED, mMetered);
+        proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
 
         proto.end(start);
     }
@@ -153,6 +159,10 @@
         return mMetered;
     }
 
+    public boolean getDefaultNetwork() {
+        return mDefaultNetwork;
+    }
+
     /**
      * Scrub given IMSI on production builds.
      */
@@ -183,7 +193,8 @@
      * Build a {@link NetworkIdentity} from the given {@link NetworkState},
      * assuming that any mobile networks are using the current IMSI.
      */
-    public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) {
+    public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state,
+            boolean defaultNetwork) {
         final int type = state.networkInfo.getType();
         final int subType = state.networkInfo.getSubtype();
 
@@ -216,7 +227,8 @@
             }
         }
 
-        return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered);
+        return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+                defaultNetwork);
     }
 
     @Override
@@ -237,6 +249,9 @@
         if (res == 0) {
             res = Boolean.compare(mMetered, another.mMetered);
         }
+        if (res == 0) {
+            res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork);
+        }
         return res;
     }
 }
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 81c49a3..763338f 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -114,6 +114,9 @@
      */
     public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE";
 
+    public static final int OVERRIDE_UNMETERED = 1 << 0;
+    public static final int OVERRIDE_CONGESTED = 1 << 1;
+
     private final Context mContext;
     private INetworkPolicyManager mService;
 
@@ -348,4 +351,13 @@
     public static String resolveNetworkId(String ssid) {
         return WifiInfo.removeDoubleQuotes(ssid);
     }
+
+    /** {@hide} */
+    public static class Listener extends INetworkPolicyListener.Stub {
+        @Override public void onUidRulesChanged(int uid, int uidRules) { }
+        @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { }
+        @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { }
+        @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { }
+        @Override public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue) { }
+    }
 }
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 171adc0..01b2b39 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -82,6 +82,13 @@
     /** {@link #roaming} value where roaming data is accounted. */
     public static final int ROAMING_YES = 1;
 
+    /** {@link #onDefaultNetwork} value to account for all default network states. */
+    public static final int DEFAULT_NETWORK_ALL = -1;
+    /** {@link #onDefaultNetwork} value to account for usage while not the default network. */
+    public static final int DEFAULT_NETWORK_NO = 0;
+    /** {@link #onDefaultNetwork} value to account for usage while the default network. */
+    public static final int DEFAULT_NETWORK_YES = 1;
+
     /** Denotes a request for stats at the interface level. */
     public static final int STATS_PER_IFACE = 0;
     /** Denotes a request for stats at the interface and UID level. */
@@ -102,6 +109,7 @@
     private int[] tag;
     private int[] metered;
     private int[] roaming;
+    private int[] defaultNetwork;
     private long[] rxBytes;
     private long[] rxPackets;
     private long[] txBytes;
@@ -125,6 +133,12 @@
          * getSummary().
          */
         public int roaming;
+        /**
+         * Note that this is only populated w/ the default value when read from /proc or written
+         * to disk. We merge in the correct value when reporting this value to clients of
+         * getSummary().
+         */
+        public int defaultNetwork;
         public long rxBytes;
         public long rxPackets;
         public long txBytes;
@@ -142,18 +156,20 @@
 
         public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
                 long txBytes, long txPackets, long operations) {
-            this(iface, uid, set, tag, METERED_NO, ROAMING_NO, rxBytes, rxPackets, txBytes,
-                    txPackets, operations);
+            this(iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+                    rxBytes, rxPackets, txBytes, txPackets, operations);
         }
 
         public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
-                 long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
+                 int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
+                 long operations) {
             this.iface = iface;
             this.uid = uid;
             this.set = set;
             this.tag = tag;
             this.metered = metered;
             this.roaming = roaming;
+            this.defaultNetwork = defaultNetwork;
             this.rxBytes = rxBytes;
             this.rxPackets = rxPackets;
             this.txBytes = txBytes;
@@ -187,6 +203,7 @@
             builder.append(" tag=").append(tagToString(tag));
             builder.append(" metered=").append(meteredToString(metered));
             builder.append(" roaming=").append(roamingToString(roaming));
+            builder.append(" defaultNetwork=").append(defaultNetworkToString(defaultNetwork));
             builder.append(" rxBytes=").append(rxBytes);
             builder.append(" rxPackets=").append(rxPackets);
             builder.append(" txBytes=").append(txBytes);
@@ -200,7 +217,8 @@
             if (o instanceof Entry) {
                 final Entry e = (Entry) o;
                 return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered
-                        && roaming == e.roaming && rxBytes == e.rxBytes && rxPackets == e.rxPackets
+                        && roaming == e.roaming && defaultNetwork == e.defaultNetwork
+                        && rxBytes == e.rxBytes && rxPackets == e.rxPackets
                         && txBytes == e.txBytes && txPackets == e.txPackets
                         && operations == e.operations && iface.equals(e.iface);
             }
@@ -209,7 +227,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(uid, set, tag, metered, roaming, iface);
+            return Objects.hash(uid, set, tag, metered, roaming, defaultNetwork, iface);
         }
     }
 
@@ -224,6 +242,7 @@
             this.tag = new int[initialSize];
             this.metered = new int[initialSize];
             this.roaming = new int[initialSize];
+            this.defaultNetwork = new int[initialSize];
             this.rxBytes = new long[initialSize];
             this.rxPackets = new long[initialSize];
             this.txBytes = new long[initialSize];
@@ -238,6 +257,7 @@
             this.tag = EmptyArray.INT;
             this.metered = EmptyArray.INT;
             this.roaming = EmptyArray.INT;
+            this.defaultNetwork = EmptyArray.INT;
             this.rxBytes = EmptyArray.LONG;
             this.rxPackets = EmptyArray.LONG;
             this.txBytes = EmptyArray.LONG;
@@ -256,6 +276,7 @@
         tag = parcel.createIntArray();
         metered = parcel.createIntArray();
         roaming = parcel.createIntArray();
+        defaultNetwork = parcel.createIntArray();
         rxBytes = parcel.createLongArray();
         rxPackets = parcel.createLongArray();
         txBytes = parcel.createLongArray();
@@ -274,6 +295,7 @@
         dest.writeIntArray(tag);
         dest.writeIntArray(metered);
         dest.writeIntArray(roaming);
+        dest.writeIntArray(defaultNetwork);
         dest.writeLongArray(rxBytes);
         dest.writeLongArray(rxPackets);
         dest.writeLongArray(txBytes);
@@ -308,10 +330,11 @@
 
     @VisibleForTesting
     public NetworkStats addValues(String iface, int uid, int set, int tag, int metered, int roaming,
-            long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
+            int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
+            long operations) {
         return addValues(new Entry(
-                iface, uid, set, tag, metered, roaming, rxBytes, rxPackets, txBytes, txPackets,
-                operations));
+                iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets,
+                txBytes, txPackets, operations));
     }
 
     /**
@@ -327,6 +350,7 @@
             tag = Arrays.copyOf(tag, newLength);
             metered = Arrays.copyOf(metered, newLength);
             roaming = Arrays.copyOf(roaming, newLength);
+            defaultNetwork = Arrays.copyOf(defaultNetwork, newLength);
             rxBytes = Arrays.copyOf(rxBytes, newLength);
             rxPackets = Arrays.copyOf(rxPackets, newLength);
             txBytes = Arrays.copyOf(txBytes, newLength);
@@ -341,6 +365,7 @@
         tag[size] = entry.tag;
         metered[size] = entry.metered;
         roaming[size] = entry.roaming;
+        defaultNetwork[size] = entry.defaultNetwork;
         rxBytes[size] = entry.rxBytes;
         rxPackets[size] = entry.rxPackets;
         txBytes[size] = entry.txBytes;
@@ -362,6 +387,7 @@
         entry.tag = tag[i];
         entry.metered = metered[i];
         entry.roaming = roaming[i];
+        entry.defaultNetwork = defaultNetwork[i];
         entry.rxBytes = rxBytes[i];
         entry.rxPackets = rxPackets[i];
         entry.txBytes = txBytes[i];
@@ -416,7 +442,7 @@
      */
     public NetworkStats combineValues(Entry entry) {
         final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.metered,
-                entry.roaming);
+                entry.roaming, entry.defaultNetwork);
         if (i == -1) {
             // only create new entry when positive contribution
             addValues(entry);
@@ -444,10 +470,12 @@
     /**
      * Find first stats index that matches the requested parameters.
      */
-    public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming) {
+    public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming,
+            int defaultNetwork) {
         for (int i = 0; i < size; i++) {
             if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
                     && metered == this.metered[i] && roaming == this.roaming[i]
+                    && defaultNetwork == this.defaultNetwork[i]
                     && Objects.equals(iface, this.iface[i])) {
                 return i;
             }
@@ -461,7 +489,7 @@
      */
     @VisibleForTesting
     public int findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming,
-            int hintIndex) {
+            int defaultNetwork, int hintIndex) {
         for (int offset = 0; offset < size; offset++) {
             final int halfOffset = offset / 2;
 
@@ -475,6 +503,7 @@
 
             if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
                     && metered == this.metered[i] && roaming == this.roaming[i]
+                    && defaultNetwork == this.defaultNetwork[i]
                     && Objects.equals(iface, this.iface[i])) {
                 return i;
             }
@@ -489,7 +518,8 @@
      */
     public void spliceOperationsFrom(NetworkStats stats) {
         for (int i = 0; i < size; i++) {
-            final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i]);
+            final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i],
+                    defaultNetwork[i]);
             if (j == -1) {
                 operations[i] = 0;
             } else {
@@ -581,6 +611,7 @@
         entry.tag = TAG_NONE;
         entry.metered = METERED_ALL;
         entry.roaming = ROAMING_ALL;
+        entry.defaultNetwork = DEFAULT_NETWORK_ALL;
         entry.rxBytes = 0;
         entry.rxPackets = 0;
         entry.txBytes = 0;
@@ -677,6 +708,7 @@
             entry.tag = left.tag[i];
             entry.metered = left.metered[i];
             entry.roaming = left.roaming[i];
+            entry.defaultNetwork = left.defaultNetwork[i];
             entry.rxBytes = left.rxBytes[i];
             entry.rxPackets = left.rxPackets[i];
             entry.txBytes = left.txBytes[i];
@@ -685,7 +717,7 @@
 
             // find remote row that matches, and subtract
             final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag,
-                    entry.metered, entry.roaming, i);
+                    entry.metered, entry.roaming, entry.defaultNetwork, i);
             if (j != -1) {
                 // Found matching row, subtract remote value.
                 entry.rxBytes -= right.rxBytes[j];
@@ -725,6 +757,7 @@
         entry.tag = TAG_NONE;
         entry.metered = METERED_ALL;
         entry.roaming = ROAMING_ALL;
+        entry.defaultNetwork = DEFAULT_NETWORK_ALL;
         entry.operations = 0L;
 
         for (int i = 0; i < size; i++) {
@@ -755,6 +788,7 @@
         entry.tag = TAG_NONE;
         entry.metered = METERED_ALL;
         entry.roaming = ROAMING_ALL;
+        entry.defaultNetwork = DEFAULT_NETWORK_ALL;
 
         for (int i = 0; i < size; i++) {
             // skip specific tags, since already counted in TAG_NONE
@@ -802,6 +836,7 @@
             pw.print(" tag="); pw.print(tagToString(tag[i]));
             pw.print(" metered="); pw.print(meteredToString(metered[i]));
             pw.print(" roaming="); pw.print(roamingToString(roaming[i]));
+            pw.print(" defaultNetwork="); pw.print(defaultNetworkToString(defaultNetwork[i]));
             pw.print(" rxBytes="); pw.print(rxBytes[i]);
             pw.print(" rxPackets="); pw.print(rxPackets[i]);
             pw.print(" txBytes="); pw.print(txBytes[i]);
@@ -900,6 +935,22 @@
         }
     }
 
+    /**
+     * Return text description of {@link #defaultNetwork} value.
+     */
+    public static String defaultNetworkToString(int defaultNetwork) {
+        switch (defaultNetwork) {
+            case DEFAULT_NETWORK_ALL:
+                return "ALL";
+            case DEFAULT_NETWORK_NO:
+                return "NO";
+            case DEFAULT_NETWORK_YES:
+                return "YES";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
     @Override
     public String toString() {
         final CharArrayWriter writer = new CharArrayWriter();
@@ -1055,6 +1106,7 @@
                 tmpEntry.set = set[i];
                 tmpEntry.metered = metered[i];
                 tmpEntry.roaming = roaming[i];
+                tmpEntry.defaultNetwork = defaultNetwork[i];
                 combineValues(tmpEntry);
                 if (tag[i] == TAG_NONE) {
                     moved.add(tmpEntry);
@@ -1075,6 +1127,7 @@
         moved.iface = underlyingIface;
         moved.metered = METERED_ALL;
         moved.roaming = ROAMING_ALL;
+        moved.defaultNetwork = DEFAULT_NETWORK_ALL;
         combineValues(moved);
 
         // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
@@ -1085,13 +1138,13 @@
         // roaming data after applying these adjustments, by checking the NetworkIdentity of the
         // underlying iface.
         int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
-                METERED_NO, ROAMING_NO);
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
         if (idxVpnBackground != -1) {
             tunSubtract(idxVpnBackground, this, moved);
         }
 
         int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
-                METERED_NO, ROAMING_NO);
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
         if (idxVpnForeground != -1) {
             tunSubtract(idxVpnForeground, this, moved);
         }
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index b307c5d..8efd39a 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -24,6 +24,15 @@
 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
 import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
+import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkStats.ROAMING_ALL;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.ROAMING_YES;
 import static android.net.wifi.WifiInfo.removeDoubleQuotes;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
@@ -191,16 +200,30 @@
 
     private final String mNetworkId;
 
+    // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
+    private final int mMetered;
+    private final int mRoaming;
+    private final int mDefaultNetwork;
+
     public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
         this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
     }
 
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
             String networkId) {
+        this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL,
+                DEFAULT_NETWORK_ALL);
+    }
+
+    public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
+            String networkId, int metered, int roaming, int defaultNetwork) {
         mMatchRule = matchRule;
         mSubscriberId = subscriberId;
         mMatchSubscriberIds = matchSubscriberIds;
         mNetworkId = networkId;
+        mMetered = metered;
+        mRoaming = roaming;
+        mDefaultNetwork = defaultNetwork;
 
         if (!isKnownMatchRule(matchRule)) {
             Log.e(TAG, "Unknown network template rule " + matchRule
@@ -213,6 +236,9 @@
         mSubscriberId = in.readString();
         mMatchSubscriberIds = in.createStringArray();
         mNetworkId = in.readString();
+        mMetered = in.readInt();
+        mRoaming = in.readInt();
+        mDefaultNetwork = in.readInt();
     }
 
     @Override
@@ -221,6 +247,9 @@
         dest.writeString(mSubscriberId);
         dest.writeStringArray(mMatchSubscriberIds);
         dest.writeString(mNetworkId);
+        dest.writeInt(mMetered);
+        dest.writeInt(mRoaming);
+        dest.writeInt(mDefaultNetwork);
     }
 
     @Override
@@ -243,12 +272,23 @@
         if (mNetworkId != null) {
             builder.append(", networkId=").append(mNetworkId);
         }
+        if (mMetered != METERED_ALL) {
+            builder.append(", metered=").append(NetworkStats.meteredToString(mMetered));
+        }
+        if (mRoaming != ROAMING_ALL) {
+            builder.append(", roaming=").append(NetworkStats.roamingToString(mRoaming));
+        }
+        if (mDefaultNetwork != DEFAULT_NETWORK_ALL) {
+            builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString(
+                    mDefaultNetwork));
+        }
         return builder.toString();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMatchRule, mSubscriberId, mNetworkId);
+        return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
+                mDefaultNetwork);
     }
 
     @Override
@@ -257,7 +297,10 @@
             final NetworkTemplate other = (NetworkTemplate) obj;
             return mMatchRule == other.mMatchRule
                     && Objects.equals(mSubscriberId, other.mSubscriberId)
-                    && Objects.equals(mNetworkId, other.mNetworkId);
+                    && Objects.equals(mNetworkId, other.mNetworkId)
+                    && mMetered == other.mMetered
+                    && mRoaming == other.mRoaming
+                    && mDefaultNetwork == other.mDefaultNetwork;
         }
         return false;
     }
@@ -300,6 +343,10 @@
      * Test if given {@link NetworkIdentity} matches this template.
      */
     public boolean matches(NetworkIdentity ident) {
+        if (!matchesMetered(ident)) return false;
+        if (!matchesRoaming(ident)) return false;
+        if (!matchesDefaultNetwork(ident)) return false;
+
         switch (mMatchRule) {
             case MATCH_MOBILE_ALL:
                 return matchesMobile(ident);
@@ -326,6 +373,24 @@
         }
     }
 
+    private boolean matchesMetered(NetworkIdentity ident) {
+        return (mMetered == METERED_ALL)
+            || (mMetered == METERED_YES && ident.mMetered)
+            || (mMetered == METERED_NO && !ident.mMetered);
+    }
+
+    private boolean matchesRoaming(NetworkIdentity ident) {
+        return (mRoaming == ROAMING_ALL)
+            || (mRoaming == ROAMING_YES && ident.mRoaming)
+            || (mRoaming == ROAMING_NO && !ident.mRoaming);
+    }
+
+    private boolean matchesDefaultNetwork(NetworkIdentity ident) {
+        return (mDefaultNetwork == DEFAULT_NETWORK_ALL)
+            || (mDefaultNetwork == DEFAULT_NETWORK_YES && ident.mDefaultNetwork)
+            || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork);
+    }
+
     public boolean matchesSubscriberId(String subscriberId) {
         return ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
     }
diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java
index 2b662a0..2425bba 100644
--- a/core/java/android/net/metrics/NetworkMetrics.java
+++ b/core/java/android/net/metrics/NetworkMetrics.java
@@ -96,6 +96,13 @@
         }
     }
 
+    /** Accumulate a single netd sock_diag poll result reported by netd. */
+    public void addTcpStatsResult(int sent, int lost, int rttUs, int sentAckDiffMs) {
+        pendingSummary.tcpLossRate.count(lost, sent);
+        pendingSummary.roundTripTimeUs.count(rttUs);
+        pendingSummary.sentAckTimeDiffenceMs.count(sentAckDiffMs);
+    }
+
     /** Represents running sums for dns and connect average error counts and average latencies. */
     public static class Summary {
 
@@ -109,6 +116,13 @@
         public final Metrics connectLatencies = new Metrics();
         // Blocking and non blocking connect error rate measured in percentage points.
         public final Metrics connectErrorRate = new Metrics();
+        // TCP socket packet loss stats collected from Netlink sock_diag.
+        public final Metrics tcpLossRate = new Metrics();
+        // TCP averaged microsecond round-trip-time stats collected from Netlink sock_diag.
+        public final Metrics roundTripTimeUs = new Metrics();
+        // TCP stats collected from Netlink sock_diag that averages millisecond per-socket
+        // differences between last packet sent timestamp and last ack received timestamp.
+        public final Metrics sentAckTimeDiffenceMs = new Metrics();
 
         public Summary(int netId, long transports) {
             this.netId = netId;
@@ -120,6 +134,7 @@
             dnsErrorRate.merge(that.dnsErrorRate);
             connectLatencies.merge(that.connectLatencies);
             connectErrorRate.merge(that.connectErrorRate);
+            tcpLossRate.merge(that.tcpLossRate);
         }
 
         @Override
@@ -135,6 +150,10 @@
             j.add(String.format("connect avg=%dms max=%dms err=%.1f%% tot=%d",
                     (int) connectLatencies.average(), (int) connectLatencies.max,
                     100 * connectErrorRate.average(), connectErrorRate.count));
+            j.add(String.format("tcp avg_loss=%.1f%% total_sent=%d total_lost=%d",
+                    100 * tcpLossRate.average(), tcpLossRate.count, (int) tcpLossRate.sum));
+            j.add(String.format("tcp rtt=%dms", (int) (roundTripTimeUs.average() / 1000)));
+            j.add(String.format("tcp sent-ack_diff=%dms", (int) sentAckTimeDiffenceMs.average()));
             return j.toString();
         }
     }
@@ -152,7 +171,11 @@
         }
 
         void count(double value) {
-            count++;
+            count(value, 1);
+        }
+
+        void count(double value, int subcount) {
+            count += subcount;
             sum += value;
             max = Math.max(max, value);
         }
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
index 7277ba3..bb36536 100644
--- a/core/java/android/net/metrics/WakeupStats.java
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -80,7 +80,7 @@
                 break;
         }
 
-        switch (ev.dstHwAddr.addressType()) {
+        switch (ev.dstHwAddr.getAddressType()) {
             case MacAddress.TYPE_UNICAST:
                 l2UnicastCount++;
                 break;
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 2acf36f..c3894c4 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1136,7 +1136,7 @@
             int intervalUs) {
         VMDebug.startMethodTracing(fixTracePath(tracePath), bufferSize, 0, true, intervalUs);
     }
-    
+
     /**
      * Formats name of trace log file for method tracing.
      */
@@ -2352,22 +2352,28 @@
     }
 
     /**
-     * Attach a library as a jvmti agent to the current runtime.
+     * Attach a library as a jvmti agent to the current runtime, with the given classloader
+     * determining the library search path.
+     * <p>
+     * Note: agents may only be attached to debuggable apps. Otherwise, this function will
+     * throw a SecurityException.
      *
-     * @param library library containing the agent
-     * @param options options passed to the agent
+     * @param library the library containing the agent.
+     * @param options the options passed to the agent.
+     * @param classLoader the classloader determining the library search path.
      *
-     * @throws IOException If the agent could not be attached
+     * @throws IOException if the agent could not be attached.
+     * @throws SecurityException if the app is not debuggable.
      */
-    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options)
-            throws IOException {
+    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
+            @Nullable ClassLoader classLoader) throws IOException {
         Preconditions.checkNotNull(library);
         Preconditions.checkArgument(!library.contains("="));
 
         if (options == null) {
-            VMDebug.attachAgent(library);
+            VMDebug.attachAgent(library, classLoader);
         } else {
-            VMDebug.attachAgent(library + "=" + options);
+            VMDebug.attachAgent(library + "=" + options, classLoader);
         }
     }
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 0874d93..5e6f5f5 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -143,7 +143,7 @@
      * Defines the UID/GID for the WebView zygote process.
      * @hide
      */
-    public static final int WEBVIEW_ZYGOTE_UID = 1051;
+    public static final int WEBVIEW_ZYGOTE_UID = 1053;
 
     /**
      * Defines the UID used for resource tracking for OTA updates.
@@ -151,6 +151,12 @@
      */
     public static final int OTA_UPDATE_UID = 1061;
 
+    /**
+     * Defines the UID/GID for the Secure Element service process.
+     * @hide
+     */
+    public static final int SE_UID = 1068;
+
     /** {@hide} */
     public static final int NOBODY_UID = 9999;
 
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 340f3fb..12a495b 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -76,8 +76,8 @@
     /**
      * @return a list of VNDK snapshots supported by the framework, as
      * specified in framework manifest. For example,
-     * [("25.0.5", ["libjpeg.so", "libbase.so"]),
-     *  ("25.1.3", ["libjpeg.so", "libbase.so"])]
+     * [("27", ["libjpeg.so", "libbase.so"]),
+     *  ("28", ["libjpeg.so", "libbase.so"])]
      */
     public static native Map<String, String[]> getVndkSnapshots();
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ed68276..7ad4373 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8646,13 +8646,52 @@
         public static final String RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS =
                 "recommended_network_evaluator_cache_expiry_ms";
 
-       /**
+        /**
         * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
         * connectivity.
         * @hide
         */
-       public static final String BLE_SCAN_ALWAYS_AVAILABLE =
-               "ble_scan_always_enabled";
+        public static final String BLE_SCAN_ALWAYS_AVAILABLE = "ble_scan_always_enabled";
+
+        /**
+         * The length in milliseconds of a BLE scan window in a low-power scan mode.
+         * @hide
+         */
+        public static final String BLE_SCAN_LOW_POWER_WINDOW_MS = "ble_scan_low_power_window_ms";
+
+        /**
+         * The length in milliseconds of a BLE scan window in a balanced scan mode.
+         * @hide
+         */
+        public static final String BLE_SCAN_BALANCED_WINDOW_MS = "ble_scan_balanced_window_ms";
+
+        /**
+         * The length in milliseconds of a BLE scan window in a low-latency scan mode.
+         * @hide
+         */
+        public static final String BLE_SCAN_LOW_LATENCY_WINDOW_MS =
+                "ble_scan_low_latency_window_ms";
+
+        /**
+         * The length in milliseconds of a BLE scan interval in a low-power scan mode.
+         * @hide
+         */
+        public static final String BLE_SCAN_LOW_POWER_INTERVAL_MS =
+                "ble_scan_low_power_interval_ms";
+
+        /**
+         * The length in milliseconds of a BLE scan interval in a balanced scan mode.
+         * @hide
+         */
+        public static final String BLE_SCAN_BALANCED_INTERVAL_MS =
+                "ble_scan_balanced_interval_ms";
+
+        /**
+         * The length in milliseconds of a BLE scan interval in a low-latency scan mode.
+         * @hide
+         */
+        public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
+                "ble_scan_low_latency_interval_ms";
 
        /**
         * Used to save the Wifi_ON state prior to tethering.
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
new file mode 100644
index 0000000..f0b9fa2d
--- /dev/null
+++ b/core/java/android/se/omapi/Channel.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Instances of this class represent an ISO/IEC 7816-4 channel opened to a
+ * Secure Element. It can be either a logical channel or the basic channel. They
+ * can be used to send APDUs to the secure element. Channels are opened by
+ * calling the Session.openBasicChannel(byte[]) or
+ * Session.openLogicalChannel(byte[]) methods.
+ *
+ * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
+ */
+public class Channel {
+
+    private static final String TAG = "OMAPI.Channel";
+    private Session mSession;
+    private final ISecureElementChannel mChannel;
+    private final SEService mService;
+    private final Object mLock = new Object();
+
+    Channel(SEService service, Session session, ISecureElementChannel channel) {
+        if (service == null || session == null || channel == null) {
+            throw new IllegalArgumentException("Parameters cannot be null");
+        }
+        mService = service;
+        mSession = session;
+        mChannel = channel;
+    }
+
+    /**
+     * Closes this channel to the Secure Element. If the method is called when
+     * the channel is already closed, this method will be ignored. The close()
+     * method shall wait for completion of any pending transmit(byte[] command)
+     * before closing the channel.
+     */
+    public void close() {
+        if (!isClosed()) {
+            synchronized (mLock) {
+                try {
+                    mChannel.close();
+                } catch (Exception e) {
+                    Log.e(TAG, "Error closing channel", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Tells if this channel is closed.
+     *
+     * @return <code>true</code> if the channel is closed or in case of an error.
+     *         <code>false</code> otherwise.
+     */
+    public boolean isClosed() {
+        if (!mService.isConnected()) {
+            Log.e(TAG, "service not connected to system");
+            return true;
+        }
+        try {
+            return mChannel.isClosed();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in isClosed()");
+            return true;
+        }
+    }
+
+    /**
+     * Returns a boolean telling if this channel is the basic channel.
+     *
+     * @return <code>true</code> if this channel is a basic channel. <code>false</code> if
+     *         this channel is a logical channel.
+     */
+    public boolean isBasicChannel() {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        try {
+            return mChannel.isBasicChannel();
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+
+    /**
+     * Transmit an APDU command (as per ISO/IEC 7816-4) to the Secure Element. The
+     * underlying layers generate as many TPDUs as necessary to transport this APDU. The
+     * API shall ensure that all available data returned from Secure Element, including
+     * concatenated responses, are retrieved and made available to the calling application. If a
+     * warning status code is received the API wont check for further response data but will
+     * return all data received so far and the warning status code.<br>
+     * The transport part is invisible from the application. The generated response is the
+     * response of the APDU which means that all protocols related responses are handled
+     * inside the API or the underlying implementation.<br>
+     * The transmit method shall support extended length APDU commands independently of
+     * the coding within the ATR.<br>
+     * For status word '61 XX' the API or underlying implementation shall issue a GET
+     * RESPONSE command as specified by ISO 7816-4 standard with LE=XX; for the status
+     * word '6C XX', the API or underlying implementation shall reissue the input command
+     * with LE=XX. For other status words, the API (or underlying implementation) shall return
+     * the complete response including data and status word to the device application. The API
+     * (or underlying implementation) shall not handle internally the received status words. The
+     * channel shall not be closed even if the Secure Element answered with an error code.
+     * The system ensures the synchronization between all the concurrent calls to this method,
+     * and that only one APDU will be sent at a time, irrespective of the number of TPDUs that
+     * might be required to transport it to the SE. The entire APDU communication to this SE is
+     * locked to the APDU.<br>
+     * The channel information in the class byte in the APDU will be ignored. The system will
+     * add any required information to ensure the APDU is transported on this channel.
+     * The only restrictions on the set of commands that can be sent is defined below, the API
+     * implementation shall be able to send all other commands: <br>
+     * <ul>
+     * <li>MANAGE_CHANNEL commands are not allowed.</li>
+     * <li>SELECT by DF Name (p1=04) are not allowed.</li>
+     * <li>CLA bytes with channel numbers are de-masked.</li>
+     * </ul>
+     *
+     * @param command the APDU command to be transmitted, as a byte array.
+     *
+     * @return the response received, as a byte array. The returned byte array contains the data
+     * bytes in the following order:
+     * [&lt;first data byte&gt;, ..., &lt;last data byte&gt;, &lt;sw1&gt;, &lt;sw2&gt;]
+     *
+     * @throws IOException if there is a communication problem to the reader or the Secure Element.
+     * @throws IllegalStateException if the channel is used after being closed.
+     * @throws IllegalArgumentException if the command byte array is less than 4 bytes long.
+     * @throws IllegalArgumentException if Lc byte is inconsistent with length of the byte array.
+     * @throws IllegalArgumentException if CLA byte is invalid according to [2] (0xff).
+     * @throws IllegalArgumentException if INS byte is invalid according to [2] (0x6x or 0x9x).
+     * @throws SecurityException if the command is filtered by the security policy.
+     * @throws NullPointerException if command is NULL.
+     */
+    public @NonNull byte[] transmit(byte[] command) throws IOException {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        synchronized (mLock) {
+            try {
+                byte[] response = mChannel.transmit(command);
+                if (response == null) {
+                    throw new IOException("Error in communicating with Secure Element");
+                }
+                return response;
+            } catch (RemoteException e) {
+                throw new IOException(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Get the session that has opened this channel.
+     *
+     * @return the session object this channel is bound to.
+     */
+    public @NonNull Session getSession() {
+        return mSession;
+    }
+
+    /**
+     * Returns the data as received from the application select command inclusively the status word
+     * received at applet selection.
+     * The returned byte array contains the data bytes in the following order:
+     * [&lt;first data byte&gt;, ..., &lt;last data byte&gt;, &lt;sw1&gt;, &lt;sw2&gt;]
+     * @return The data as returned by the application select command inclusively the status word.
+     * Only the status word if the application select command has no returned data.
+     * Returns null if an application select command has not been performed or the selection
+     * response can not be retrieved by the reader implementation.
+     */
+    public @Nullable byte[] getSelectResponse() {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+
+        byte[] response;
+        try {
+            response = mChannel.getSelectResponse();
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+
+        if (response != null && response.length == 0) {
+            response = null;
+        }
+        return response;
+    }
+
+    /**
+     * Performs a selection of the next Applet on this channel that matches to the partial AID
+     * specified in the openBasicChannel(byte[] aid) or openLogicalChannel(byte[] aid) method.
+     * This mechanism can be used by a device application to iterate through all Applets
+     * matching to the same partial AID.
+     * If selectNext() returns true a new Applet was successfully selected on this channel.
+     * If no further Applet exists with matches to the partial AID this method returns false
+     * and the already selected Applet stays selected. <br>
+     *
+     * Since the API cannot distinguish between a partial and full AID the API shall rely on the
+     * response of the Secure Element for the return value of this method. <br>
+     * The implementation of the underlying SELECT command within this method shall use
+     * the same values as the corresponding openBasicChannel(byte[] aid) or
+     * openLogicalChannel(byte[] aid) command with the option: <br>
+     * P2='02' (Next occurrence) <br>
+     * The select response stored in the Channel object shall be updated with the APDU
+     * response of the SELECT command.
+
+     * @return <code>true</code> if new Applet was selected on this channel.
+               <code>false</code> he already selected Applet stays selected on this channel.
+     *
+     * @throws IOException if there is a communication problem to the reader or the Secure Element.
+     * @throws IllegalStateException if the channel is used after being closed.
+     * @throws UnsupportedOperationException if this operation is not supported by the card.
+     */
+    public boolean selectNext() throws IOException {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        try {
+            synchronized (mLock) {
+                return mChannel.selectNext();
+            }
+        } catch (RemoteException e) {
+            throw new IOException(e.getMessage());
+        }
+    }
+}
diff --git a/core/java/android/se/omapi/ISecureElementChannel.aidl b/core/java/android/se/omapi/ISecureElementChannel.aidl
new file mode 100644
index 0000000..4ae57ab
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementChannel.aidl
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017, 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementSession;
+
+/** @hide */
+interface ISecureElementChannel {
+
+    /**
+     * Closes the specified connection and frees internal resources.
+     * A logical channel will be closed.
+     */
+    void close();
+
+    /**
+     * Tells if this channel is closed.
+     *
+     * @return <code>true</code> if the channel is closed,
+     *         <code>false</code> otherwise.
+     */
+    boolean isClosed();
+
+    /**
+     * Returns a boolean telling if this channel is the basic channel.
+     *
+     * @return <code>true</code> if this channel is a basic channel.
+     *         <code>false</code> if this channel is a logical channel.
+     */
+    boolean isBasicChannel();
+
+     /**
+     * Returns the data as received from the application select command
+     * inclusively the status word. The returned byte array contains the data
+     * bytes in the following order:
+     * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+     */
+    byte[] getSelectResponse();
+
+    /**
+     * Transmits the specified command APDU and returns the response APDU.
+     * MANAGE channel commands are not supported.
+     * Selection of applets is not supported in logical channels.
+     */
+    byte[] transmit(in byte[] command);
+
+    /**
+     * Performs a selection of the next Applet on this channel that matches to
+     * the partial AID specified in the openBasicChannel(byte[] aid) or
+     * openLogicalChannel(byte[] aid) method. This mechanism can be used by a
+     * device application to iterate through all Applets matching to the same
+     * partial AID.
+     * If selectNext() returns true a new Applet was successfully selected on
+     * this channel.
+     * If no further Applet exists with matches to the partial AID this method
+     * returns false and the already selected Applet stays selected.
+     *
+     * @return <code>true</code> if new Applet was successfully selected.
+     *         <code>false</code> if no further Applet exists which matches the
+     *         partial AID.
+     */
+    boolean selectNext();
+}
diff --git a/core/java/android/os/Seccomp.java b/core/java/android/se/omapi/ISecureElementListener.aidl
similarity index 62%
copy from core/java/android/os/Seccomp.java
copy to core/java/android/se/omapi/ISecureElementListener.aidl
index f14e93f..3a99d63 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/java/android/se/omapi/ISecureElementListener.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2017, 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.
@@ -13,12 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
 
-package android.os;
+package android.se.omapi;
 
 /**
- * @hide
+ * Interface to receive call-backs when the service is connected.
  */
-public final class Seccomp {
-    public static final native void setPolicy();
+interface ISecureElementListener {
+  /**
+   * Called by the framework when the service is connected.
+   */
+  void serviceConnected();
 }
diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/core/java/android/se/omapi/ISecureElementReader.aidl
new file mode 100644
index 0000000..a312c44
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementReader.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017, 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementSession;
+
+/** @hide */
+interface ISecureElementReader {
+
+    /**
+     * Returns true if a card is present in the specified reader.
+     * Returns false if a card is not present in the specified reader.
+     */
+    boolean isSecureElementPresent();
+
+    /**
+     * Connects to a secure element in this reader. <br>
+     * This method prepares (initialises) the Secure Element for communication
+     * before the Session object is returned (e.g. powers the Secure Element by
+     * ICC ON if its not already on). There might be multiple sessions opened at
+     * the same time on the same reader. The system ensures the interleaving of
+     * APDUs between the respective sessions.
+     *
+     * @return a Session object to be used to create Channels.
+     */
+    ISecureElementSession openSession();
+
+    /**
+     * Close all the sessions opened on this reader. All the channels opened by
+     * all these sessions will be closed.
+     */
+    void closeSessions();
+
+}
diff --git a/core/java/android/se/omapi/ISecureElementService.aidl b/core/java/android/se/omapi/ISecureElementService.aidl
new file mode 100644
index 0000000..4fa799e
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementService.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017, 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementReader;
+
+/**
+ * SecureElement service interface.
+ * @hide
+ */
+interface ISecureElementService {
+
+    /**
+     * Returns the friendly names of available Secure Element readers.
+     */
+    String[] getReaders();
+
+    /**
+     * Returns SecureElement Service reader object to the given name.
+     */
+    ISecureElementReader getReader(String reader);
+
+    /**
+     * Checks if the application defined by the package name is allowed to
+     * receive NFC transaction events for the defined AID.
+     */
+    boolean[] isNFCEventAllowed(String reader, in byte[] aid,
+            in String[] packageNames);
+
+}
diff --git a/core/java/android/se/omapi/ISecureElementSession.aidl b/core/java/android/se/omapi/ISecureElementSession.aidl
new file mode 100644
index 0000000..8ea599f
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementSession.aidl
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017, 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementChannel;
+import android.se.omapi.ISecureElementReader;
+import android.se.omapi.ISecureElementListener;
+
+/** @hide */
+interface ISecureElementSession {
+
+    /**
+     * Returns the ATR of the connected card or null if the ATR is not available
+     */
+    byte[] getAtr();
+
+    /**
+     * Close the connection with the Secure Element. This will close any
+     * channels opened by this application with this Secure Element.
+     */
+    void close();
+
+    /**
+     * Close any channel opened on this session.
+     */
+    void closeChannels();
+
+
+    /**
+     * Tells if this session is closed.
+     *
+     * @return <code>true</code> if the session is closed, false otherwise.
+     */
+    boolean isClosed();
+
+    /**
+     * Opens a connection using the basic channel of the card in the
+     * specified reader and returns a channel handle. Selects the specified
+     * applet if aid != null.
+     * Logical channels cannot be opened with this connection.
+     * Use interface method openLogicalChannel() to open a logical channel.
+     */
+    ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2,
+            ISecureElementListener listener);
+
+    /**
+     * Opens a connection using the next free logical channel of the card in the
+     * specified reader. Selects the specified applet.
+     * Selection of other applets with this connection is not supported.
+     */
+    ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2,
+            ISecureElementListener listener);
+}
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
new file mode 100644
index 0000000..9f15739
--- /dev/null
+++ b/core/java/android/se/omapi/Reader.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Instances of this class represent Secure Element Readers supported to this
+ * device. These Readers can be physical devices or virtual devices. They can be
+ * removable or not. They can contain Secure Element that can or cannot be
+ * removed.
+ *
+ * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
+ */
+public class Reader {
+
+    private static final String TAG = "OMAPI.Reader";
+    private final String mName;
+    private final SEService mService;
+    private ISecureElementReader mReader;
+    private final Object mLock = new Object();
+
+
+    Reader(SEService service, String name, ISecureElementReader reader) throws
+            IOException {
+        if (reader == null || service == null || name == null) {
+            throw new IllegalArgumentException("Parameters cannot be null");
+        }
+        mName = name;
+        mService = service;
+        mReader = reader;
+    }
+
+    /**
+     * Return the name of this reader.
+     * <ul>
+     * <li>If this reader is a SIM reader, then its name must be "SIM[Slot]".</li>
+     * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
+     * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
+     * </ul>
+     * Slot is a decimal number without leading zeros. The Numbering must start with 1
+     * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
+     * The slot number “1” for a reader is optional
+     * (SIM and SIM1 are both valid for the first SIM-reader,
+     * but if there are two readers then the second reader must be named SIM2).
+     * This applies also for other SD or SE readers.
+     *
+     * @return the reader name, as a String.
+     */
+    public @NonNull String getName() {
+        return mName;
+    }
+
+    /**
+     * Connects to a Secure Element in this reader. <br>
+     * This method prepares (initialises) the Secure Element for communication
+     * before the Session object is returned (e.g. powers the Secure Element by
+     * ICC ON if its not already on). There might be multiple sessions opened at
+     * the same time on the same reader. The system ensures the interleaving of
+     * APDUs between the respective sessions.
+     *
+     * @throws IOException if something went wrong with the communicating to the
+     *             Secure Element or the reader.
+     * @return a Session object to be used to create Channels.
+     */
+    public @NonNull Session openSession() throws IOException {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service is not connected");
+        }
+
+        synchronized (mLock) {
+            ISecureElementSession session;
+            try {
+                session = mReader.openSession();
+            } catch (RemoteException e) {
+                throw new IOException(e.getMessage());
+            }
+            if (session == null) {
+                throw new IOException("service session is null.");
+            }
+            return new Session(mService, session, this);
+        }
+    }
+
+    /**
+     * Check if a Secure Element is present in this reader.
+     *
+     * @throws IllegalStateException if the service is not connected
+     * @return <code>true</code> if the SE is present, <code>false</code> otherwise.
+     */
+    public boolean isSecureElementPresent() {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service is not connected");
+        }
+
+        try {
+            return mReader.isSecureElementPresent();
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Error in isSecureElementPresent()");
+        }
+    }
+
+    /**
+     * Return the Secure Element service this reader is bound to.
+     *
+     * @return the SEService object.
+     */
+    public @NonNull SEService getSEService() {
+        return mService;
+    }
+
+    /**
+     * Close all the sessions opened on this reader.
+     * All the channels opened by all these sessions will be closed.
+     */
+    public void closeSessions() {
+        if (!mService.isConnected()) {
+            Log.e(TAG, "service is not connected");
+            return;
+        }
+        synchronized (mLock) {
+            try {
+                mReader.closeSessions();
+            } catch (RemoteException ignore) { }
+        }
+    }
+}
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
new file mode 100644
index 0000000..1e37277d
--- /dev/null
+++ b/core/java/android/se/omapi/SEService.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * The SEService realises the communication to available Secure Elements on the
+ * device. This is the entry point of this API. It is used to connect to the
+ * infrastructure and get access to a list of Secure Element Readers.
+ *
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
+ */
+public class SEService {
+
+    private static final String TAG = "OMAPI.SEService";
+
+    private final Object mLock = new Object();
+
+    /** The client context (e.g. activity). */
+    private final Context mContext;
+
+    /** The backend system. */
+    private volatile ISecureElementService mSecureElementService;
+
+    /**
+     * Class for interacting with the main interface of the backend.
+     */
+    private ServiceConnection mConnection;
+
+    /**
+     * Collection of available readers
+     */
+    private final HashMap<String, Reader> mReaders = new HashMap<String, Reader>();
+
+    /**
+     * Listener object that allows the notification of the caller if this
+     * SEService could be bound to the backend.
+     */
+    private ISecureElementListener mSEListener;
+
+    /**
+     * Establishes a new connection that can be used to connect to all the
+     * Secure Elements available in the system. The connection process can be
+     * quite long, so it happens in an asynchronous way. It is usable only if
+     * the specified listener is called or if isConnected() returns
+     * <code>true</code>. <br>
+     * The call-back object passed as a parameter will have its
+     * serviceConnected() method called when the connection actually happen.
+     *
+     * @param context
+     *            the context of the calling application. Cannot be
+     *            <code>null</code>.
+     * @param listener
+     *            a ISecureElementListener object. Can be <code>null</code>.
+     */
+    public SEService(Context context, ISecureElementListener listener) {
+
+        if (context == null) {
+            throw new NullPointerException("context must not be null");
+        }
+
+        mContext = context;
+        mSEListener = listener;
+
+        mConnection = new ServiceConnection() {
+
+            public synchronized void onServiceConnected(
+                    ComponentName className, IBinder service) {
+
+                mSecureElementService = ISecureElementService.Stub.asInterface(service);
+                if (mSEListener != null) {
+                    try {
+                        mSEListener.serviceConnected();
+                    } catch (RemoteException ignore) { }
+                }
+                Log.i(TAG, "Service onServiceConnected");
+            }
+
+            public void onServiceDisconnected(ComponentName className) {
+                mSecureElementService = null;
+                Log.i(TAG, "Service onServiceDisconnected");
+            }
+        };
+
+        Intent intent = new Intent(ISecureElementService.class.getName());
+        intent.setClassName("com.android.se",
+                            "com.android.se.SecureElementService");
+        boolean bindingSuccessful =
+                mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+        if (bindingSuccessful) {
+            Log.i(TAG, "bindService successful");
+        }
+    }
+
+    /**
+     * Tells whether or not the service is connected.
+     *
+     * @return <code>true</code> if the service is connected.
+     */
+    public boolean isConnected() {
+        return mSecureElementService != null;
+    }
+
+    /**
+     * Returns the list of available Secure Element readers.
+     * There must be no duplicated objects in the returned list.
+     * All available readers shall be listed even if no card is inserted.
+     *
+     * @return The readers list, as an array of Readers. If there are no
+     * readers the returned array is of length 0.
+     */
+    public @NonNull Reader[] getReaders() {
+        if (mSecureElementService == null) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        String[] readerNames;
+        try {
+            readerNames = mSecureElementService.getReaders();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        Reader[] readers = new Reader[readerNames.length];
+        int i = 0;
+        for (String readerName : readerNames) {
+            if (mReaders.get(readerName) == null) {
+                try {
+                    mReaders.put(readerName, new Reader(this, readerName,
+                            getReader(readerName)));
+                    readers[i++] = mReaders.get(readerName);
+                } catch (Exception e) {
+                    Log.e(TAG, "Error adding Reader: " + readerName, e);
+                }
+            } else {
+                readers[i++] = mReaders.get(readerName);
+            }
+        }
+        return readers;
+    }
+
+    /**
+     * Releases all Secure Elements resources allocated by this SEService
+     * (including any binding to an underlying service).
+     * As a result isConnected() will return false after shutdown() was called.
+     * After this method call, the SEService object is not connected.
+     * It is recommended to call this method in the termination method of the calling application
+     * (or part of this application) which is bound to this SEService.
+     */
+    public void shutdown() {
+        synchronized (mLock) {
+            if (mSecureElementService != null) {
+                for (Reader reader : mReaders.values()) {
+                    try {
+                        reader.closeSessions();
+                    } catch (Exception ignore) { }
+                }
+            }
+            try {
+                mContext.unbindService(mConnection);
+            } catch (IllegalArgumentException e) {
+                // Do nothing and fail silently since an error here indicates
+                // that binding never succeeded in the first place.
+            }
+            mSecureElementService = null;
+        }
+    }
+
+    /**
+     * Returns the version of the OpenMobile API specification this
+     * implementation is based on.
+     *
+     * @return String containing the OpenMobile API version (e.g. "3.0").
+     */
+    public String getVersion() {
+        return "3.2";
+    }
+
+    @NonNull ISecureElementListener getListener() {
+        return mSEListener;
+    }
+
+    /**
+     * Obtain a Reader instance from the SecureElementService
+     */
+    private @NonNull ISecureElementReader getReader(String name) {
+        try {
+            return mSecureElementService.getReader(name);
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+}
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
new file mode 100644
index 0000000..bb2a032
--- /dev/null
+++ b/core/java/android/se/omapi/Session.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+/*
+ * Copyright (c) 2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.NoSuchElementException;
+
+/**
+ * Instances of this class represent a connection session to one of the Secure
+ * Elements available on the device. These objects can be used to get a
+ * communication channel with an Applet in the Secure Element.
+ * This channel can be the basic channel or a logical channel.
+ *
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
+ */
+public class Session {
+
+    private final Object mLock = new Object();
+    private final SEService mService;
+    private final Reader mReader;
+    private final ISecureElementSession mSession;
+    private static final String TAG = "OMAPI.Session";
+
+    Session(SEService service, ISecureElementSession session, Reader reader) {
+        if (service == null || reader == null || session == null) {
+            throw new IllegalArgumentException("Parameters cannot be null");
+        }
+        mService = service;
+        mReader = reader;
+        mSession = session;
+    }
+
+    /**
+     * Get the reader that provides this session.
+     *
+     * @return The Reader object.
+     */
+    public @NonNull Reader getReader() {
+        return mReader;
+    }
+
+    /**
+     * Get the Answer to Reset of this Secure Element. <br>
+     * The returned byte array can be null if the ATR for this Secure Element is
+     * not available.
+     *
+     * @throws IllegalStateException if there was an error connecting to SE or
+     *                               if the service was not connected.
+     * @return the ATR as a byte array or null.
+     */
+    public @Nullable byte[] getATR() {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        try {
+            return mSession.getAtr();
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+
+    /**
+     * Close the connection with the Secure Element. This will close any
+     * channels opened by this application with this Secure Element.
+     */
+    public void close() {
+        if (!mService.isConnected()) {
+            Log.e(TAG, "service not connected to system");
+            return;
+        }
+        synchronized (mLock) {
+            try {
+                mSession.close();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error closing session", e);
+            }
+        }
+    }
+
+    /**
+     * Tells if this session is closed.
+     *
+     * @return <code>true</code> if the session is closed, false otherwise.
+     */
+    public boolean isClosed() {
+        try {
+            return mSession.isClosed();
+        } catch (RemoteException e) {
+            // If there was an error here, then the session is considered close
+            return true;
+        }
+    }
+
+    /**
+     * Close any channel opened on this session.
+     */
+    public void closeChannels() {
+        if (!mService.isConnected()) {
+            Log.e(TAG, "service not connected to system");
+            return;
+        }
+
+        synchronized (mLock) {
+            try {
+                mSession.closeChannels();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error closing channels", e);
+            }
+        }
+    }
+
+    /**
+     * Get an access to the basic channel, as defined in the ISO/IEC 7816-4 specification (the
+     * one that has number 0). The obtained object is an instance of the Channel class.
+     * If the AID is null, it means no Applet is to be selected on this channel and the default
+     * Applet is used. If the AID is defined then the corresponding Applet is selected.
+     * Once this channel has been opened by a device application, it is considered as "locked"
+     * by this device application, and other calls to this method will return null, until the
+     * channel is closed. Some Secure Elements (like the UICC) might always keep the basic channel
+     * locked (i.e. return null to applications), to prevent access to the basic channel, while
+     * some other might return a channel object implementing some kind of filtering on the
+     * commands, restricting the set of accepted command to a smaller set.
+     * It is recommended for the UICC to reject the opening of the basic channel to a specific
+     * applet, by always answering null to such a request.
+     * For other Secure Elements, the recommendation is to accept opening the basic channel
+     * on the default applet until another applet is selected on the basic channel. As there is no
+     * other way than a reset to select again the default applet, the implementation of the
+     * transport API should guarantee that the openBasicChannel(null) command will return
+     * null until a reset occurs.
+     * With previous release (V2.05) it was not possible to set P2 value, this value was always
+     * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
+     * recommended that the device allows all values for P2, however only the following values
+     * are mandatory: '00', '04', '08', '0C'(as defined in [2])
+     * The implementation of the underlying SELECT command within this method shall be
+     * based on ISO 7816-4 with following options:
+     * <ul>
+     * <li>CLA = '00'</li>
+     * <li>INS = 'A4'</li>
+     * <li>P1 = '04' (Select by DF name/application identifier)</li>
+     * </ul>
+     *
+     * The select response data can be retrieved with byte[] getSelectResponse().
+     * The API shall handle received status word as follow. If the status word indicates that the
+     * Secure Element was able to open a channel (e.g. status word '90 00' or status words
+     * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
+     * channel opened and the next getSelectResponse() shall return the received status
+     * word.
+     * Other received status codes indicating that the Secure Element was able not to open a
+     * channel shall be considered as an error and the corresponding channel shall not be
+     * opened.
+     * The function without P2 as parameter is provided for backwards compatibility and will
+     * fall back to a select command with P2='00'.
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array, or null if no Applet is to be selected.
+     * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element session is used after
+     *             being closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
+     *             selected.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device
+     * @return an instance of Channel if available or null.
+     */
+    public @Nullable Channel openBasicChannel(byte[] aid, byte p2) throws IOException {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+
+        synchronized (mLock) {
+            try {
+                ISecureElementChannel channel = mSession.openBasicChannel(aid, p2,
+                        mReader.getSEService().getListener());
+                if (channel == null) {
+                    return null;
+                }
+                return new Channel(mService, this, channel);
+            } catch (RemoteException e) {
+                throw new IOException(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * This method is provided to ease the development of mobile application and for compliancy
+     * with existing applications.
+     * This method is equivalent to openBasicChannel(aid, P2=0x00)
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array, or null if no Applet is to be selected.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element session is used after
+     *             being closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
+     *             selected.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device
+     * @return an instance of Channel if available or null.
+     */
+    public @Nullable Channel openBasicChannel(byte[] aid) throws IOException {
+        return openBasicChannel(aid, (byte) 0x00);
+    }
+
+    /**
+     * Open a logical channel with the Secure Element, selecting the Applet represented by
+     * the given AID. If the AID is null, which means no Applet is to be selected on this
+     * channel, the default Applet is used. It's up to the Secure Element to choose which
+     * logical channel will be used.
+     * With previous release (V2.05) it was not possible to set P2 value, this value was always
+     * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
+     * recommended that the device allows all values for P2, however only the following values
+     * are mandatory: '00', '04', '08', '0C'(as defined in [2])
+     * The implementation of the underlying SELECT command within this method shall be
+     * based on ISO 7816-4 with following options:
+     *
+     * <ul>
+     * <li>CLA = '01' to '03', '40 to 4F'</li>
+     * <li>INS = 'A4'</li>
+     * <li>P1 = '04' (Select by DF name/application identifier)</li>
+     * </ul>
+     *
+     * The select response data can be retrieved with byte[] getSelectResponse().
+     * The API shall handle received status word as follow. If the status word indicates that the
+     * Secure Element was able to open a channel (e.g. status word '90 00' or status words
+     * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
+     * channel opened and the next getSelectResponse() shall return the received status
+     * word.
+     * Other received status codes indicating that the Secure Element was able not to open a
+     * channel shall be considered as an error and the corresponding channel shall not be
+     * opened.
+     * In case of UICC it is recommended for the API to reject the opening of the logical
+     * channel without a specific AID, by always answering null to such a request.
+     * The function without P2 as parameter is provided for backwards compatibility and will
+     * fall back to a select command with P2=00.
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array.
+     * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element is used after being
+     *             closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not
+     *             available or cannot be selected or a logical channel is already
+     *             open to a non-multiselectable Applet.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device.
+     * @return an instance of Channel. Null if the Secure Element is unable to
+     *         provide a new logical channel.
+     */
+    public @Nullable Channel openLogicalChannel(byte[] aid, byte p2) throws IOException {
+
+        if ((mReader.getName().startsWith("SIM")) && (aid == null)) {
+            Log.e(TAG, "NULL AID not supported on " + mReader.getName());
+            return null;
+        }
+
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        synchronized (mLock) {
+            try {
+                ISecureElementChannel channel = mSession.openLogicalChannel(
+                        aid,
+                        p2,
+                        mReader.getSEService().getListener());
+                if (channel == null) {
+                    return null;
+                }
+                return new Channel(mService, this, channel);
+            } catch (RemoteException e) {
+                throw new IOException(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * This method is provided to ease the development of mobile application and for compliancy
+     * with existing applications.
+     * This method is equivalent to openLogicalChannel(aid, P2=0x00)
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element is used after being
+     *             closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not
+     *             available or cannot be selected or a logical channel is already
+     *             open to a non-multiselectable Applet.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device.
+     * @return an instance of Channel. Null if the Secure Element is unable to
+     *         provide a new logical channel.
+     */
+    public @Nullable Channel openLogicalChannel(byte[] aid) throws IOException {
+        return openLogicalChannel(aid, (byte) 0x00);
+    }
+}
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index bbd9c9c..562ae7a 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -358,7 +358,7 @@
     }
 
     /**
-     * Return the current time in YYYYMMDDTHHMMSS<tz> format
+     * Return the current time in YYYYMMDDTHHMMSS&lt;tz&gt; format
      */
     @Override
     public String toString() {
@@ -738,6 +738,7 @@
      * <p>
      * You should also use <tt>toMillis(false)</tt> if you want
      * to read back the same milliseconds that you set with {@link #set(long)}
+     * or {@link #set(Time)} or after parsing a date string.
      *
      * <p>
      * This method can return {@code -1} when the date / time fields have been
@@ -745,8 +746,6 @@
      * For example, when daylight savings transitions cause an hour to be
      * skipped: times within that hour will return {@code -1} if isDst =
      * {@code -1}.
-     *
-     * or {@link #set(Time)} or after parsing a date string.
      */
     public long toMillis(boolean ignoreDst) {
         calculator.copyFieldsFromTime(this);
diff --git a/core/java/android/util/MutableBoolean.java b/core/java/android/util/MutableBoolean.java
index ed837ab..44e73cc 100644
--- a/core/java/android/util/MutableBoolean.java
+++ b/core/java/android/util/MutableBoolean.java
@@ -17,7 +17,9 @@
 package android.util;
 
 /**
+ * @deprecated This class will be removed from a future version of the Android API.
  */
+@Deprecated
 public final class MutableBoolean {
     public boolean value;
 
diff --git a/core/java/android/util/MutableByte.java b/core/java/android/util/MutableByte.java
index cc6b00a..b9ec25d 100644
--- a/core/java/android/util/MutableByte.java
+++ b/core/java/android/util/MutableByte.java
@@ -17,7 +17,9 @@
 package android.util;
 
 /**
+ * @deprecated This class will be removed from a future version of the Android API.
  */
+@Deprecated
 public final class MutableByte {
     public byte value;
 
diff --git a/core/java/android/util/MutableChar.java b/core/java/android/util/MutableChar.java
index 9a2e2bc..9f7a9ae 100644
--- a/core/java/android/util/MutableChar.java
+++ b/core/java/android/util/MutableChar.java
@@ -17,7 +17,9 @@
 package android.util;
 
 /**
+ * @deprecated This class will be removed from a future version of the Android API.
  */
+@Deprecated
 public final class MutableChar {
     public char value;
 
diff --git a/core/java/android/util/MutableDouble.java b/core/java/android/util/MutableDouble.java
index bd7329a..56e539b 100644
--- a/core/java/android/util/MutableDouble.java
+++ b/core/java/android/util/MutableDouble.java
@@ -17,7 +17,9 @@
 package android.util;
 
 /**
+ * @deprecated This class will be removed from a future version of the Android API.
  */
+@Deprecated
 public final class MutableDouble {
     public double value;
 
diff --git a/core/java/android/util/MutableFloat.java b/core/java/android/util/MutableFloat.java
index e6f2d7d..6d7ad59 100644
--- a/core/java/android/util/MutableFloat.java
+++ b/core/java/android/util/MutableFloat.java
@@ -17,7 +17,9 @@
 package android.util;
 
 /**
+ * @deprecated This class will be removed from a future version of the Android API.
  */
+@Deprecated
 public final class MutableFloat {
     public float value;
 
diff --git a/core/java/android/util/MutableInt.java b/core/java/android/util/MutableInt.java
index a3d8606..bb24566 100644
--- a/core/java/android/util/MutableInt.java
+++ b/core/java/android/util/MutableInt.java
@@ -17,7 +17,9 @@
 package android.util;
 
 /**
+ * @deprecated This class will be removed from a future version of the Android API.
  */
+@Deprecated
 public final class MutableInt {
     public int value;
 
diff --git a/core/java/android/util/MutableLong.java b/core/java/android/util/MutableLong.java
index 575068e..86e70e1 100644
--- a/core/java/android/util/MutableLong.java
+++ b/core/java/android/util/MutableLong.java
@@ -17,7 +17,9 @@
 package android.util;
 
 /**
+ * @deprecated This class will be removed from a future version of the Android API.
  */
+@Deprecated
 public final class MutableLong {
     public long value;
 
diff --git a/core/java/android/util/MutableShort.java b/core/java/android/util/MutableShort.java
index 48fb232..b94ab07 100644
--- a/core/java/android/util/MutableShort.java
+++ b/core/java/android/util/MutableShort.java
@@ -17,7 +17,9 @@
 package android.util;
 
 /**
+ * @deprecated This class will be removed from a future version of the Android API.
  */
+@Deprecated
 public final class MutableShort {
     public short value;
 
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 3d3e148..902bd12 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -31,13 +31,17 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ProcFileReader;
+import com.google.android.collect.Lists;
 
 import libcore.io.IoUtils;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.net.ProtocolException;
+import java.util.ArrayList;
 import java.util.Objects;
 
 /**
@@ -55,6 +59,8 @@
     // Used for correct stats accounting on clatd interfaces.
     private static final int IPV4V6_HEADER_DELTA = 20;
 
+    /** Path to {@code /proc/net/dev}. */
+    private final File mStatsIfaceDev;
     /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
     private final File mStatsXtIfaceAll;
     /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
@@ -62,6 +68,8 @@
     /** Path to {@code /proc/net/xt_qtaguid/stats}. */
     private final File mStatsXtUid;
 
+    private boolean mUseBpfStats;
+
     // TODO: to improve testability and avoid global state, do not use a static variable.
     @GuardedBy("sStackedIfaces")
     private static final ArrayMap<String, String> sStackedIfaces = new ArrayMap<>();
@@ -77,14 +85,54 @@
     }
 
     public NetworkStatsFactory() {
-        this(new File("/proc/"));
+        this(new File("/proc/"), new File("/sys/fs/bpf/traffic_uid_stats_map").exists());
     }
 
     @VisibleForTesting
-    public NetworkStatsFactory(File procRoot) {
+    public NetworkStatsFactory(File procRoot, boolean useBpfStats) {
+        mStatsIfaceDev = new File(procRoot, "net/dev");
         mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
         mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
         mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
+        mUseBpfStats = useBpfStats;
+    }
+
+    @VisibleForTesting
+    public NetworkStats readNetworkStatsIfaceDev() throws IOException {
+        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
+
+        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
+        final NetworkStats.Entry entry = new NetworkStats.Entry();
+
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader(mStatsIfaceDev));
+
+            // skip first two header lines
+            reader.readLine();
+            reader.readLine();
+
+            // parse remaining lines
+            String line;
+            while ((line = reader.readLine()) != null) {
+                String[] values = line.trim().split("\\:?\\s+");
+                entry.iface = values[0];
+                entry.uid = UID_ALL;
+                entry.set = SET_ALL;
+                entry.tag = TAG_NONE;
+                entry.rxBytes = Long.parseLong(values[1]);
+                entry.rxPackets = Long.parseLong(values[2]);
+                entry.txBytes = Long.parseLong(values[9]);
+                entry.txPackets = Long.parseLong(values[10]);
+                stats.addValues(entry);
+            }
+        } catch (NullPointerException|NumberFormatException e) {
+            throw new ProtocolException("problem parsing stats", e);
+        } finally {
+            IoUtils.closeQuietly(reader);
+            StrictMode.setThreadPolicy(savedPolicy);
+        }
+        return stats;
     }
 
     /**
@@ -96,6 +144,11 @@
      * @throws IllegalStateException when problem parsing stats.
      */
     public NetworkStats readNetworkStatsSummaryDev() throws IOException {
+
+        // Return the stats get from /proc/net/dev if switched to bpf module.
+        if (mUseBpfStats)
+            return readNetworkStatsIfaceDev();
+
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
 
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
@@ -147,6 +200,11 @@
      * @throws IllegalStateException when problem parsing stats.
      */
     public NetworkStats readNetworkStatsSummaryXt() throws IOException {
+
+        // Return the stats get from /proc/net/dev if qtaguid  module is replaced.
+        if (mUseBpfStats)
+            return readNetworkStatsIfaceDev();
+
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
 
         // return null when kernel doesn't support
@@ -217,7 +275,7 @@
             }
 
             NetworkStats.Entry adjust =
-                    new NetworkStats.Entry(baseIface, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
+                    new NetworkStats.Entry(baseIface, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
             // Subtract any 464lat traffic seen for the root UID on the current base interface.
             adjust.rxBytes -= (entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
             adjust.txBytes -= (entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
@@ -252,7 +310,7 @@
                 stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
             }
             if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
-                    limitIfaces, limitTag) != 0) {
+                    limitIfaces, limitTag, mUseBpfStats) != 0) {
                 throw new IOException("Failed to parse network stats");
             }
             if (SANITY_CHECK_NATIVE) {
@@ -346,6 +404,6 @@
      * are expected to monotonically increase since device boot.
      */
     @VisibleForTesting
-    public static native int nativeReadNetworkStatsDetail(
-            NetworkStats stats, String path, int limitUid, String[] limitIfaces, int limitTag);
+    public static native int nativeReadNetworkStatsDetail(NetworkStats stats, String path,
+        int limitUid, String[] limitIfaces, int limitTag, boolean useBpfStats);
 }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 3ee8b47..d0f5ba7 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -55,6 +55,8 @@
     public static final int DISABLE_VERIFIER = 1 << 9;
     /** Only use oat files located in /system. Otherwise use dex/jar/apk . */
     public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
+    /** Do not enfore hidden API access restrictions. */
+    public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
 
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = 0;
@@ -69,6 +71,9 @@
 
     private Zygote() {}
 
+    /** Called for some security initialization before any fork. */
+    native static void nativeSecurityInit();
+
     /**
      * Forks a new VM instance.  The current VM must have been started
      * with the -Xzygote flag. <b>NOTE: new instance keeps all
@@ -155,6 +160,9 @@
      */
     public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
+        // SystemServer is always allowed to use hidden APIs.
+        runtimeFlags |= DISABLE_HIDDEN_API_CHECKS;
+
         VM_HOOKS.preFork();
         // Resets nice priority for zygote process.
         resetNicePriority();
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 2be6212..89a70fc 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -30,7 +30,6 @@
 import android.os.Environment;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.Seccomp;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
 import android.os.SystemClock;
@@ -99,6 +98,10 @@
 
     private static final String SOCKET_NAME_ARG = "--socket-name=";
 
+    /* Dexopt flag to disable hidden API access checks when dexopting SystemServer.
+     * Must be kept in sync with com.android.server.pm.Installer. */
+    private static final int DEXOPT_DISABLE_HIDDEN_API_CHECKS = 1 << 10;
+
     /**
      * Used to pre-load resources.
      */
@@ -566,16 +569,21 @@
             if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                 final String packageName = "*";
                 final String outputPath = null;
-                final int dexFlags = 0;
+                // Dexopt with a flag which lifts restrictions on hidden API usage.
+                // Offending methods would otherwise be re-verified at runtime and
+                // we want to avoid the performance overhead of that.
+                final int dexFlags = DEXOPT_DISABLE_HIDDEN_API_CHECKS;
                 final String compilerFilter = systemServerFilter;
                 final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
                 final String seInfo = null;
                 final String classLoaderContext =
                         getSystemServerClassLoaderContext(classPathForElement);
+                final int targetSdkVersion = 0;  // SystemServer targets the system's SDK version
                 try {
                     installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
                             instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
-                            uuid, classLoaderContext, seInfo, false /* downgrade */);
+                            uuid, classLoaderContext, seInfo, false /* downgrade */,
+                            targetSdkVersion);
                 } catch (RemoteException | ServiceSpecificException e) {
                     // Ignore (but log), we need this on the classpath for fallback mode.
                     Log.w(TAG, "Failed compiling classpath element for system server: "
@@ -779,12 +787,11 @@
             // Zygote.
             Trace.setTracingEnabled(false, 0);
 
+            Zygote.nativeSecurityInit();
+
             // Zygote process unmounts root storage spaces.
             Zygote.nativeUnmountStorageOnInit();
 
-            // Set seccomp policy
-            Seccomp.setPolicy();
-
             ZygoteHooks.stopZygoteNoThreadCreation();
 
             if (startSystemServer) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 551d54a..b3fb43d 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -86,7 +86,6 @@
         "android_os_MessageQueue.cpp",
         "android_os_Parcel.cpp",
         "android_os_SELinux.cpp",
-        "android_os_seccomp.cpp",
         "android_os_SharedMemory.cpp",
         "android_os_SystemClock.cpp",
         "android_os_SystemProperties.cpp",
@@ -216,6 +215,8 @@
     ],
 
     shared_libs: [
+        "libbpf",
+        "libnetdutils",
         "libmemtrack",
         "libandroidfw",
         "libappfuse",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 047fa84..d7f725d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -163,7 +163,6 @@
 extern int register_android_os_SELinux(JNIEnv* env);
 extern int register_android_os_VintfObject(JNIEnv *env);
 extern int register_android_os_VintfRuntimeInfo(JNIEnv *env);
-extern int register_android_os_seccomp(JNIEnv* env);
 extern int register_android_os_SystemProperties(JNIEnv *env);
 extern int register_android_os_SystemClock(JNIEnv* env);
 extern int register_android_os_Trace(JNIEnv* env);
@@ -970,6 +969,12 @@
         addOption("--generate-debug-info");
     }
 
+    // The mini-debug-info makes it possible to backtrace through JIT code.
+    if (property_get_bool("dalvik.vm.minidebuginfo", 0)) {
+        addOption("-Xcompiler-option");
+        addOption("--generate-mini-debug-info");
+    }
+
     /*
      * Retrieve the build fingerprint and provide it to the runtime. That way, ANR dumps will
      * contain the fingerprint and can be parsed.
@@ -1420,7 +1425,6 @@
     REG_JNI(register_android_os_GraphicsEnvironment),
     REG_JNI(register_android_os_MessageQueue),
     REG_JNI(register_android_os_SELinux),
-    REG_JNI(register_android_os_seccomp),
     REG_JNI(register_android_os_Trace),
     REG_JNI(register_android_os_UEventObserver),
     REG_JNI(register_android_net_LocalSocketImpl),
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index 9494fb8..061349a 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -391,6 +391,10 @@
     Status status;
     status_t err = ::android::hardware::readFromParcel(&status, *parcel);
     signalExceptionForError(env, err);
+
+    if (!status.isOk()) {
+        signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
+    }
 }
 
 static void JHwParcel_native_release(
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 1eeea51..1659168 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -146,8 +146,8 @@
         return nullptr;
     }
     jobject jMap = env->NewObject(gHashMapClazz, gHashMapInit);
-    for (const Vndk &vndk : manifest->vndks()) {
-        std::string key = to_string(vndk.versionRange());
+    for (const auto &vndk : manifest->vendorNdks()) {
+        std::string key = vndk.version();
         env->CallObjectMethod(jMap, gHashMapPut,
                 env->NewStringUTF(key.c_str()), toJavaStringArray(env, vndk.libraries()));
     }
diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp
deleted file mode 100644
index 06e2a16..0000000
--- a/core/jni/android_os_seccomp.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include "core_jni_helpers.h"
-#include <nativehelper/JniConstants.h>
-#include "utils/Log.h"
-#include <selinux/selinux.h>
-
-#include "seccomp_policy.h"
-
-static void Seccomp_setPolicy(JNIEnv* /*env*/) {
-    if (security_getenforce() == 0) {
-        ALOGI("seccomp disabled by setenforce 0");
-        return;
-    }
-
-    if (!set_seccomp_filter()) {
-        ALOGE("Failed to set seccomp policy - killing");
-        exit(1);
-    }
-}
-
-static const JNINativeMethod method_table[] = {
-    NATIVE_METHOD(Seccomp, setPolicy, "()V"),
-};
-
-namespace android {
-
-int register_android_os_seccomp(JNIEnv* env) {
-    return android::RegisterMethodsOrDie(env, "android/os/Seccomp",
-                                         method_table, NELEM(method_table));
-}
-
-}
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index 0cb6935..99d9839 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -30,7 +30,14 @@
 
 #include <utils/Log.h>
 #include <utils/misc.h>
-#include <utils/Vector.h>
+
+#include "android-base/unique_fd.h"
+#include "bpf/BpfNetworkStats.h"
+#include "bpf/BpfUtils.h"
+
+using android::bpf::hasBpfSupport;
+using android::bpf::parseBpfNetworkStatsDetail;
+using android::bpf::stats_line;
 
 namespace android {
 
@@ -45,6 +52,7 @@
     jfieldID tag;
     jfieldID metered;
     jfieldID roaming;
+    jfieldID defaultNetwork;
     jfieldID rxBytes;
     jfieldID rxPackets;
     jfieldID txBytes;
@@ -52,17 +60,6 @@
     jfieldID operations;
 } gNetworkStatsClassInfo;
 
-struct stats_line {
-    char iface[32];
-    int32_t uid;
-    int32_t set;
-    int32_t tag;
-    int64_t rxBytes;
-    int64_t rxPackets;
-    int64_t txBytes;
-    int64_t txPackets;
-};
-
 static jobjectArray get_string_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow)
 {
     if (!grow) {
@@ -96,33 +93,14 @@
     return env->NewLongArray(size);
 }
 
-static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
-        jstring path, jint limitUid, jobjectArray limitIfacesObj, jint limitTag) {
-    ScopedUtfChars path8(env, path);
-    if (path8.c_str() == NULL) {
-        return -1;
-    }
-
-    FILE *fp = fopen(path8.c_str(), "r");
+static int legacyReadNetworkStatsDetail(std::vector<stats_line>* lines,
+                                        const std::vector<std::string>& limitIfaces,
+                                        int limitTag, int limitUid, const char* path) {
+    FILE* fp = fopen(path, "r");
     if (fp == NULL) {
         return -1;
     }
 
-    Vector<String8> limitIfaces;
-    if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
-        int num = env->GetArrayLength(limitIfacesObj);
-        limitIfaces.setCapacity(num);
-        for (int i=0; i<num; i++) {
-            jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
-            ScopedUtfChars string8(env, string);
-            if (string8.c_str() != NULL) {
-                limitIfaces.add(String8(string8.c_str()));
-            }
-        }
-    }
-
-    Vector<stats_line> lines;
-
     int lastIdx = 1;
     int idx;
     char buffer[384];
@@ -214,7 +192,7 @@
                 //ALOGI("skipping due to uid: %s", buffer);
                 continue;
             }
-            lines.push_back(s);
+            lines->push_back(s);
         } else {
             //ALOGI("skipping due to bad remaining fields: %s", pos);
         }
@@ -224,8 +202,42 @@
         ALOGE("Failed to close netstats file");
         return -1;
     }
+    return 0;
+}
+
+static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
+                                  jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
+                                  jboolean useBpfStats) {
+    ScopedUtfChars path8(env, path);
+    if (path8.c_str() == NULL) {
+        return -1;
+    }
+
+    std::vector<std::string> limitIfaces;
+    if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
+        int num = env->GetArrayLength(limitIfacesObj);
+        for (int i = 0; i < num; i++) {
+            jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
+            ScopedUtfChars string8(env, string);
+            if (string8.c_str() != NULL) {
+                limitIfaces.push_back(std::string(string8.c_str()));
+            }
+        }
+    }
+    std::vector<stats_line> lines;
+
+
+    if (useBpfStats) {
+        if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
+            return -1;
+    } else {
+        if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
+                                         limitUid, path8.c_str()) < 0)
+            return -1;
+    }
 
     int size = lines.size();
+
     bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity);
 
     ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats,
@@ -246,6 +258,9 @@
     ScopedIntArrayRW roaming(env, get_int_array(env, stats,
             gNetworkStatsClassInfo.roaming, size, grow));
     if (roaming.get() == NULL) return -1;
+    ScopedIntArrayRW defaultNetwork(env, get_int_array(env, stats,
+            gNetworkStatsClassInfo.defaultNetwork, size, grow));
+    if (defaultNetwork.get() == NULL) return -1;
     ScopedLongArrayRW rxBytes(env, get_long_array(env, stats,
             gNetworkStatsClassInfo.rxBytes, size, grow));
     if (rxBytes.get() == NULL) return -1;
@@ -269,7 +284,7 @@
         uid[i] = lines[i].uid;
         set[i] = lines[i].set;
         tag[i] = lines[i].tag;
-        // Metered and Roaming are populated in Java-land by inspecting the iface properties.
+        // Metered, roaming and defaultNetwork are populated in Java-land.
         rxBytes[i] = lines[i].rxBytes;
         rxPackets[i] = lines[i].rxPackets;
         txBytes[i] = lines[i].txBytes;
@@ -285,6 +300,8 @@
         env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray());
         env->SetObjectField(stats, gNetworkStatsClassInfo.metered, metered.getJavaArray());
         env->SetObjectField(stats, gNetworkStatsClassInfo.roaming, roaming.getJavaArray());
+        env->SetObjectField(stats, gNetworkStatsClassInfo.defaultNetwork,
+                defaultNetwork.getJavaArray());
         env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray());
         env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray());
         env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray());
@@ -297,7 +314,7 @@
 
 static const JNINativeMethod gMethods[] = {
         { "nativeReadNetworkStatsDetail",
-                "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;I)I",
+                "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;IZ)I",
                 (void*) readNetworkStatsDetail }
 };
 
@@ -318,6 +335,7 @@
     gNetworkStatsClassInfo.tag = GetFieldIDOrDie(env, clazz, "tag", "[I");
     gNetworkStatsClassInfo.metered = GetFieldIDOrDie(env, clazz, "metered", "[I");
     gNetworkStatsClassInfo.roaming = GetFieldIDOrDie(env, clazz, "roaming", "[I");
+    gNetworkStatsClassInfo.defaultNetwork = GetFieldIDOrDie(env, clazz, "defaultNetwork", "[I");
     gNetworkStatsClassInfo.rxBytes = GetFieldIDOrDie(env, clazz, "rxBytes", "[J");
     gNetworkStatsClassInfo.rxPackets = GetFieldIDOrDie(env, clazz, "rxPackets", "[J");
     gNetworkStatsClassInfo.txBytes = GetFieldIDOrDie(env, clazz, "txBytes", "[J");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 32ef3dc..63dba43 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -53,6 +53,7 @@
 #include <private/android_filesystem_config.h>
 #include <utils/String8.h>
 #include <selinux/android.h>
+#include <seccomp_policy.h>
 #include <processgroup/processgroup.h>
 
 #include "core_jni_helpers.h"
@@ -76,6 +77,8 @@
 static jclass gZygoteClass;
 static jmethodID gCallPostForkChildHooks;
 
+static bool g_is_security_enforced = true;
+
 // Must match values in com.android.internal.os.Zygote.
 enum MountExternalKind {
   MOUNT_EXTERNAL_NONE = 0,
@@ -229,6 +232,20 @@
   mallopt(M_DECAY_TIME, 1);
 }
 
+static void SetUpSeccompFilter(uid_t uid) {
+  if (!g_is_security_enforced) {
+    ALOGI("seccomp disabled by setenforce 0");
+    return;
+  }
+
+  // Apply system or app filter based on uid.
+  if (getuid() >= AID_APP_START) {
+    set_app_seccomp_filter();
+  } else {
+    set_system_seccomp_filter();
+  }
+}
+
 static void EnableKeepCapabilities(JNIEnv* env) {
   int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
   if (rc == -1) {
@@ -541,6 +558,11 @@
       RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed.");
     }
 
+    // Must be called when the new process still has CAP_SYS_ADMIN.  The other alternative is to
+    // call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+    // b/71859146).
+    SetUpSeccompFilter(uid);
+
     // Keep capabilities across UID change, unless we're staying root.
     if (uid != 0) {
       EnableKeepCapabilities(env);
@@ -698,6 +720,12 @@
 
 namespace android {
 
+static void com_android_internal_os_Zygote_nativeSecurityInit(JNIEnv*, jclass) {
+  // security_getenforce is not allowed on app process. Initialize and cache the value before
+  // zygote forks.
+  g_is_security_enforced = security_getenforce();
+}
+
 static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jclass) {
   PreApplicationInit();
 }
@@ -832,6 +860,8 @@
 }
 
 static const JNINativeMethod gMethods[] = {
+    { "nativeSecurityInit", "()V",
+      (void *) com_android_internal_os_Zygote_nativeSecurityInit },
     { "nativeForkAndSpecialize",
       "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
index 5a577b1..e5dbdbb 100644
--- a/core/proto/android/service/netstats.proto
+++ b/core/proto/android/service/netstats.proto
@@ -64,6 +64,8 @@
     bool roaming = 4;
 
     bool metered = 5;
+
+    bool default_network = 6;
 }
 
 // Corresponds to NetworkStatsRecorder.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fdda55b..07f9530 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1415,6 +1415,12 @@
         android:label="@string/permlab_nfc"
         android:protectionLevel="normal" />
 
+    <!-- Allows applications to receive NFC transaction events.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.NFC_TRANSACTION_EVENT"
+        android:protectionLevel="normal" />
+
     <!-- @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
          @hide -->
     <permission android:name="android.permission.CONNECTIVITY_INTERNAL"
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
index c66ed12..4a3bf22 100755
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -25,15 +25,5 @@
     -->
     <integer name="config_mobile_mtu">1358</integer>
 
-    <!--Thresholds for LTE dbm in status bar-->
-    <integer-array translatable="false" name="config_lteDbmThresholds">
-        <item>-140</item>    <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
-        <item>-115</item>    <!-- SIGNAL_STRENGTH_POOR -->
-        <item>-105</item>    <!-- SIGNAL_STRENGTH_MODERATE -->
-        <item>-95</item>     <!-- SIGNAL_STRENGTH_GOOD -->
-        <item>-85</item>     <!-- SIGNAL_STRENGTH_GREAT -->
-        <item>-44</item>
-    </integer-array>
-
     <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true;BAE0000000000000</string>
 </resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index 04f182e..cc7daa8 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -51,16 +51,6 @@
 
     <bool name="config_auto_attach_data_on_creation">false</bool>
 
-    <!--Thresholds for LTE dbm in status bar-->
-    <integer-array translatable="false" name="config_lteDbmThresholds">
-        <item>-140</item>    <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
-        <item>-115</item>    <!-- SIGNAL_STRENGTH_POOR -->
-        <item>-105</item>    <!-- SIGNAL_STRENGTH_MODERATE -->
-        <item>-95</item>     <!-- SIGNAL_STRENGTH_GOOD -->
-        <item>-85</item>     <!-- SIGNAL_STRENGTH_GREAT -->
-        <item>-44</item>
-    </integer-array>
-
     <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
 
     <bool name="config_use_sim_language_file">true</bool>
diff --git a/core/res/res/values-mcc505-mnc01/config.xml b/core/res/res/values-mcc505-mnc01/config.xml
index 5a5b8f7..bc088d1 100644
--- a/core/res/res/values-mcc505-mnc01/config.xml
+++ b/core/res/res/values-mcc505-mnc01/config.xml
@@ -31,16 +31,6 @@
       <item>9</item>
     </integer-array>
 
-    <!--Thresholds for LTE dbm in status bar-->
-    <integer-array translatable="false" name="config_lteDbmThresholds">
-        <item>-140</item>    <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
-        <item>-120</item>    <!-- SIGNAL_STRENGTH_POOR -->
-        <item>-115</item>    <!-- SIGNAL_STRENGTH_MODERATE -->
-        <item>-100</item>    <!-- SIGNAL_STRENGTH_GOOD -->
-        <item>-90</item>     <!-- SIGNAL_STRENGTH_GREAT -->
-        <item>-44</item>
-    </integer-array>
-
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1400</integer>
diff --git a/core/res/res/values-mcc505-mnc11/config.xml b/core/res/res/values-mcc505-mnc11/config.xml
deleted file mode 100644
index 6d085c1..0000000
--- a/core/res/res/values-mcc505-mnc11/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!--Thresholds for LTE dbm in status bar-->
-    <integer-array translatable="false" name="config_lteDbmThresholds">
-        <item>-140</item>    <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
-        <item>-120</item>    <!-- SIGNAL_STRENGTH_POOR -->
-        <item>-115</item>    <!-- SIGNAL_STRENGTH_MODERATE -->
-        <item>-100</item>    <!-- SIGNAL_STRENGTH_GOOD -->
-        <item>-90</item>     <!-- SIGNAL_STRENGTH_GREAT -->
-        <item>-44</item>
-    </integer-array>
-</resources>
diff --git a/core/res/res/values-mcc505-mnc71/config.xml b/core/res/res/values-mcc505-mnc71/config.xml
deleted file mode 100644
index 6d085c1..0000000
--- a/core/res/res/values-mcc505-mnc71/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!--Thresholds for LTE dbm in status bar-->
-    <integer-array translatable="false" name="config_lteDbmThresholds">
-        <item>-140</item>    <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
-        <item>-120</item>    <!-- SIGNAL_STRENGTH_POOR -->
-        <item>-115</item>    <!-- SIGNAL_STRENGTH_MODERATE -->
-        <item>-100</item>    <!-- SIGNAL_STRENGTH_GOOD -->
-        <item>-90</item>     <!-- SIGNAL_STRENGTH_GREAT -->
-        <item>-44</item>
-    </integer-array>
-</resources>
diff --git a/core/res/res/values-mcc505-mnc72/config.xml b/core/res/res/values-mcc505-mnc72/config.xml
deleted file mode 100644
index 6d085c1..0000000
--- a/core/res/res/values-mcc505-mnc72/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!--Thresholds for LTE dbm in status bar-->
-    <integer-array translatable="false" name="config_lteDbmThresholds">
-        <item>-140</item>    <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
-        <item>-120</item>    <!-- SIGNAL_STRENGTH_POOR -->
-        <item>-115</item>    <!-- SIGNAL_STRENGTH_MODERATE -->
-        <item>-100</item>    <!-- SIGNAL_STRENGTH_GOOD -->
-        <item>-90</item>     <!-- SIGNAL_STRENGTH_GREAT -->
-        <item>-44</item>
-    </integer-array>
-</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e18265b..5cc727d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2557,6 +2557,7 @@
 
     <bool name="config_sms_force_7bit_encoding">false</bool>
 
+
     <!-- Number of physical SIM slots on the device. This includes both eSIM and pSIM slots, and
          is not necessarily the same as the number of phones/logical modems supported by the device.
          For example, a multi-sim device can have 2 phones/logical modems, but 3 physical slots,
@@ -2564,16 +2565,6 @@
          and one pSIM) -->
     <integer name="config_num_physical_slots">1</integer>
 
-    <!--Thresholds for LTE dbm in status bar-->
-    <integer-array translatable="false" name="config_lteDbmThresholds">
-        <item>-140</item>    <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
-        <item>-128</item>    <!-- SIGNAL_STRENGTH_POOR -->
-        <item>-118</item>    <!-- SIGNAL_STRENGTH_MODERATE -->
-        <item>-108</item>    <!-- SIGNAL_STRENGTH_GOOD -->
-        <item>-98</item>     <!-- SIGNAL_STRENGTH_GREAT -->
-        <item>-44</item>
-    </integer-array>
-
     <!-- Enabled built-in zen mode condition providers -->
     <string-array translatable="false" name="config_system_condition_providers">
         <item>countdown</item>
@@ -3104,4 +3095,6 @@
     <!-- Decide whether to display 'No service' on status bar instead of 'Emergency calls only'
          when SIM is unready. -->
     <bool name="config_display_no_service_when_sim_unready">false</bool>
+
+    <bool name="config_supportBluetoothPersistedState">true</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f1070de..fb755a1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2384,9 +2384,6 @@
   <!-- Cascading submenus -->
   <java-symbol type="dimen" name="cascading_menus_min_smallest_width" />
 
-  <!-- From SignalStrength -->
-  <java-symbol type="array" name="config_lteDbmThresholds" />
-
   <java-symbol type="string" name="android_system_label" />
   <java-symbol type="string" name="system_error_wipe_data" />
   <java-symbol type="string" name="system_error_manufacturer" />
@@ -3082,4 +3079,6 @@
   <java-symbol type="integer" name="config_stableDeviceDisplayWidth" />
   <java-symbol type="integer" name="config_stableDeviceDisplayHeight" />
   <java-symbol type="bool" name="config_display_no_service_when_sim_unready" />
+
+  <java-symbol type="bool" name="config_supportBluetoothPersistedState" />
 </resources>
diff --git a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
index e62fbd6..c213464 100644
--- a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
+++ b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
@@ -53,7 +53,7 @@
                     stats, mStats.getAbsolutePath(), NetworkStats.UID_ALL,
                     // Looks like this was broken by change d0c5b9abed60b7bc056d026bf0f2b2235410fb70
                     // Fixed compilation problem but needs addressing properly.
-                    new String[0], 999);
+                    new String[0], 999, false);
         }
     }
 }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 19808ca..ee276ef 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -111,6 +111,12 @@
                     Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
                     Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
                     Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
+                    Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS,
+                    Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS,
+                    Settings.Global.BLE_SCAN_BALANCED_WINDOW_MS,
+                    Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS,
+                    Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS,
+                    Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
                     Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
                     Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
                     Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX,
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e3740e3..7ad062a 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -163,7 +163,7 @@
     /**
      * Create a drawable by opening a given file path and decoding the bitmap.
      */
-    @SuppressWarnings("unused")
+    @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
     public BitmapDrawable(Resources res, String filepath) {
         this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
         mBitmapState.mTargetDensity = mTargetDensity;
@@ -188,7 +188,7 @@
     /**
      * Create a drawable by decoding a bitmap from the given input stream.
      */
-    @SuppressWarnings("unused")
+    @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
     public BitmapDrawable(Resources res, java.io.InputStream is) {
         this(new BitmapState(BitmapFactory.decodeStream(is)), null);
         mBitmapState.mTargetDensity = mTargetDensity;
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
deleted file mode 100644
index 793bbe8..0000000
--- a/legacy-test/Android.mk
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-# Generate the stub source files for legacy.test.stubs
-# ====================================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := \
-    core-oj \
-    core-libart \
-    framework \
-
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src
-
-LEGACY_TEST_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/legacy.test.stubs_intermediates/api.txt
-LEGACY_TEST_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/legacy.test.stubs_intermediates/removed.txt
-
-LEGACY_TEST_API_FILE := $(LOCAL_PATH)/api/legacy-test-current.txt
-LEGACY_TEST_REMOVED_API_FILE := $(LOCAL_PATH)/api/legacy-test-removed.txt
-
-LOCAL_DROIDDOC_OPTIONS:= \
-    -stubpackages android.test:android.test.suitebuilder.annotation:com.android.internal.util:junit.framework \
-    -stubsourceonly \
-    -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/legacy.test.stubs_intermediates/src \
-    -nodocs \
-    -api $(LEGACY_TEST_OUTPUT_API_FILE) \
-    -removedApi $(LEGACY_TEST_OUTPUT_REMOVED_API_FILE) \
-
-LOCAL_UNINSTALLABLE_MODULE := true
-LOCAL_MODULE := legacy-test-api-stubs-gen
-
-include $(BUILD_DROIDDOC)
-
-# Remember the target that will trigger the code generation.
-legacy_test_api_gen_stamp := $(full_target)
-
-# Add some additional dependencies
-$(LEGACY_TEST_OUTPUT_API_FILE): $(full_target)
-$(LEGACY_TEST_OUTPUT_REMOVED_API_FILE): $(full_target)
-
-# Build the legacy.test.stubs library
-# ===================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := legacy.test.stubs
-
-LOCAL_SOURCE_FILES_ALL_GENERATED := true
-
-# Make sure to run droiddoc first to generate the stub source files.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(legacy_test_api_gen_stamp)
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Archive a copy of the classes.jar in SDK build.
-$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):legacy.test.stubs.jar)
-
-# Check that the legacy.test.stubs library has not changed
-# ========================================================
-
-# Check that the API we're building hasn't changed from the not-yet-released
-# SDK version.
-$(eval $(call check-api, \
-    check-legacy-test-api-current, \
-    $(LEGACY_TEST_API_FILE), \
-    $(LEGACY_TEST_OUTPUT_API_FILE), \
-    $(LEGACY_TEST_REMOVED_API_FILE), \
-    $(LEGACY_TEST_OUTPUT_REMOVED_API_FILE), \
-    -error 2 -error 3 -error 4 -error 5 -error 6 \
-    -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
-    -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
-    -error 25 -error 26 -error 27, \
-    cat $(LOCAL_PATH)/api/apicheck_msg_legacy_test.txt, \
-    check-legacy-test-api, \
-    $(call doc-timestamp-for,legacy-test-api-stubs-gen) \
-    ))
-
-.PHONY: check-legacy-test-api
-checkapi: check-legacy-test-api
-
-.PHONY: update-legacy-test-api
-update-api: update-legacy-test-api
-
-update-legacy-test-api: $(LEGACY_TEST_OUTPUT_API_FILE) | $(ACP)
-	@echo Copying current.txt
-	$(hide) $(ACP) $(LEGACY_TEST_OUTPUT_API_FILE) $(LEGACY_TEST_API_FILE)
-	@echo Copying removed.txt
-	$(hide) $(ACP) $(LEGACY_TEST_OUTPUT_REMOVED_API_FILE) $(LEGACY_TEST_REMOVED_API_FILE)
-
-ifeq ($(HOST_OS),linux)
-# Build the legacy-performance-test-hostdex library
-# =================================================
-# This contains the android.test.PerformanceTestCase class only
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java
-LOCAL_MODULE := legacy-performance-test-hostdex
-
-include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY)
-endif  # HOST_OS == linux
diff --git a/legacy-test/api/apicheck_msg_legacy_test.txt b/legacy-test/api/apicheck_msg_legacy_test.txt
deleted file mode 100644
index ad5f235..0000000
--- a/legacy-test/api/apicheck_msg_legacy_test.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-
-******************************
-You have tried to change the API from what has been previously approved.
-
-To make these errors go away, you have two choices:
-   1) You can add "@hide" javadoc comments to the methods, etc. listed in the
-      errors above.
-
-   2) You can update legacy-test-current.txt by executing the following command:
-         make update-legacy-test-api
-
-      To submit the revised legacy-test-current.txt to the main Android repository,
-      you will need approval.
-******************************
-
-
-
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 7c60467..e3af655 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -551,18 +551,20 @@
     }
 
     // Animate spots that are fading out and being removed.
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
+    for (size_t i = 0; i < mLocked.spots.size();) {
         Spot* spot = mLocked.spots.itemAt(i);
         if (spot->id == Spot::INVALID_ID) {
             spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
             if (spot->alpha <= 0) {
-                mLocked.spots.removeAt(i--);
+                mLocked.spots.removeAt(i);
                 releaseSpotLocked(spot);
+                continue;
             } else {
                 spot->sprite->setAlpha(spot->alpha);
                 keepAnimating = true;
             }
         }
+        ++i;
     }
     return keepAnimating;
 }
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
new file mode 100644
index 0000000..53e6201
--- /dev/null
+++ b/libs/services/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+// Provides C++ wrappers for system services.
+
+cc_library_shared {
+    name: "libservices",
+    srcs: [
+        ":IDropBoxManagerService.aidl",
+        "src/os/DropBoxManager.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libcutils",
+        "libutils",
+    ],
+    header_libs: [
+        "libbase_headers",
+    ],
+    aidl: {
+        include_dirs: ["frameworks/base/core/java/"],
+    },
+
+    export_include_dirs: ["include"],
+    export_header_lib_headers: ["libbase_headers"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/libs/services/Android.mk b/libs/services/Android.mk
deleted file mode 100644
index cbfd4b3..0000000
--- a/libs/services/Android.mk
+++ /dev/null
@@ -1,43 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Provides C++ wrappers for system services.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libservices
-LOCAL_SRC_FILES := \
-    ../../core/java/com/android/internal/os/IDropBoxManagerService.aidl \
-    src/os/DropBoxManager.cpp
-
-LOCAL_AIDL_INCLUDES := \
-    $(LOCAL_PATH)/../../core/java
-LOCAL_C_INCLUDES := \
-    system/core/include
-LOCAL_SHARED_LIBRARIES := \
-    libbinder \
-    liblog \
-    libcutils \
-    libutils
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h
index 8717178..3449a7b 100644
--- a/libs/services/include/android/os/DropBoxManager.h
+++ b/libs/services/include/android/os/DropBoxManager.h
@@ -57,7 +57,11 @@
     // and a handle will be passed to the system process, so no additional permissions
     // are required from the system process.  Returns NULL if the file can't be opened.
     Status addFile(const String16& tag, const string& filename, int flags);
-    
+
+    // Create a new Entry from an already opened file. Takes ownership of the
+    // file descriptor.
+    Status addFile(const String16& tag, int fd, int flags);
+
     class Entry : public virtual RefBase, public Parcelable {
     public:
         Entry();
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
index bbb45f0..e8e34d7 100644
--- a/libs/services/src/os/DropBoxManager.cpp
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -179,7 +179,12 @@
         ALOGW("DropboxManager: %s", message.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str());
     }
+    return addFile(tag, fd, flags);
+}
 
+Status
+DropBoxManager::addFile(const String16& tag, int fd, int flags)
+{
     Entry entry(tag, flags, fd);
     return add(entry);
 }
diff --git a/location/lib/Android.mk b/location/lib/Android.mk
index 62f5677..8424601 100644
--- a/location/lib/Android.mk
+++ b/location/lib/Android.mk
@@ -22,9 +22,7 @@
 LOCAL_MODULE:= com.android.location.provider
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := \
-            $(call all-subdir-java-files) \
-            $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 include $(BUILD_JAVA_LIBRARY)
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3df1706..339c767 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3629,6 +3629,33 @@
     }
 
      /**
+     * Indicate A2DP source or sink connection state change and eventually suppress
+     * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
+     * @param device Bluetooth device connected/disconnected
+     * @param state  new connection state (BluetoothProfile.STATE_xxx)
+     * @param profile profile for the A2DP device
+     * (either {@link android.bluetooth.BluetoothProfile.A2DP} or
+     * {@link android.bluetooth.BluetoothProfile.A2DP_SINK})
+     * @param suppressNoisyIntent if true the
+     * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+     * @return a delay in ms that the caller should wait before broadcasting
+     * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent.
+     * {@hide}
+     */
+    public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+                BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent) {
+        final IAudioService service = getService();
+        int delay = 0;
+        try {
+            delay = service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
+                state, profile, suppressNoisyIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return delay;
+    }
+
+     /**
      * Indicate A2DP device configuration has changed.
      * @param device Bluetooth device whose configuration has changed.
      * {@hide}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index bb6ae98..6c65223 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -203,5 +203,8 @@
 
     oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
 
+    int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
+            int state, int profile, boolean suppressNoisyIntent);
+
     // WARNING: read warning at top of file, it is recommended to add new methods at the end
 }
diff --git a/media/lib/remotedisplay/Android.mk b/media/lib/remotedisplay/Android.mk
index ea1ac2b..e88c0f1 100644
--- a/media/lib/remotedisplay/Android.mk
+++ b/media/lib/remotedisplay/Android.mk
@@ -22,9 +22,7 @@
 LOCAL_MODULE:= com.android.media.remotedisplay
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := \
-            $(call all-java-files-under, java) \
-            $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
 
 include $(BUILD_JAVA_LIBRARY)
 
diff --git a/media/lib/signer/Android.mk b/media/lib/signer/Android.mk
index b0d3177..69ca4d2 100644
--- a/media/lib/signer/Android.mk
+++ b/media/lib/signer/Android.mk
@@ -22,9 +22,7 @@
 LOCAL_MODULE:= com.android.mediadrm.signer
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := \
-            $(call all-java-files-under, java) \
-            $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
 
 include $(BUILD_JAVA_LIBRARY)
 
diff --git a/media/lib/tvremote/Android.mk b/media/lib/tvremote/Android.mk
index 06838c2..1ffdd62 100644
--- a/media/lib/tvremote/Android.mk
+++ b/media/lib/tvremote/Android.mk
@@ -22,9 +22,7 @@
 LOCAL_MODULE:= com.android.media.tv.remoteprovider
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := \
-            $(call all-java-files-under, java) \
-            $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
 
 LOCAL_DEX_PREOPT := false
 
@@ -45,4 +43,4 @@
 
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 
-include $(BUILD_PREBUILT)
\ No newline at end of file
+include $(BUILD_PREBUILT)
diff --git a/native/android/net.c b/native/android/net.c
index de4b90c..60296a7 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -27,7 +27,7 @@
     static const uint32_t k32BitMask = 0xffffffff;
     // This value MUST be kept in sync with the corresponding value in
     // the android.net.Network#getNetworkHandle() implementation.
-    static const uint32_t kHandleMagic = 0xfacade;
+    static const uint32_t kHandleMagic = 0xcafed00d;
 
     // Check for minimum acceptable version of the API in the low bits.
     if (handle != NETWORK_UNSPECIFIED &&
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 5785b0c..d7695ef 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -30,6 +30,13 @@
         "libandroid_runtime",
         "libskia",
     ],
+
+    arch: {
+        arm: {
+            // TODO: This is to work around b/24465209. Remove after root cause is fixed
+            ldflags: ["-Wl,--hash-style=both"],
+        },
+    },
 }
 
 // The headers module is in frameworks/native/Android.bp.
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 184e559..888fedb 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -154,16 +154,16 @@
 
     @AfterClass
     public static void enableAnimations() throws Exception {
-        if (sWindowAnimationScaleBefore != Float.NaN) {
+        if (!Float.isNaN(sWindowAnimationScaleBefore)) {
             runShellCommand(
                     "settings put global window_animation_scale " + sWindowAnimationScaleBefore);
         }
-        if (sTransitionAnimationScaleBefore != Float.NaN) {
+        if (!Float.isNaN(sTransitionAnimationScaleBefore)) {
             runShellCommand(
                     "settings put global transition_animation_scale " +
                             sTransitionAnimationScaleBefore);
         }
-        if (sAnimatiorDurationScaleBefore != Float.NaN) {
+        if (!Float.isNaN(sAnimatiorDurationScaleBefore)) {
             runShellCommand(
                     "settings put global animator_duration_scale " + sAnimatiorDurationScaleBefore);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 0946181..853cbba 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -129,14 +129,18 @@
 
     public boolean connect(BluetoothDevice device) {
         if (mService == null) return false;
-        List<BluetoothDevice> sinks = getConnectedDevices();
-        if (sinks != null) {
-            for (BluetoothDevice sink : sinks) {
-                if (sink.equals(device)) {
-                    Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
-                    continue;
+        int max_connected_devices = mLocalAdapter.getMaxConnectedAudioDevices();
+        if (max_connected_devices == 1) {
+            // Original behavior: disconnect currently connected device
+            List<BluetoothDevice> sinks = getConnectedDevices();
+            if (sinks != null) {
+                for (BluetoothDevice sink : sinks) {
+                    if (sink.equals(device)) {
+                        Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
+                        continue;
+                    }
+                    mService.disconnect(sink);
                 }
-                mService.disconnect(sink);
             }
         }
         return mService.connect(device);
@@ -158,6 +162,16 @@
         return mService.getConnectionState(device);
     }
 
+    public boolean setActiveDevice(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.setActiveDevice(device);
+    }
+
+    public BluetoothDevice getActiveDevice() {
+        if (mService == null) return null;
+        return mService.getActiveDevice();
+    }
+
     public boolean isPreferred(BluetoothDevice device) {
         if (mService == null) return false;
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
@@ -181,8 +195,8 @@
     boolean isA2dpPlaying() {
         if (mService == null) return false;
         List<BluetoothDevice> sinks = mService.getConnectedDevices();
-        if (!sinks.isEmpty()) {
-            if (mService.isA2dpPlaying(sinks.get(0))) {
+        for (BluetoothDevice device : sinks) {
+            if (mService.isA2dpPlaying(device)) {
                 return true;
             }
         }
@@ -206,8 +220,8 @@
             return true;
         }
         BluetoothCodecConfig codecConfig = null;
-        if (mServiceWrapper.getCodecStatus() != null) {
-            codecConfig = mServiceWrapper.getCodecStatus().getCodecConfig();
+        if (mServiceWrapper.getCodecStatus(device) != null) {
+            codecConfig = mServiceWrapper.getCodecStatus(device).getCodecConfig();
         }
         if (codecConfig != null)  {
             return !codecConfig.isMandatoryCodec();
@@ -225,9 +239,9 @@
             return;
         }
         if (enabled) {
-            mService.enableOptionalCodecs();
+            mService.enableOptionalCodecs(device);
         } else {
-            mService.disableOptionalCodecs();
+            mService.disableOptionalCodecs(device);
         }
     }
 
@@ -240,8 +254,8 @@
         // We want to get the highest priority codec, since that's the one that will be used with
         // this device, and see if it is high-quality (ie non-mandatory).
         BluetoothCodecConfig[] selectable = null;
-        if (mServiceWrapper.getCodecStatus() != null) {
-            selectable = mServiceWrapper.getCodecStatus().getCodecsSelectableCapabilities();
+        if (mServiceWrapper.getCodecStatus(device) != null) {
+            selectable = mServiceWrapper.getCodecStatus(device).getCodecsSelectableCapabilities();
             // To get the highest priority, we sort in reverse.
             Arrays.sort(selectable,
                     (a, b) -> {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
index aa3e835..dace1bb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
@@ -39,7 +39,7 @@
     /**
      * Wraps {@code BluetoothA2dp.getCodecStatus}
      */
-    public BluetoothCodecStatus getCodecStatus();
+    public BluetoothCodecStatus getCodecStatus(BluetoothDevice device);
 
     /**
      * Wraps {@code BluetoothA2dp.supportsOptionalCodecs}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java
index 14fa796..c49bb98 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java
@@ -41,8 +41,8 @@
     }
 
     @Override
-    public BluetoothCodecStatus getCodecStatus() {
-        return mService.getCodecStatus();
+    public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
+        return mService.getCodecStatus(device);
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 4c41b49..ac3599c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -28,4 +28,5 @@
     void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
     void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
     void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
+    void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index f57d02b..3cda9c9 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -16,9 +16,12 @@
 
 package com.android.settingslib.bluetooth;
 
+import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -31,6 +34,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -106,6 +110,12 @@
         // Dock event broadcasts
         addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
 
+        // Active device broadcasts
+        addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED,
+                   new ActiveDeviceChangedHandler());
+        addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
+                   new ActiveDeviceChangedHandler());
+
         mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
         mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
     }
@@ -409,4 +419,35 @@
 
         return deviceAdded;
     }
+
+    private class ActiveDeviceChangedHandler implements Handler {
+        @Override
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            String action = intent.getAction();
+            if (action == null) {
+                Log.w(TAG, "ActiveDeviceChangedHandler: action is null");
+                return;
+            }
+            CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
+            int bluetoothProfile = 0;
+            if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
+                bluetoothProfile = BluetoothProfile.A2DP;
+            } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
+                bluetoothProfile = BluetoothProfile.HEADSET;
+            } else {
+                Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
+                return;
+            }
+            dispatchActiveDeviceChanged(activeDevice, bluetoothProfile);
+        }
+    }
+
+    private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+                                             int bluetoothProfile) {
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
+            }
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9caff10..fb0f75b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -105,6 +105,10 @@
     private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
     private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
 
+    // Active device state
+    private boolean mIsActiveDeviceA2dp = false;
+    private boolean mIsActiveDeviceHeadset = false;
+
     /**
      * Describes the current device and profile for logging.
      *
@@ -156,6 +160,7 @@
             mRemovedProfiles.add(profile);
             mLocalNapRoleConnected = false;
         }
+        fetchActiveDevices();
     }
 
     CachedBluetoothDevice(Context context,
@@ -359,6 +364,7 @@
         fetchName();
         fetchBtClass();
         updateProfiles();
+        fetchActiveDevices();
         migratePhonebookPermissionChoice();
         migrateMessagePermissionChoice();
         fetchMessageRejectionCount();
@@ -454,6 +460,33 @@
         return mDevice.getBondState();
     }
 
+    /**
+     * Set the device status as active or non-active per Bluetooth profile.
+     *
+     * @param isActive true if the device is active
+     * @param bluetoothProfile the Bluetooth profile
+     */
+    public void setActiveDevice(boolean isActive, int bluetoothProfile) {
+        boolean changed = false;
+        switch (bluetoothProfile) {
+        case BluetoothProfile.A2DP:
+            changed = (mIsActiveDeviceA2dp != isActive);
+            mIsActiveDeviceA2dp = isActive;
+            break;
+        case BluetoothProfile.HEADSET:
+            changed = (mIsActiveDeviceHeadset != isActive);
+            mIsActiveDeviceHeadset = isActive;
+            break;
+        default:
+            Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
+                    " isActive " + isActive);
+            break;
+        }
+        if (changed) {
+            dispatchAttributesChanged();
+        }
+    }
+
     void setRssi(short rssi) {
         if (mRssi != rssi) {
             mRssi = rssi;
@@ -529,6 +562,17 @@
         return true;
     }
 
+    private void fetchActiveDevices() {
+        A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+        if (a2dpProfile != null) {
+            mIsActiveDeviceA2dp = mDevice.equals(a2dpProfile.getActiveDevice());
+        }
+        HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+        if (headsetProfile != null) {
+            mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
+        }
+    }
+
     /**
      * Refreshes the UI for the BT class, including fetching the latest value
      * for the class.
@@ -896,37 +940,60 @@
                     com.android.settingslib.Utils.formatPercentage(batteryLevel);
         }
 
+        // TODO: A temporary workaround solution using string description the device is active.
+        // Issue tracked by b/72317067 .
+        // An alternative solution would be visual indication.
+        // Intentionally not adding the strings to strings.xml for now:
+        //  1) If this is just a short-term solution, no need to waste translation effort
+        //  2) The number of strings with all possible combinations becomes enormously large.
+        // If string description becomes part of the final solution, we MUST NOT
+        // concatenate the strings here: this does not translate well.
+        String activeString = null;
+        if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
+            activeString = ", active";
+        } else {
+            if (mIsActiveDeviceA2dp) {
+                activeString = ", active(media)";
+            }
+            if (mIsActiveDeviceHeadset) {
+                activeString = ", active(phone)";
+            }
+        }
+        if (activeString == null) activeString = "";
+
         if (profileConnected) {
             if (a2dpNotConnected && hfpNotConnected) {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(
                             R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp);
+                    return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp) +
+                        activeString;
                 }
 
             } else if (a2dpNotConnected) {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_a2dp);
+                    return mContext.getString(R.string.bluetooth_connected_no_a2dp) + activeString;
                 }
 
             } else if (hfpNotConnected) {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_headset);
+                    return mContext.getString(R.string.bluetooth_connected_no_headset)
+                          + activeString;
                 }
             } else {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(R.string.bluetooth_connected_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected);
+                    return mContext.getString(R.string.bluetooth_connected) + activeString;
                 }
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index d45fe1a..ee12191 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -153,6 +153,16 @@
         return BluetoothProfile.STATE_DISCONNECTED;
     }
 
+    public boolean setActiveDevice(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.setActiveDevice(device);
+    }
+
+    public BluetoothDevice getActiveDevice() {
+        if (mService == null) return null;
+        return mService.getActiveDevice();
+    }
+
     public boolean isPreferred(BluetoothDevice device) {
         if (mService == null) return false;
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 22674cb..cda4e45 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -239,4 +239,8 @@
     public BluetoothDevice getRemoteDevice(String address) {
         return mAdapter.getRemoteDevice(address);
     }
+
+    public int getMaxConnectedAudioDevices() {
+        return mAdapter.getMaxConnectedAudioDevices();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index 4a73c1b..8761807 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -122,7 +122,7 @@
         when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
                 BluetoothProfile.STATE_CONNECTED);
         BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
-        when(mBluetoothA2dpWrapper.getCodecStatus()).thenReturn(status);
+        when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
         BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
         when(status.getCodecConfig()).thenReturn(config);
         when(config.isMandatoryCodec()).thenReturn(false);
@@ -185,7 +185,7 @@
         BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
         BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
         BluetoothCodecConfig[] configs = {config};
-        when(mBluetoothA2dpWrapper.getCodecStatus()).thenReturn(status);
+        when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
         when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
 
         when(config.isMandatoryCodec()).thenReturn(true);
@@ -200,7 +200,7 @@
         BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
         BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
         BluetoothCodecConfig[] configs = {config};
-        when(mBluetoothA2dpWrapper.getCodecStatus()).thenReturn(status);
+        when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
         when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
 
         when(config.isMandatoryCodec()).thenReturn(false);
diff --git a/packages/SystemUI/res/layout/back.xml b/packages/SystemUI/res/layout/back.xml
index 43bec91..6843db9 100644
--- a/packages/SystemUI/res/layout/back.xml
+++ b/packages/SystemUI/res/layout/back.xml
@@ -24,8 +24,8 @@
     systemui:keyCode="4"
     android:scaleType="fitCenter"
     android:contentDescription="@string/accessibility_back"
-    android:paddingTop="15dp"
-    android:paddingBottom="15dp"
+    android:paddingTop="@dimen/home_padding"
+    android:paddingBottom="@dimen/home_padding"
     android:paddingStart="@dimen/navigation_key_padding"
     android:paddingEnd="@dimen/navigation_key_padding"
     />
diff --git a/packages/SystemUI/res/layout/recent_apps.xml b/packages/SystemUI/res/layout/recent_apps.xml
index c84d280..6b08cea 100644
--- a/packages/SystemUI/res/layout/recent_apps.xml
+++ b/packages/SystemUI/res/layout/recent_apps.xml
@@ -23,8 +23,8 @@
     android:layout_weight="0"
     android:scaleType="fitCenter"
     android:contentDescription="@string/accessibility_recent"
-    android:paddingTop="15dp"
-    android:paddingBottom="15dp"
+    android:paddingTop="@dimen/home_padding"
+    android:paddingBottom="@dimen/home_padding"
     android:paddingStart="@dimen/navigation_key_padding"
     android:paddingEnd="@dimen/navigation_key_padding"
     />
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index c8ffe8f..a16ea70 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -98,4 +98,7 @@
 
     <!-- The offsets the tasks animate from when recents is launched while docking -->
     <dimen name="recents_task_stack_animation_launched_while_docking_offset">192dp</dimen>
+
+    <!-- Home button padding for sizing -->
+    <dimen name="home_padding">0dp</dimen>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 4b775a5..b8411e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -608,6 +608,9 @@
         public void onScanningStateChanged(boolean started) { }
         @Override
         public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
+        @Override
+        public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+                                          int bluetoothProfile) { }
     }
 
     private final class BluetoothErrorListener implements Utils.ErrorListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 3b15c2b..fcf084b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -276,6 +276,9 @@
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
     }
 
+    @Override
+    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
+
     private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
         ActuallyCachedState state = mCachedState.get(device);
         if (state == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
index 2951943..2ede327 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
@@ -74,17 +74,9 @@
         }
     }
 
-    private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
+    private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
         @Override
-        public void onUidRulesChanged(int uid, int uidRules) throws RemoteException {
-        }
-
-        @Override
-        public void onMeteredIfacesChanged(String[] strings) throws RemoteException {
-        }
-
-        @Override
-        public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
+        public void onRestrictBackgroundChanged(final boolean isDataSaving) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -92,10 +84,6 @@
                 }
             });
         }
-
-        @Override
-        public void onUidPoliciesChanged(int uid, int uidPolicies) throws RemoteException {
-        }
     };
 
 }
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
old mode 100644
new mode 100755
index e970367..dc2707b
--- a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
+++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
@@ -22,11 +22,15 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -216,7 +220,27 @@
                 intent.setClassName(mContext, lastapp.className);
                 intent.setComponent(new ComponentName(lastapp.packageName,
                         lastapp.className));
-                if (mContext.startService(intent) == null) {
+                PackageManager pm = mContext.getPackageManager();
+                PowerManager powerManager =
+                        (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+                try {
+                    ApplicationInfo appInfo = pm.getApplicationInfo(lastapp.packageName, 0);
+                    if (appInfo.targetSdkVersion < Build.VERSION_CODES.O ||
+                            powerManager.isIgnoringBatteryOptimizations(lastapp.packageName)) {
+                        if (mContext.startService(intent) == null) {
+                            Log.w(LOG_TAG, "invalid name " +
+                                    lastapp.packageName + "/" + lastapp.className);
+                            return WapPushManagerParams.INVALID_RECEIVER_NAME;
+                        }
+                    } else {
+                        if (mContext.startForegroundService(intent) == null) {
+                            Log.w(LOG_TAG, "invalid name " +
+                                    lastapp.packageName + "/" + lastapp.className);
+                            return WapPushManagerParams.INVALID_RECEIVER_NAME;
+                        }
+                    }
+
+                } catch (NameNotFoundException e) {
                     Log.w(LOG_TAG, "invalid name " +
                             lastapp.packageName + "/" + lastapp.className);
                     return WapPushManagerParams.INVALID_RECEIVER_NAME;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d9713a5..2077790 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -60,6 +60,7 @@
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.util.DumpUtils;
 import com.android.server.pm.UserRestrictionsUtils;
 
@@ -415,9 +416,14 @@
 
         int systemUiUid = -1;
         try {
-            systemUiUid = mContext.getPackageManager()
-                    .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
-                            UserHandle.USER_SYSTEM);
+            // Check if device is configured with no home screen, which implies no SystemUI.
+            boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
+            if (!noHome) {
+                systemUiUid = mContext.getPackageManager()
+                        .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
+                                UserHandle.USER_SYSTEM);
+            }
+            Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
         } catch (PackageManager.NameNotFoundException e) {
             // Some platforms, such as wearables do not have a system ui.
             Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
@@ -433,10 +439,17 @@
                 Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
     }
 
+    private boolean supportBluetoothPersistedState() {
+        return mContext.getResources().getBoolean(R.bool.config_supportBluetoothPersistedState);
+    }
+
     /**
      *  Returns true if the Bluetooth saved state is "on"
      */
     private boolean isBluetoothPersistedStateOn() {
+        if (!supportBluetoothPersistedState()) {
+            return false;
+        }
         int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
         if (DBG) {
             Slog.d(TAG, "Bluetooth persisted state: " + state);
@@ -448,6 +461,9 @@
      *  Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH
      */
     private boolean isBluetoothPersistedStateOnBluetooth() {
+        if (!supportBluetoothPersistedState()) {
+            return false;
+        }
         return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON,
                 BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f34730c..bff5c10 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -30,6 +30,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 
@@ -63,6 +64,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkMisc;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
@@ -104,6 +106,7 @@
 import android.security.KeyStore;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.LocalLog.ReadOnlyLocalLog;
 import android.util.Log;
@@ -131,6 +134,7 @@
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
 import com.android.server.connectivity.DnsManager;
+import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.LingerMonitor;
@@ -172,6 +176,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
@@ -225,7 +230,11 @@
     @GuardedBy("mVpns")
     private final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
 
+    // TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
+    // a direct call to LockdownVpnTracker.isEnabled().
+    @GuardedBy("mVpns")
     private boolean mLockdownEnabled;
+    @GuardedBy("mVpns")
     private LockdownVpnTracker mLockdownTracker;
 
     final private Context mContext;
@@ -395,6 +404,9 @@
      */
     private static final int EVENT_REVALIDATE_NETWORK = 36;
 
+    // Handle changes in Private DNS settings.
+    private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;
+
     private static String eventName(int what) {
         return sMagicDecoderRing.get(what, Integer.toString(what));
     }
@@ -444,8 +456,8 @@
     private LingerMonitor mLingerMonitor;
 
     // sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
-    private final static int MIN_NET_ID = 100; // some reserved marks
-    private final static int MAX_NET_ID = 65535;
+    private static final int MIN_NET_ID = 100; // some reserved marks
+    private static final int MAX_NET_ID = 65535 - 0x0400; // Top 1024 bits reserved by IpSecService
     private int mNextNetId = MIN_NET_ID;
 
     // sequence number of NetworkRequests
@@ -700,12 +712,12 @@
         mSystemProperties = getSystemProperties();
 
         mMetricsLog = logger;
-        mDefaultRequest = createInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
+        mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
         NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
         mNetworkRequests.put(mDefaultRequest, defaultNRI);
         mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
 
-        mDefaultMobileDataRequest = createInternetRequestForTransport(
+        mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
                 NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
 
         mHandlerThread = new HandlerThread("ConnectivityServiceThread");
@@ -859,6 +871,7 @@
         mMultinetworkPolicyTracker.start();
 
         mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+        registerPrivateDnsSettingsCallbacks();
     }
 
     private Tethering makeTethering() {
@@ -869,7 +882,7 @@
                 deps);
     }
 
-    private NetworkRequest createInternetRequestForTransport(
+    private NetworkRequest createDefaultInternetRequestForTransport(
             int transportType, NetworkRequest.Type type) {
         NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
@@ -919,6 +932,12 @@
                 EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
     }
 
+    private void registerPrivateDnsSettingsCallbacks() {
+        for (Uri u : DnsManager.getPrivateDnsSettingsUris()) {
+            mSettingsObserver.observe(u, EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
+        }
+    }
+
     private synchronized int nextNetworkRequestId() {
         return mNextNetworkRequestId++;
     }
@@ -974,9 +993,9 @@
     }
 
     private Network[] getVpnUnderlyingNetworks(int uid) {
-        if (!mLockdownEnabled) {
-            int user = UserHandle.getUserId(uid);
-            synchronized (mVpns) {
+        synchronized (mVpns) {
+            if (!mLockdownEnabled) {
+                int user = UserHandle.getUserId(uid);
                 Vpn vpn = mVpns.get(user);
                 if (vpn != null && vpn.appliesToUid(uid)) {
                     return vpn.getUnderlyingNetworks();
@@ -1064,8 +1083,10 @@
         if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, ignoreBlocked)) {
             state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
         }
-        if (mLockdownTracker != null) {
-            mLockdownTracker.augmentNetworkInfo(state.networkInfo);
+        synchronized (mVpns) {
+            if (mLockdownTracker != null) {
+                mLockdownTracker.augmentNetworkInfo(state.networkInfo);
+            }
         }
     }
 
@@ -1230,8 +1251,8 @@
             result.put(nai.network, nc);
         }
 
-        if (!mLockdownEnabled) {
-            synchronized (mVpns) {
+        synchronized (mVpns) {
+            if (!mLockdownEnabled) {
                 Vpn vpn = mVpns.get(userId);
                 if (vpn != null) {
                     Network[] networks = vpn.getUnderlyingNetworks();
@@ -1239,7 +1260,11 @@
                         for (Network network : networks) {
                             nai = getNetworkAgentInfoForNetwork(network);
                             nc = getNetworkCapabilitiesInternal(nai);
+                            // nc is a copy of the capabilities in nai, so it's fine to mutate it
+                            // TODO : don't remove the UIDs when communicating with processes
+                            // that have the NETWORK_SETTINGS permission.
                             if (nc != null) {
+                                nc.setSingleUid(userId);
                                 result.put(network, nc);
                             }
                         }
@@ -1461,15 +1486,12 @@
         return true;
     }
 
-    private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
+    private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
         @Override
         public void onUidRulesChanged(int uid, int uidRules) {
             // TODO: notify UID when it has requested targeted updates
         }
         @Override
-        public void onMeteredIfacesChanged(String[] meteredIfaces) {
-        }
-        @Override
         public void onRestrictBackgroundChanged(boolean restrictBackground) {
             // TODO: relocate this specific callback in Tethering.
             if (restrictBackground) {
@@ -1477,9 +1499,6 @@
                 mTethering.untetherAll();
             }
         }
-        @Override
-        public void onUidPoliciesChanged(int uid, int uidPolicies) {
-        }
     };
 
     /**
@@ -1557,9 +1576,11 @@
     }
 
     private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
-        if (mLockdownTracker != null) {
-            info = new NetworkInfo(info);
-            mLockdownTracker.augmentNetworkInfo(info);
+        synchronized (mVpns) {
+            if (mLockdownTracker != null) {
+                info = new NetworkInfo(info);
+                mLockdownTracker.augmentNetworkInfo(info);
+            }
         }
 
         Intent intent = new Intent(bcastType);
@@ -2036,24 +2057,6 @@
                     if (score != null) updateNetworkScore(nai, score.intValue());
                     break;
                 }
-                case NetworkAgent.EVENT_UID_RANGES_ADDED: {
-                    try {
-                        mNetd.addVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
-                    } catch (Exception e) {
-                        // Never crash!
-                        loge("Exception in addVpnUidRanges: " + e);
-                    }
-                    break;
-                }
-                case NetworkAgent.EVENT_UID_RANGES_REMOVED: {
-                    try {
-                        mNetd.removeVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
-                    } catch (Exception e) {
-                        // Never crash!
-                        loge("Exception in removeVpnUidRanges: " + e);
-                    }
-                    break;
-                }
                 case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
                     if (nai.everConnected && !nai.networkMisc.explicitlySelected) {
                         loge("ERROR: already-connected network explicitly selected.");
@@ -2078,36 +2081,59 @@
                     synchronized (mNetworkForNetId) {
                         nai = mNetworkForNetId.get(msg.arg2);
                     }
-                    if (nai != null) {
-                        final boolean valid =
-                                (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
-                        final boolean wasValidated = nai.lastValidated;
-                        final boolean wasDefault = isDefaultNetwork(nai);
-                        if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
-                                (msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
-                        if (valid != nai.lastValidated) {
-                            if (wasDefault) {
-                                metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
-                                        SystemClock.elapsedRealtime(), valid);
-                            }
-                            final int oldScore = nai.getCurrentScore();
-                            nai.lastValidated = valid;
-                            nai.everValidated |= valid;
-                            updateCapabilities(oldScore, nai, nai.networkCapabilities);
-                            // If score has changed, rebroadcast to NetworkFactories. b/17726566
-                            if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+                    if (nai == null) break;
+
+                    final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+                    final boolean wasValidated = nai.lastValidated;
+                    final boolean wasDefault = isDefaultNetwork(nai);
+
+                    final PrivateDnsConfig privateDnsCfg = (msg.obj instanceof PrivateDnsConfig)
+                            ? (PrivateDnsConfig) msg.obj : null;
+                    final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
+
+                    final boolean reevaluationRequired;
+                    final String logMsg;
+                    if (valid) {
+                        reevaluationRequired = updatePrivateDns(nai, privateDnsCfg);
+                        logMsg = (DBG && (privateDnsCfg != null))
+                                 ? " with " + privateDnsCfg.toString() : "";
+                    } else {
+                        reevaluationRequired = false;
+                        logMsg = (DBG && !TextUtils.isEmpty(redirectUrl))
+                                 ? " with redirect to " + redirectUrl : "";
+                    }
+                    if (DBG) {
+                        log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
+                    }
+                    // If there is a change in Private DNS configuration,
+                    // trigger reevaluation of the network to test it.
+                    if (reevaluationRequired) {
+                        nai.networkMonitor.sendMessage(
+                                NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
+                        break;
+                    }
+                    if (valid != nai.lastValidated) {
+                        if (wasDefault) {
+                            metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
+                                    SystemClock.elapsedRealtime(), valid);
                         }
-                        updateInetCondition(nai);
-                        // Let the NetworkAgent know the state of its network
-                        Bundle redirectUrlBundle = new Bundle();
-                        redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, (String)msg.obj);
-                        nai.asyncChannel.sendMessage(
-                                NetworkAgent.CMD_REPORT_NETWORK_STATUS,
-                                (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
-                                0, redirectUrlBundle);
-                         if (wasValidated && !nai.lastValidated) {
-                             handleNetworkUnvalidated(nai);
-                         }
+                        final int oldScore = nai.getCurrentScore();
+                        nai.lastValidated = valid;
+                        nai.everValidated |= valid;
+                        updateCapabilities(oldScore, nai, nai.networkCapabilities);
+                        // If score has changed, rebroadcast to NetworkFactories. b/17726566
+                        if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+                    }
+                    updateInetCondition(nai);
+                    // Let the NetworkAgent know the state of its network
+                    Bundle redirectUrlBundle = new Bundle();
+                    redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
+                    nai.asyncChannel.sendMessage(
+                            NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+                            (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
+                            0, redirectUrlBundle);
+                    if (wasValidated && !nai.lastValidated) {
+                        handleNetworkUnvalidated(nai);
                     }
                     break;
                 }
@@ -2147,6 +2173,21 @@
                     }
                     break;
                 }
+                case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
+                    final NetworkAgentInfo nai;
+                    synchronized (mNetworkForNetId) {
+                        nai = mNetworkForNetId.get(msg.arg2);
+                    }
+                    if (nai == null) break;
+
+                    final PrivateDnsConfig cfg = (PrivateDnsConfig) msg.obj;
+                    final boolean reevaluationRequired = updatePrivateDns(nai, cfg);
+                    if (nai.lastValidated && reevaluationRequired) {
+                        nai.networkMonitor.sendMessage(
+                                NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
+                    }
+                    break;
+                }
             }
             return true;
         }
@@ -2182,6 +2223,63 @@
         }
     }
 
+    private void handlePrivateDnsSettingsChanged() {
+        final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
+
+        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            // Private DNS only ever applies to networks that might provide
+            // Internet access and therefore also require validation.
+            if (!NetworkMonitor.isValidationRequired(
+                    mDefaultRequest.networkCapabilities, nai.networkCapabilities)) {
+                continue;
+            }
+
+            // Notify the NetworkMonitor thread in case it needs to cancel or
+            // schedule DNS resolutions. If a DNS resolution is required the
+            // result will be sent back to us.
+            nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
+
+            if (!cfg.inStrictMode()) {
+                // No strict mode hostname DNS resolution needed, so just update
+                // DNS settings directly. In opportunistic and "off" modes this
+                // just reprograms netd with the network-supplied DNS servers
+                // (and of course the boolean of whether or not to attempt TLS).
+                //
+                // TODO: Consider code flow parity with strict mode, i.e. having
+                // NetworkMonitor relay the PrivateDnsConfig back to us and then
+                // performing this call at that time.
+                updatePrivateDns(nai, cfg);
+            }
+        }
+    }
+
+    private boolean updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
+        final boolean reevaluationRequired = true;
+        final boolean dontReevaluate = false;
+
+        final PrivateDnsConfig oldCfg = mDnsManager.updatePrivateDns(nai.network, newCfg);
+        updateDnses(nai.linkProperties, null, nai.network.netId);
+
+        if (newCfg == null) {
+            if (oldCfg == null) return dontReevaluate;
+            return oldCfg.useTls ? reevaluationRequired : dontReevaluate;
+        }
+
+        if (oldCfg == null) {
+            return newCfg.useTls ? reevaluationRequired : dontReevaluate;
+        }
+
+        if (oldCfg.useTls != newCfg.useTls) {
+            return reevaluationRequired;
+        }
+
+        if (newCfg.inStrictMode() && !Objects.equals(oldCfg.hostname, newCfg.hostname)) {
+            return reevaluationRequired;
+        }
+
+        return dontReevaluate;
+    }
+
     private void updateLingerState(NetworkAgentInfo nai, long now) {
         // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
         // 2. If the network was lingering and there are now requests, unlinger it.
@@ -2316,6 +2414,7 @@
                 } catch (Exception e) {
                     loge("Exception removing network: " + e);
                 }
+                mDnsManager.removeNetwork(nai.network);
             }
             synchronized (mNetworkForNetId) {
                 mNetIdInUse.delete(nai.network.netId);
@@ -2472,6 +2571,7 @@
     private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
         nri.unlinkDeathRecipient();
         mNetworkRequests.remove(nri.request);
+
         synchronized (mUidToNetworkRequestCount) {
             int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
             if (requests < 1) {
@@ -2484,6 +2584,7 @@
                 mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
             }
         }
+
         mNetworkRequestInfoLogs.log("RELEASE " + nri);
         if (nri.request.isRequest()) {
             boolean wasKept = false;
@@ -2860,6 +2961,9 @@
                     handleReportNetworkConnectivity((Network) msg.obj, msg.arg1, toBool(msg.arg2));
                     break;
                 }
+                case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
+                    handlePrivateDnsSettingsChanged();
+                    break;
             }
         }
     }
@@ -3406,9 +3510,9 @@
     public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
             int userId) {
         enforceCrossUserPermission(userId);
-        throwIfLockdownEnabled();
 
         synchronized (mVpns) {
+            throwIfLockdownEnabled();
             Vpn vpn = mVpns.get(userId);
             if (vpn != null) {
                 return vpn.prepare(oldPackage, newPackage);
@@ -3452,9 +3556,9 @@
      */
     @Override
     public ParcelFileDescriptor establishVpn(VpnConfig config) {
-        throwIfLockdownEnabled();
         int user = UserHandle.getUserId(Binder.getCallingUid());
         synchronized (mVpns) {
+            throwIfLockdownEnabled();
             return mVpns.get(user).establish(config);
         }
     }
@@ -3465,13 +3569,13 @@
      */
     @Override
     public void startLegacyVpn(VpnProfile profile) {
-        throwIfLockdownEnabled();
+        int user = UserHandle.getUserId(Binder.getCallingUid());
         final LinkProperties egress = getActiveLinkProperties();
         if (egress == null) {
             throw new IllegalStateException("Missing active network connection");
         }
-        int user = UserHandle.getUserId(Binder.getCallingUid());
         synchronized (mVpns) {
+            throwIfLockdownEnabled();
             mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
         }
     }
@@ -3497,11 +3601,11 @@
     @Override
     public VpnInfo[] getAllVpnInfo() {
         enforceConnectivityInternalPermission();
-        if (mLockdownEnabled) {
-            return new VpnInfo[0];
-        }
-
         synchronized (mVpns) {
+            if (mLockdownEnabled) {
+                return new VpnInfo[0];
+            }
+
             List<VpnInfo> infoList = new ArrayList<>();
             for (int i = 0; i < mVpns.size(); i++) {
                 VpnInfo info = createVpnInfo(mVpns.valueAt(i));
@@ -3566,33 +3670,33 @@
             return false;
         }
 
-        // Tear down existing lockdown if profile was removed
-        mLockdownEnabled = LockdownVpnTracker.isEnabled();
-        if (mLockdownEnabled) {
-            byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
-            if (profileTag == null) {
-                Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
-                return false;
-            }
-            String profileName = new String(profileTag);
-            final VpnProfile profile = VpnProfile.decode(
-                    profileName, mKeyStore.get(Credentials.VPN + profileName));
-            if (profile == null) {
-                Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
-                setLockdownTracker(null);
-                return true;
-            }
-            int user = UserHandle.getUserId(Binder.getCallingUid());
-            synchronized (mVpns) {
+        synchronized (mVpns) {
+            // Tear down existing lockdown if profile was removed
+            mLockdownEnabled = LockdownVpnTracker.isEnabled();
+            if (mLockdownEnabled) {
+                byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
+                if (profileTag == null) {
+                    Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
+                    return false;
+                }
+                String profileName = new String(profileTag);
+                final VpnProfile profile = VpnProfile.decode(
+                        profileName, mKeyStore.get(Credentials.VPN + profileName));
+                if (profile == null) {
+                    Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
+                    setLockdownTracker(null);
+                    return true;
+                }
+                int user = UserHandle.getUserId(Binder.getCallingUid());
                 Vpn vpn = mVpns.get(user);
                 if (vpn == null) {
                     Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
                     return false;
                 }
                 setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
+            } else {
+                setLockdownTracker(null);
             }
-        } else {
-            setLockdownTracker(null);
         }
 
         return true;
@@ -3602,6 +3706,7 @@
      * Internally set new {@link LockdownVpnTracker}, shutting down any existing
      * {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
      */
+    @GuardedBy("mVpns")
     private void setLockdownTracker(LockdownVpnTracker tracker) {
         // Shutdown any existing tracker
         final LockdownVpnTracker existing = mLockdownTracker;
@@ -3616,6 +3721,7 @@
         }
     }
 
+    @GuardedBy("mVpns")
     private void throwIfLockdownEnabled() {
         if (mLockdownEnabled) {
             throw new IllegalStateException("Unavailable in lockdown mode");
@@ -3663,12 +3769,12 @@
         enforceConnectivityInternalPermission();
         enforceCrossUserPermission(userId);
 
-        // Can't set always-on VPN if legacy VPN is already in lockdown mode.
-        if (LockdownVpnTracker.isEnabled()) {
-            return false;
-        }
-
         synchronized (mVpns) {
+            // Can't set always-on VPN if legacy VPN is already in lockdown mode.
+            if (LockdownVpnTracker.isEnabled()) {
+                return false;
+            }
+
             Vpn vpn = mVpns.get(userId);
             if (vpn == null) {
                 Slog.w(TAG, "User " + userId + " has no Vpn configuration");
@@ -3844,9 +3950,9 @@
             }
             userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
             mVpns.put(userId, userVpn);
-        }
-        if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
-            updateLockdownVpn();
+            if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
+                updateLockdownVpn();
+            }
         }
     }
 
@@ -3883,11 +3989,13 @@
     }
 
     private void onUserUnlocked(int userId) {
-        // User present may be sent because of an unlock, which might mean an unlocked keystore.
-        if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
-            updateLockdownVpn();
-        } else {
-            startAlwaysOnVpn(userId);
+        synchronized (mVpns) {
+            // User present may be sent because of an unlock, which might mean an unlocked keystore.
+            if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
+                updateLockdownVpn();
+            } else {
+                startAlwaysOnVpn(userId);
+            }
         }
     }
 
@@ -4087,6 +4195,7 @@
         // the system default network.
         if (type == NetworkRequest.Type.TRACK_DEFAULT) {
             networkCapabilities = new NetworkCapabilities(mDefaultRequest.networkCapabilities);
+            networkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
             enforceAccessPermission();
         } else {
             networkCapabilities = new NetworkCapabilities(networkCapabilities);
@@ -4097,6 +4206,13 @@
             enforceMeteredApnPolicy(networkCapabilities);
         }
         ensureRequestableCapabilities(networkCapabilities);
+        // Set the UID range for this request to the single UID of the requester.
+        // This will overwrite any allowed UIDs in the requested capabilities. Though there
+        // are no visible methods to set the UIDs, an app could use reflection to try and get
+        // networks for other apps so it's essential that the UIDs are overwritten.
+        // TODO : don't forcefully set the UID when communicating with processes
+        // that have the NETWORK_SETTINGS permission.
+        networkCapabilities.setSingleUid(Binder.getCallingUid());
 
         if (timeoutMs < 0) {
             throw new IllegalArgumentException("Bad timeout specified");
@@ -4170,6 +4286,9 @@
         enforceMeteredApnPolicy(networkCapabilities);
         ensureRequestableCapabilities(networkCapabilities);
         ensureValidNetworkSpecifier(networkCapabilities);
+        // TODO : don't forcefully set the UID when communicating with processes
+        // that have the NETWORK_SETTINGS permission.
+        networkCapabilities.setSingleUid(Binder.getCallingUid());
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
                 nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
@@ -4223,6 +4342,9 @@
         }
 
         NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
+        // TODO : don't forcefully set the UIDs when communicating with processes
+        // that have the NETWORK_SETTINGS permission.
+        nc.setSingleUid(Binder.getCallingUid());
         if (!ConnectivityManager.checkChangePermission(mContext)) {
             // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
             // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
@@ -4251,8 +4373,12 @@
         }
         ensureValidNetworkSpecifier(networkCapabilities);
 
-        NetworkRequest networkRequest = new NetworkRequest(
-                new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId(),
+        final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
+        // TODO : don't forcefully set the UIDs when communicating with processes
+        // that have the NETWORK_SETTINGS permission.
+        nc.setSingleUid(Binder.getCallingUid());
+
+        NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
         NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
         if (VDBG) log("pendingListenForNetwork for " + nri);
@@ -4395,6 +4521,7 @@
         NetworkInfo networkInfo = na.networkInfo;
         na.networkInfo = null;
         updateNetworkInfo(na, networkInfo);
+        updateUids(na, null, na.networkCapabilities);
     }
 
     private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
@@ -4545,11 +4672,12 @@
         final NetworkAgentInfo defaultNai = getDefaultNetwork();
         final boolean isDefaultNetwork = (defaultNai != null && defaultNai.network.netId == netId);
 
-        Collection<InetAddress> dnses = newLp.getDnsServers();
-        if (DBG) log("Setting DNS servers for network " + netId + " to " + dnses);
+        if (DBG) {
+            final Collection<InetAddress> dnses = newLp.getDnsServers();
+            log("Setting DNS servers for network " + netId + " to " + dnses);
+        }
         try {
-            mDnsManager.setDnsConfigurationForNetwork(
-                    netId, dnses, newLp.getDomains(), isDefaultNetwork);
+            mDnsManager.setDnsConfigurationForNetwork(netId, newLp, isDefaultNetwork);
         } catch (Exception e) {
             loge("Exception in setDnsConfigurationForNetwork: " + e);
         }
@@ -4567,51 +4695,67 @@
     }
 
     /**
-     * Update the NetworkCapabilities for {@code networkAgent} to {@code networkCapabilities}
-     * augmented with any stateful capabilities implied from {@code networkAgent}
-     * (e.g., validated status and captive portal status).
-     *
-     * @param oldScore score of the network before any of the changes that prompted us
-     *                 to call this function.
-     * @param nai the network having its capabilities updated.
-     * @param networkCapabilities the new network capabilities.
+     * 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,
+     * and foreground status).
      */
-    private void updateCapabilities(
-            int oldScore, NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) {
+    private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
         // Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
-        if (nai.everConnected && !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(
-                networkCapabilities)) {
-            // TODO: consider not complaining when a network agent degrade its capabilities if this
+        if (nai.everConnected &&
+                !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(nc)) {
+            // TODO: consider not complaining when a network agent degrades its capabilities if this
             // does not cause any request (that is not a listen) currently matching that agent to
             // stop being matched by the updated agent.
-            String diff = nai.networkCapabilities.describeImmutableDifferences(networkCapabilities);
+            String diff = nai.networkCapabilities.describeImmutableDifferences(nc);
             if (!TextUtils.isEmpty(diff)) {
                 Slog.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff);
             }
         }
 
         // Don't modify caller's NetworkCapabilities.
-        networkCapabilities = new NetworkCapabilities(networkCapabilities);
+        NetworkCapabilities newNc = new NetworkCapabilities(nc);
         if (nai.lastValidated) {
-            networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
+            newNc.addCapability(NET_CAPABILITY_VALIDATED);
         } else {
-            networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
+            newNc.removeCapability(NET_CAPABILITY_VALIDATED);
         }
         if (nai.lastCaptivePortalDetected) {
-            networkCapabilities.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+            newNc.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
         } else {
-            networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+            newNc.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
         }
         if (nai.isBackgroundNetwork()) {
-            networkCapabilities.removeCapability(NET_CAPABILITY_FOREGROUND);
+            newNc.removeCapability(NET_CAPABILITY_FOREGROUND);
         } else {
-            networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
+            newNc.addCapability(NET_CAPABILITY_FOREGROUND);
         }
 
-        if (Objects.equals(nai.networkCapabilities, networkCapabilities)) return;
+        return newNc;
+    }
+
+    /**
+     * Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
+     *
+     * 1. Calls mixInCapabilities to merge the passed-in NetworkCapabilities {@code nc} with the
+     *    capabilities we manage and store in {@code nai}, such as validated status and captive
+     *    portal status)
+     * 2. Takes action on the result: changes network permissions, sends CAP_CHANGED callbacks, and
+     *    potentially triggers rematches.
+     * 3. Directly informs other network stack components (NetworkStatsService, VPNs, etc. of the
+     *    change.)
+     *
+     * @param oldScore score of the network before any of the changes that prompted us
+     *                 to call this function.
+     * @param nai the network having its capabilities updated.
+     * @param nc the new network capabilities.
+     */
+    private void updateCapabilities(int oldScore, NetworkAgentInfo nai, NetworkCapabilities nc) {
+        NetworkCapabilities newNc = mixInCapabilities(nai, nc);
+
+        if (Objects.equals(nai.networkCapabilities, newNc)) return;
 
         final String oldPermission = getNetworkPermission(nai.networkCapabilities);
-        final String newPermission = getNetworkPermission(networkCapabilities);
+        final String newPermission = getNetworkPermission(newNc);
         if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
             try {
                 mNetd.setNetworkPermission(nai.network.netId, newPermission);
@@ -4623,11 +4767,12 @@
         final NetworkCapabilities prevNc;
         synchronized (nai) {
             prevNc = nai.networkCapabilities;
-            nai.networkCapabilities = networkCapabilities;
+            nai.networkCapabilities = newNc;
         }
 
-        if (nai.getCurrentScore() == oldScore &&
-                networkCapabilities.equalRequestableCapabilities(prevNc)) {
+        updateUids(nai, prevNc, newNc);
+
+        if (nai.getCurrentScore() == oldScore && newNc.equalRequestableCapabilities(prevNc)) {
             // If the requestable capabilities haven't changed, and the score hasn't changed, then
             // the change we're processing can't affect any requests, it can only affect the listens
             // on this network. We might have been called by rematchNetworkAndRequests when a
@@ -4643,15 +4788,15 @@
         // Report changes that are interesting for network statistics tracking.
         if (prevNc != null) {
             final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
-                    networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED);
+                    newNc.hasCapability(NET_CAPABILITY_NOT_METERED);
             final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
-                    networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+                    newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
             if (meteredChanged || roamingChanged) {
                 notifyIfacesChangedForNetworkStats();
             }
         }
 
-        if (!networkCapabilities.hasTransport(TRANSPORT_VPN)) {
+        if (!newNc.hasTransport(TRANSPORT_VPN)) {
             // Tell VPNs about updated capabilities, since they may need to
             // bubble those changes through.
             synchronized (mVpns) {
@@ -4663,6 +4808,34 @@
         }
     }
 
+    private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
+            NetworkCapabilities newNc) {
+        Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUids();
+        Set<UidRange> newRanges = null == newNc ? null : newNc.getUids();
+        if (null == prevRanges) prevRanges = new ArraySet<>();
+        if (null == newRanges) newRanges = new ArraySet<>();
+        final Set<UidRange> prevRangesCopy = new ArraySet<>(prevRanges);
+
+        prevRanges.removeAll(newRanges);
+        newRanges.removeAll(prevRangesCopy);
+
+        try {
+            if (!newRanges.isEmpty()) {
+                final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
+                newRanges.toArray(addedRangesArray);
+                mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray);
+            }
+            if (!prevRanges.isEmpty()) {
+                final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
+                prevRanges.toArray(removedRangesArray);
+                mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray);
+            }
+        } catch (Exception e) {
+            // Never crash!
+            loge("Exception in updateUids: " + e);
+        }
+    }
+
     public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
         if (mNetworkForNetId.get(nai.network.netId) != nai) {
             // Ignore updates for disconnected networks
@@ -4754,7 +4927,12 @@
                 break;
             }
             case ConnectivityManager.CALLBACK_CAP_CHANGED: {
-                putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
+                final NetworkCapabilities nc =
+                        new NetworkCapabilities(networkAgent.networkCapabilities);
+                // TODO : don't remove the UIDs when communicating with processes
+                // that have the NETWORK_SETTINGS permission.
+                nc.setSingleUid(nri.mUid);
+                putParcelable(bundle, nc);
                 break;
             }
             case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -4822,10 +5000,12 @@
         } catch (Exception e) {
             loge("Exception setting default network :" + e);
         }
+
         notifyLockdownVpn(newNetwork);
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
         updateTcpBufferSizes(newNetwork);
         mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
+        notifyIfacesChangedForNetworkStats();
     }
 
     private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -5175,11 +5355,13 @@
     }
 
     private void notifyLockdownVpn(NetworkAgentInfo nai) {
-        if (mLockdownTracker != null) {
-            if (nai != null && nai.isVPN()) {
-                mLockdownTracker.onVpnStateChanged(nai.networkInfo);
-            } else {
-                mLockdownTracker.onNetworkInfoChanged();
+        synchronized (mVpns) {
+            if (mLockdownTracker != null) {
+                if (nai != null && nai.isVPN()) {
+                    mLockdownTracker.onVpnStateChanged(nai.networkInfo);
+                } else {
+                    mLockdownTracker.onNetworkInfoChanged();
+                }
             }
         }
     }
@@ -5274,6 +5456,7 @@
                         }
                     }
                 }
+                updateUids(networkAgent, networkAgent.networkCapabilities, null);
             }
         } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
                 state == NetworkInfo.State.SUSPENDED) {
@@ -5397,44 +5580,62 @@
     }
 
     /**
+     * Returns the list of all interfaces that could be used by network traffic that does not
+     * explicitly specify a network. This includes the default network, but also all VPNs that are
+     * currently connected.
+     *
+     * Must be called on the handler thread.
+     */
+    private Network[] getDefaultNetworks() {
+        ArrayList<Network> defaultNetworks = new ArrayList<>();
+        NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) {
+                defaultNetworks.add(nai.network);
+            }
+        }
+        return defaultNetworks.toArray(new Network[0]);
+    }
+
+    /**
      * Notify NetworkStatsService that the set of active ifaces has changed, or that one of the
      * properties tracked by NetworkStatsService on an active iface has changed.
      */
     private void notifyIfacesChangedForNetworkStats() {
         try {
-            mStatsService.forceUpdateIfaces();
+            mStatsService.forceUpdateIfaces(getDefaultNetworks());
         } catch (Exception ignored) {
         }
     }
 
     @Override
     public boolean addVpnAddress(String address, int prefixLength) {
-        throwIfLockdownEnabled();
         int user = UserHandle.getUserId(Binder.getCallingUid());
         synchronized (mVpns) {
+            throwIfLockdownEnabled();
             return mVpns.get(user).addAddress(address, prefixLength);
         }
     }
 
     @Override
     public boolean removeVpnAddress(String address, int prefixLength) {
-        throwIfLockdownEnabled();
         int user = UserHandle.getUserId(Binder.getCallingUid());
         synchronized (mVpns) {
+            throwIfLockdownEnabled();
             return mVpns.get(user).removeAddress(address, prefixLength);
         }
     }
 
     @Override
     public boolean setUnderlyingNetworksForVpn(Network[] networks) {
-        throwIfLockdownEnabled();
         int user = UserHandle.getUserId(Binder.getCallingUid());
-        boolean success;
+        final boolean success;
         synchronized (mVpns) {
+            throwIfLockdownEnabled();
             success = mVpns.get(user).setUnderlyingNetworks(networks);
         }
         if (success) {
-            notifyIfacesChangedForNetworkStats();
+            mHandler.post(() -> notifyIfacesChangedForNetworkStats());
         }
         return success;
     }
@@ -5490,31 +5691,31 @@
                     setAlwaysOnVpnPackage(userId, null, false);
                     setVpnPackageAuthorization(alwaysOnPackage, userId, false);
                 }
-            }
 
-            // Turn Always-on VPN off
-            if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    mKeyStore.delete(Credentials.LOCKDOWN_VPN);
-                    mLockdownEnabled = false;
-                    setLockdownTracker(null);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
+                // Turn Always-on VPN off
+                if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        mKeyStore.delete(Credentials.LOCKDOWN_VPN);
+                        mLockdownEnabled = false;
+                        setLockdownTracker(null);
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
                 }
-            }
 
-            // Turn VPN off
-            VpnConfig vpnConfig = getVpnConfig(userId);
-            if (vpnConfig != null) {
-                if (vpnConfig.legacy) {
-                    prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
-                } else {
-                    // Prevent this app (packagename = vpnConfig.user) from initiating VPN connections
-                    // in the future without user intervention.
-                    setVpnPackageAuthorization(vpnConfig.user, userId, false);
+                // Turn VPN off
+                VpnConfig vpnConfig = getVpnConfig(userId);
+                if (vpnConfig != null) {
+                    if (vpnConfig.legacy) {
+                        prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
+                    } else {
+                        // Prevent this app (packagename = vpnConfig.user) from initiating
+                        // VPN connections in the future without user intervention.
+                        setVpnPackageAuthorization(vpnConfig.user, userId, false);
 
-                    prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
+                        prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 02cfe3d..fe4ac6d7 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -25,6 +25,7 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.IIpSecService;
 import android.net.INetd;
 import android.net.IpSecAlgorithm;
@@ -33,7 +34,9 @@
 import android.net.IpSecSpiResponse;
 import android.net.IpSecTransform;
 import android.net.IpSecTransformResponse;
+import android.net.IpSecTunnelInterfaceResponse;
 import android.net.IpSecUdpEncapResponse;
+import android.net.Network;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
 import android.net.util.NetdService;
@@ -49,6 +52,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -62,7 +66,6 @@
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import libcore.io.IoUtils;
 
@@ -83,7 +86,7 @@
 
     private static final String NETD_SERVICE_NAME = "netd";
     private static final int[] DIRECTIONS =
-            new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
+            new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN};
 
     private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
     private static final int MAX_PORT_BIND_ATTEMPTS = 10;
@@ -99,15 +102,16 @@
 
     static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
     static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
+    static final String TUNNEL_INTERFACE_PREFIX = "ipsec";
 
     /* Binder context for this service */
     private final Context mContext;
 
     /**
-     * The next non-repeating global ID for tracking resources between users, this service,
-     * and kernel data structures. Accessing this variable is not thread safe, so it is
-     * only read or modified within blocks synchronized on IpSecService.this. We want to
-     * avoid -1 (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
+     * The next non-repeating global ID for tracking resources between users, this service, and
+     * kernel data structures. Accessing this variable is not thread safe, so it is only read or
+     * modified within blocks synchronized on IpSecService.this. We want to avoid -1
+     * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
      */
     @GuardedBy("IpSecService.this")
     private int mNextResourceId = 1;
@@ -148,7 +152,7 @@
          * resources.
          *
          * <p>References to the IResource object may be held by other RefcountedResource objects,
-         * and as such, the kernel resources and quota may not be cleaned up.
+         * and as such, the underlying resources and quota may not be cleaned up.
          */
         void invalidate() throws RemoteException;
 
@@ -298,7 +302,12 @@
         }
     }
 
-    /* Very simple counting class that looks much like a counting semaphore */
+    /**
+     * Very simple counting class that looks much like a counting semaphore
+     *
+     * <p>This class is not thread-safe, and expects that that users of this class will ensure
+     * synchronization and thread safety by holding the IpSecService.this instance lock.
+     */
     @VisibleForTesting
     static class ResourceTracker {
         private final int mMax;
@@ -341,27 +350,43 @@
 
     @VisibleForTesting
     static final class UserRecord {
-        /* Type names */
-        public static final String TYPENAME_SPI = "SecurityParameterIndex";
-        public static final String TYPENAME_TRANSFORM = "IpSecTransform";
-        public static final String TYPENAME_ENCAP_SOCKET = "UdpEncapSocket";
-
         /* Maximum number of each type of resource that a single UID may possess */
+        public static final int MAX_NUM_TUNNEL_INTERFACES = 2;
         public static final int MAX_NUM_ENCAP_SOCKETS = 2;
         public static final int MAX_NUM_TRANSFORMS = 4;
         public static final int MAX_NUM_SPIS = 8;
 
+        /**
+         * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
+         * and explicit (user) reference management.
+         *
+         * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
+         *
+         * <p>Resources are removed from this array when the user releases their explicit reference
+         * by calling one of the releaseResource() methods.
+         */
         final RefcountedResourceArray<SpiRecord> mSpiRecords =
-                new RefcountedResourceArray<>(TYPENAME_SPI);
-        final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
-
+                new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
         final RefcountedResourceArray<TransformRecord> mTransformRecords =
-                new RefcountedResourceArray<>(TYPENAME_TRANSFORM);
-        final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
-
+                new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
         final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
-                new RefcountedResourceArray<>(TYPENAME_ENCAP_SOCKET);
+                new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
+        final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
+                new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
+
+        /**
+         * Trackers for quotas for each of the OwnedResource types.
+         *
+         * <p>These trackers are separate from the resource arrays, since they are incremented and
+         * decremented at different points in time. Specifically, quota is only returned upon final
+         * resource deallocation (after all explicit and implicit references are released). Note
+         * that it is possible that calls to releaseResource() will not return the used quota if
+         * there are other resources that depend on (are parents of) the resource being released.
+         */
+        final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
+        final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
         final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
+        final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
 
         void removeSpiRecord(int resourceId) {
             mSpiRecords.remove(resourceId);
@@ -371,6 +396,10 @@
             mTransformRecords.remove(resourceId);
         }
 
+        void removeTunnelInterfaceRecord(int resourceId) {
+            mTunnelInterfaceRecords.remove(resourceId);
+        }
+
         void removeEncapSocketRecord(int resourceId) {
             mEncapSocketRecords.remove(resourceId);
         }
@@ -395,11 +424,15 @@
         }
     }
 
+    /**
+     * This class is not thread-safe, and expects that that users of this class will ensure
+     * synchronization and thread safety by holding the IpSecService.this instance lock.
+     */
     @VisibleForTesting
     static final class UserResourceTracker {
         private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
 
-        /** Never-fail getter that populates the list of UIDs as-needed */
+        /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
         public UserRecord getUserRecord(int uid) {
             checkCallerUid(uid);
 
@@ -428,18 +461,20 @@
     @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
 
     /**
-     * The KernelResourceRecord class provides a facility to cleanly and reliably track system
+     * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
      * resources. It relies on a provided resourceId that should uniquely identify the kernel
      * resource. To use this class, the user should implement the invalidate() and
      * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
-     * tracking arrays and kernel resources, respectively
+     * tracking arrays and kernel resources, respectively.
+     *
+     * <p>This class associates kernel resources with the UID that owns and controls them.
      */
-    private abstract class KernelResourceRecord implements IResource {
+    private abstract class OwnedResourceRecord implements IResource {
         final int pid;
         final int uid;
         protected final int mResourceId;
 
-        KernelResourceRecord(int resourceId) {
+        OwnedResourceRecord(int resourceId) {
             super();
             if (resourceId == INVALID_RESOURCE_ID) {
                 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
@@ -479,8 +514,6 @@
         }
     };
 
-    // TODO: Move this to right after RefcountedResource. With this here, Gerrit was showing many
-    // more things as changed.
     /**
      * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
      *
@@ -534,46 +567,56 @@
         }
     }
 
-    private final class TransformRecord extends KernelResourceRecord {
+    /**
+     * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
+     * created, the SpiRecord that originally tracked the SAs will reliquish the
+     * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
+     */
+    private final class TransformRecord extends OwnedResourceRecord {
         private final IpSecConfig mConfig;
-        private final SpiRecord[] mSpis;
+        private final SpiRecord mSpi;
         private final EncapSocketRecord mSocket;
 
         TransformRecord(
-                int resourceId, IpSecConfig config, SpiRecord[] spis, EncapSocketRecord socket) {
+                int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
             super(resourceId);
             mConfig = config;
-            mSpis = spis;
+            mSpi = spi;
             mSocket = socket;
+
+            spi.setOwnedByTransform();
         }
 
         public IpSecConfig getConfig() {
             return mConfig;
         }
 
-        public SpiRecord getSpiRecord(int direction) {
-            return mSpis[direction];
+        public SpiRecord getSpiRecord() {
+            return mSpi;
+        }
+
+        public EncapSocketRecord getSocketRecord() {
+            return mSocket;
         }
 
         /** always guarded by IpSecService#this */
         @Override
         public void freeUnderlyingResources() {
-            for (int direction : DIRECTIONS) {
-                int spi = mSpis[direction].getSpi();
-                try {
-                    mSrvConfig
-                            .getNetdInstance()
-                            .ipSecDeleteSecurityAssociation(
-                                    mResourceId,
-                                    direction,
-                                    mConfig.getLocalAddress(),
-                                    mConfig.getRemoteAddress(),
-                                    spi);
-                } catch (ServiceSpecificException e) {
-                    // FIXME: get the error code and throw is at an IOException from Errno Exception
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
-                }
+            int spi = mSpi.getSpi();
+            try {
+                mSrvConfig
+                        .getNetdInstance()
+                        .ipSecDeleteSecurityAssociation(
+                                mResourceId,
+                                mConfig.getSourceAddress(),
+                                mConfig.getDestinationAddress(),
+                                spi,
+                                mConfig.getMarkValue(),
+                                mConfig.getMarkMask());
+            } catch (ServiceSpecificException e) {
+                // FIXME: get the error code and throw is at an IOException from Errno Exception
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
             }
 
             getResourceTracker().give();
@@ -597,10 +640,8 @@
                     .append(super.toString())
                     .append(", mSocket=")
                     .append(mSocket)
-                    .append(", mSpis[OUT].mResourceId=")
-                    .append(mSpis[IpSecTransform.DIRECTION_OUT].mResourceId)
-                    .append(", mSpis[IN].mResourceId=")
-                    .append(mSpis[IpSecTransform.DIRECTION_IN].mResourceId)
+                    .append(", mSpi.mResourceId=")
+                    .append(mSpi.mResourceId)
                     .append(", mConfig=")
                     .append(mConfig)
                     .append("}");
@@ -608,45 +649,33 @@
         }
     }
 
-    private final class SpiRecord extends KernelResourceRecord {
-        private final int mDirection;
-        private final String mLocalAddress;
-        private final String mRemoteAddress;
+    /**
+     * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
+     * responsibility for cleaning up underlying resources will be passed to the TransformRecord
+     * object
+     */
+    private final class SpiRecord extends OwnedResourceRecord {
+        private final String mSourceAddress;
+        private final String mDestinationAddress;
         private int mSpi;
 
         private boolean mOwnedByTransform = false;
 
-        SpiRecord(
-                int resourceId,
-                int direction,
-                String localAddress,
-                String remoteAddress,
-                int spi) {
+        SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
             super(resourceId);
-            mDirection = direction;
-            mLocalAddress = localAddress;
-            mRemoteAddress = remoteAddress;
+            mSourceAddress = sourceAddress;
+            mDestinationAddress = destinationAddress;
             mSpi = spi;
         }
 
         /** always guarded by IpSecService#this */
         @Override
         public void freeUnderlyingResources() {
-            if (mOwnedByTransform) {
-                Log.d(TAG, "Cannot release Spi " + mSpi + ": Currently locked by a Transform");
-                // Because SPIs are "handed off" to transform, objects, they should never be
-                // freed from the SpiRecord once used in a transform. (They refer to the same SA,
-                // thus ownership and responsibility for freeing these resources passes to the
-                // Transform object). Thus, we should let the user free them without penalty once
-                // they are applied in a Transform object.
-                return;
-            }
-
             try {
                 mSrvConfig
                         .getNetdInstance()
                         .ipSecDeleteSecurityAssociation(
-                                mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi);
+                                mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
             } catch (ServiceSpecificException e) {
                 // FIXME: get the error code and throw is at an IOException from Errno Exception
             } catch (RemoteException e) {
@@ -662,6 +691,10 @@
             return mSpi;
         }
 
+        public String getDestinationAddress() {
+            return mDestinationAddress;
+        }
+
         public void setOwnedByTransform() {
             if (mOwnedByTransform) {
                 // Programming error
@@ -671,6 +704,10 @@
             mOwnedByTransform = true;
         }
 
+        public boolean getOwnedByTransform() {
+            return mOwnedByTransform;
+        }
+
         @Override
         public void invalidate() throws RemoteException {
             getUserRecord().removeSpiRecord(mResourceId);
@@ -689,12 +726,10 @@
                     .append(super.toString())
                     .append(", mSpi=")
                     .append(mSpi)
-                    .append(", mDirection=")
-                    .append(mDirection)
-                    .append(", mLocalAddress=")
-                    .append(mLocalAddress)
-                    .append(", mRemoteAddress=")
-                    .append(mRemoteAddress)
+                    .append(", mSourceAddress=")
+                    .append(mSourceAddress)
+                    .append(", mDestinationAddress=")
+                    .append(mDestinationAddress)
                     .append(", mOwnedByTransform=")
                     .append(mOwnedByTransform)
                     .append("}");
@@ -702,7 +737,173 @@
         }
     }
 
-    private final class EncapSocketRecord extends KernelResourceRecord {
+    // These values have been reserved in ConnectivityService
+    @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
+
+    @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400;
+
+    private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
+    private int mNextTunnelNetIdIndex = 0;
+
+    /**
+     * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
+     *
+     * <p>This method should only be called from Binder threads. Do not call this from within the
+     * system server as it will crash the system on failure.
+     *
+     * @return an integer key within the netId range, if successful
+     * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
+     */
+    @VisibleForTesting
+    int reserveNetId() {
+        synchronized (mTunnelNetIds) {
+            for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
+                int index = mNextTunnelNetIdIndex;
+                int netId = index + TUN_INTF_NETID_START;
+                if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
+                if (!mTunnelNetIds.get(netId)) {
+                    mTunnelNetIds.put(netId, true);
+                    return netId;
+                }
+            }
+        }
+        throw new IllegalStateException("No free netIds to allocate");
+    }
+
+    @VisibleForTesting
+    void releaseNetId(int netId) {
+        synchronized (mTunnelNetIds) {
+            mTunnelNetIds.delete(netId);
+        }
+    }
+
+    private final class TunnelInterfaceRecord extends OwnedResourceRecord {
+        private final String mInterfaceName;
+        private final Network mUnderlyingNetwork;
+
+        // outer addresses
+        private final String mLocalAddress;
+        private final String mRemoteAddress;
+
+        private final int mIkey;
+        private final int mOkey;
+
+        TunnelInterfaceRecord(
+                int resourceId,
+                String interfaceName,
+                Network underlyingNetwork,
+                String localAddr,
+                String remoteAddr,
+                int ikey,
+                int okey) {
+            super(resourceId);
+
+            mInterfaceName = interfaceName;
+            mUnderlyingNetwork = underlyingNetwork;
+            mLocalAddress = localAddr;
+            mRemoteAddress = remoteAddr;
+            mIkey = ikey;
+            mOkey = okey;
+        }
+
+        /** always guarded by IpSecService#this */
+        @Override
+        public void freeUnderlyingResources() {
+            // Calls to netd
+            //       Teardown VTI
+            //       Delete global policies
+            try {
+                mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName);
+
+                for (int direction : DIRECTIONS) {
+                    int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
+                    mSrvConfig
+                            .getNetdInstance()
+                            .ipSecDeleteSecurityPolicy(
+                                    0, direction, mLocalAddress, mRemoteAddress, mark, 0xffffffff);
+                }
+            } catch (ServiceSpecificException e) {
+                // FIXME: get the error code and throw is at an IOException from Errno Exception
+            } catch (RemoteException e) {
+                Log.e(
+                        TAG,
+                        "Failed to delete VTI with interface name: "
+                                + mInterfaceName
+                                + " and id: "
+                                + mResourceId);
+            }
+
+            getResourceTracker().give();
+            releaseNetId(mIkey);
+            releaseNetId(mOkey);
+        }
+
+        public String getInterfaceName() {
+            return mInterfaceName;
+        }
+
+        public Network getUnderlyingNetwork() {
+            return mUnderlyingNetwork;
+        }
+
+        /** Returns the local, outer address for the tunnelInterface */
+        public String getLocalAddress() {
+            return mLocalAddress;
+        }
+
+        /** Returns the remote, outer address for the tunnelInterface */
+        public String getRemoteAddress() {
+            return mRemoteAddress;
+        }
+
+        public int getIkey() {
+            return mIkey;
+        }
+
+        public int getOkey() {
+            return mOkey;
+        }
+
+        @Override
+        protected ResourceTracker getResourceTracker() {
+            return getUserRecord().mTunnelQuotaTracker;
+        }
+
+        @Override
+        public void invalidate() {
+            getUserRecord().removeTunnelInterfaceRecord(mResourceId);
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder()
+                    .append("{super=")
+                    .append(super.toString())
+                    .append(", mInterfaceName=")
+                    .append(mInterfaceName)
+                    .append(", mUnderlyingNetwork=")
+                    .append(mUnderlyingNetwork)
+                    .append(", mLocalAddress=")
+                    .append(mLocalAddress)
+                    .append(", mRemoteAddress=")
+                    .append(mRemoteAddress)
+                    .append(", mIkey=")
+                    .append(mIkey)
+                    .append(", mOkey=")
+                    .append(mOkey)
+                    .append("}")
+                    .toString();
+        }
+    }
+
+    /**
+     * Tracks a UDP encap socket, and manages cleanup paths
+     *
+     * <p>While this class does not manage non-kernel resources, race conditions around socket
+     * binding require that the service creates the encap socket, binds it and applies the socket
+     * policy before handing it to a user.
+     */
+    private final class EncapSocketRecord extends OwnedResourceRecord {
         private FileDescriptor mSocket;
         private final int mPort;
 
@@ -772,14 +973,17 @@
     /** @hide */
     @VisibleForTesting
     public IpSecService(Context context, IpSecServiceConfiguration config) {
-        this(context, config, (fd, uid) ->  {
-            try{
-                TrafficStats.setThreadStatsUid(uid);
-                TrafficStats.tagFileDescriptor(fd);
-            } finally {
-                TrafficStats.clearThreadStatsUid();
-            }
-        });
+        this(
+                context,
+                config,
+                (fd, uid) -> {
+                    try {
+                        TrafficStats.setThreadStatsUid(uid);
+                        TrafficStats.tagFileDescriptor(fd);
+                    } finally {
+                        TrafficStats.clearThreadStatsUid();
+                    }
+                });
     }
 
     /** @hide */
@@ -845,8 +1049,8 @@
      */
     private static void checkDirection(int direction) {
         switch (direction) {
-            case IpSecTransform.DIRECTION_OUT:
-            case IpSecTransform.DIRECTION_IN:
+            case IpSecManager.DIRECTION_OUT:
+            case IpSecManager.DIRECTION_IN:
                 return;
         }
         throw new IllegalArgumentException("Invalid Direction: " + direction);
@@ -855,10 +1059,8 @@
     /** Get a new SPI and maintain the reservation in the system server */
     @Override
     public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
-            int direction, String remoteAddress, int requestedSpi, IBinder binder)
-            throws RemoteException {
-        checkDirection(direction);
-        checkInetAddress(remoteAddress);
+            String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
+        checkInetAddress(destinationAddress);
         /* requestedSpi can be anything in the int range, so no check is needed. */
         checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
 
@@ -866,28 +1068,21 @@
         final int resourceId = mNextResourceId++;
 
         int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
-        String localAddress = "";
-
         try {
             if (!userRecord.mSpiQuotaTracker.isAvailable()) {
                 return new IpSecSpiResponse(
                         IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
             }
+
             spi =
                     mSrvConfig
                             .getNetdInstance()
-                            .ipSecAllocateSpi(
-                                    resourceId,
-                                    direction,
-                                    localAddress,
-                                    remoteAddress,
-                                    requestedSpi);
+                            .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi);
             Log.d(TAG, "Allocated SPI " + spi);
             userRecord.mSpiRecords.put(
                     resourceId,
                     new RefcountedResource<SpiRecord>(
-                            new SpiRecord(resourceId, direction, localAddress, remoteAddress, spi),
-                            binder));
+                            new SpiRecord(resourceId, "", destinationAddress, spi), binder));
         } catch (ServiceSpecificException e) {
             // TODO: Add appropriate checks when other ServiceSpecificException types are supported
             return new IpSecSpiResponse(
@@ -1031,28 +1226,152 @@
         releaseResource(userRecord.mEncapSocketRecords, resourceId);
     }
 
-    @VisibleForTesting
-    void validateAlgorithms(IpSecConfig config, int direction) throws IllegalArgumentException {
-            IpSecAlgorithm auth = config.getAuthentication(direction);
-            IpSecAlgorithm crypt = config.getEncryption(direction);
-            IpSecAlgorithm aead = config.getAuthenticatedEncryption(direction);
+    /**
+     * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
+     * tunnel interface and a record of its owner so that it can and must be freed when no longer
+     * needed.
+     */
+    @Override
+    public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
+            String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder) {
+        checkNotNull(binder, "Null Binder passed to createTunnelInterface");
+        checkNotNull(underlyingNetwork, "No underlying network was specified");
+        checkInetAddress(localAddr);
+        checkInetAddress(remoteAddr);
 
-            // Validate the algorithm set
-            Preconditions.checkArgument(
-                    aead != null || crypt != null || auth != null,
-                    "No Encryption or Authentication algorithms specified");
-            Preconditions.checkArgument(
-                    auth == null || auth.isAuthentication(),
-                    "Unsupported algorithm for Authentication");
-            Preconditions.checkArgument(
+        // TODO: Check that underlying network exists, and IP addresses not assigned to a different
+        //       network (b/72316676).
+
+        UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+        if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
+            return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
+        }
+
+        final int resourceId = mNextResourceId++;
+        final int ikey = reserveNetId();
+        final int okey = reserveNetId();
+        String intfName = String.format("%s%d", TUNNEL_INTERFACE_PREFIX, resourceId);
+
+        try {
+            // Calls to netd:
+            //       Create VTI
+            //       Add inbound/outbound global policies
+            //              (use reqid = 0)
+            mSrvConfig
+                    .getNetdInstance()
+                    .addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
+
+            for (int direction : DIRECTIONS) {
+                int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
+
+                mSrvConfig
+                        .getNetdInstance()
+                        .ipSecAddSecurityPolicy(
+                                0, // Use 0 for reqId
+                                direction,
+                                "",
+                                "",
+                                0,
+                                mark,
+                                0xffffffff);
+            }
+
+            userRecord.mTunnelInterfaceRecords.put(
+                    resourceId,
+                    new RefcountedResource<TunnelInterfaceRecord>(
+                            new TunnelInterfaceRecord(
+                                    resourceId,
+                                    intfName,
+                                    underlyingNetwork,
+                                    localAddr,
+                                    remoteAddr,
+                                    ikey,
+                                    okey),
+                            binder));
+            return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
+        } catch (RemoteException e) {
+            // Release keys if we got an error.
+            releaseNetId(ikey);
+            releaseNetId(okey);
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            // FIXME: get the error code and throw is at an IOException from Errno Exception
+        }
+
+        // If we make it to here, then something has gone wrong and we couldn't create a VTI.
+        // Release the keys that we reserved, and return an error status.
+        releaseNetId(ikey);
+        releaseNetId(okey);
+        return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
+    }
+
+    /**
+     * Adds a new local address to the tunnel interface. This allows packets to be sent and received
+     * from multiple local IP addresses over the same tunnel.
+     */
+    @Override
+    public synchronized void addAddressToTunnelInterface(int tunnelResourceId, String localAddr) {
+        UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+
+        // Get tunnelInterface record; if no such interface is found, will throw
+        // IllegalArgumentException
+        TunnelInterfaceRecord tunnelInterfaceInfo =
+                userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
+
+        // TODO: Add calls to netd:
+        //       Add address to TunnelInterface
+    }
+
+    /**
+     * Remove a new local address from the tunnel interface. After removal, the address will no
+     * longer be available to send from, or receive on.
+     */
+    @Override
+    public synchronized void removeAddressFromTunnelInterface(
+            int tunnelResourceId, String localAddr) {
+        UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+
+        // Get tunnelInterface record; if no such interface is found, will throw
+        // IllegalArgumentException
+        TunnelInterfaceRecord tunnelInterfaceInfo =
+                userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
+
+        // TODO: Add calls to netd:
+        //       Remove address from TunnelInterface
+    }
+
+    /**
+     * Delete a TunnelInterface that has been been allocated by and registered with the system
+     * server
+     */
+    @Override
+    public synchronized void deleteTunnelInterface(int resourceId) throws RemoteException {
+        UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+        releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
+    }
+
+    @VisibleForTesting
+    void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
+        IpSecAlgorithm auth = config.getAuthentication();
+        IpSecAlgorithm crypt = config.getEncryption();
+        IpSecAlgorithm aead = config.getAuthenticatedEncryption();
+
+        // Validate the algorithm set
+        Preconditions.checkArgument(
+                aead != null || crypt != null || auth != null,
+                "No Encryption or Authentication algorithms specified");
+        Preconditions.checkArgument(
+                auth == null || auth.isAuthentication(),
+                "Unsupported algorithm for Authentication");
+        Preconditions.checkArgument(
                 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
-            Preconditions.checkArgument(
-                    aead == null || aead.isAead(),
-                    "Unsupported algorithm for Authenticated Encryption");
-            Preconditions.checkArgument(
-                    aead == null || (auth == null && crypt == null),
-                    "Authenticated Encryption is mutually exclusive with other Authentication "
-                                    + "or Encryption algorithms");
+        Preconditions.checkArgument(
+                aead == null || aead.isAead(),
+                "Unsupported algorithm for Authenticated Encryption");
+        Preconditions.checkArgument(
+                aead == null || (auth == null && crypt == null),
+                "Authenticated Encryption is mutually exclusive with other Authentication "
+                        + "or Encryption algorithms");
     }
 
     /**
@@ -1062,29 +1381,6 @@
     private void checkIpSecConfig(IpSecConfig config) {
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
 
-        if (config.getLocalAddress() == null) {
-            throw new IllegalArgumentException("Invalid null Local InetAddress");
-        }
-
-        if (config.getRemoteAddress() == null) {
-            throw new IllegalArgumentException("Invalid null Remote InetAddress");
-        }
-
-        switch (config.getMode()) {
-            case IpSecTransform.MODE_TRANSPORT:
-                if (!config.getLocalAddress().isEmpty()) {
-                    throw new IllegalArgumentException("Non-empty Local Address");
-                }
-                // Must be valid, and not a wildcard
-                checkInetAddress(config.getRemoteAddress());
-                break;
-            case IpSecTransform.MODE_TUNNEL:
-                break;
-            default:
-                throw new IllegalArgumentException(
-                        "Invalid IpSecTransform.mode: " + config.getMode());
-        }
-
         switch (config.getEncapType()) {
             case IpSecTransform.ENCAP_NONE:
                 break;
@@ -1103,97 +1399,129 @@
                 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
         }
 
-        for (int direction : DIRECTIONS) {
-            validateAlgorithms(config, direction);
+        validateAlgorithms(config);
 
-            // Retrieve SPI record; will throw IllegalArgumentException if not found
-            userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId(direction));
+        // Retrieve SPI record; will throw IllegalArgumentException if not found
+        SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
+
+        // Check to ensure that SPI has not already been used.
+        if (s.getOwnedByTransform()) {
+            throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
+        }
+
+        // If no remote address is supplied, then use one from the SPI.
+        if (TextUtils.isEmpty(config.getDestinationAddress())) {
+            config.setDestinationAddress(s.getDestinationAddress());
+        }
+
+        // All remote addresses must match
+        if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
+            throw new IllegalArgumentException("Mismatched remote addresseses.");
+        }
+
+        // This check is technically redundant due to the chain of custody between the SPI and
+        // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
+        // the transform, this will prevent us from messing up.
+        checkInetAddress(config.getDestinationAddress());
+
+        // Require a valid source address for all transforms.
+        checkInetAddress(config.getSourceAddress());
+
+        switch (config.getMode()) {
+            case IpSecTransform.MODE_TRANSPORT:
+            case IpSecTransform.MODE_TUNNEL:
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Invalid IpSecTransform.mode: " + config.getMode());
         }
     }
 
-    /**
-     * Create a transport mode transform, which represent two security associations (one in each
-     * direction) in the kernel. The transform will be cached by the system server and must be freed
-     * when no longer needed. It is possible to free one, deleting the SA from underneath sockets
-     * that are using it, which will result in all of those sockets becoming unable to send or
-     * receive data.
-     */
-    @Override
-    public synchronized IpSecTransformResponse createTransportModeTransform(
-            IpSecConfig c, IBinder binder) throws RemoteException {
-        checkIpSecConfig(c);
-        checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
-        final int resourceId = mNextResourceId++;
+    private void createOrUpdateTransform(
+            IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
+            throws RemoteException {
 
-        UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
-        // Avoid resizing by creating a dependency array of min-size 3 (1 UDP encap + 2 SPIs)
-        List<RefcountedResource> dependencies = new ArrayList<>(3);
-
-        if (!userRecord.mTransformQuotaTracker.isAvailable()) {
-            return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
-        }
-        SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
-
-        int encapType, encapLocalPort = 0, encapRemotePort = 0;
-        EncapSocketRecord socketRecord = null;
-        encapType = c.getEncapType();
+        int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
         if (encapType != IpSecTransform.ENCAP_NONE) {
-            RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
-                    userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
-                            c.getEncapSocketResourceId());
-            dependencies.add(refcountedSocketRecord);
-
-            socketRecord = refcountedSocketRecord.getResource();
             encapLocalPort = socketRecord.getPort();
             encapRemotePort = c.getEncapRemotePort();
         }
 
-        for (int direction : DIRECTIONS) {
-            IpSecAlgorithm auth = c.getAuthentication(direction);
-            IpSecAlgorithm crypt = c.getEncryption(direction);
-            IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(direction);
+        IpSecAlgorithm auth = c.getAuthentication();
+        IpSecAlgorithm crypt = c.getEncryption();
+        IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
 
-            RefcountedResource<SpiRecord> refcountedSpiRecord =
-                    userRecord.mSpiRecords.getRefcountedResourceOrThrow(
-                            c.getSpiResourceId(direction));
-            dependencies.add(refcountedSpiRecord);
+        mSrvConfig
+                .getNetdInstance()
+                .ipSecAddSecurityAssociation(
+                        resourceId,
+                        c.getMode(),
+                        c.getSourceAddress(),
+                        c.getDestinationAddress(),
+                        (c.getNetwork() != null) ? c.getNetwork().netId : 0,
+                        spiRecord.getSpi(),
+                        c.getMarkValue(),
+                        c.getMarkMask(),
+                        (auth != null) ? auth.getName() : "",
+                        (auth != null) ? auth.getKey() : new byte[] {},
+                        (auth != null) ? auth.getTruncationLengthBits() : 0,
+                        (crypt != null) ? crypt.getName() : "",
+                        (crypt != null) ? crypt.getKey() : new byte[] {},
+                        (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+                        (authCrypt != null) ? authCrypt.getName() : "",
+                        (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
+                        (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
+                        encapType,
+                        encapLocalPort,
+                        encapRemotePort);
+    }
 
-            spis[direction] = refcountedSpiRecord.getResource();
-            int spi = spis[direction].getSpi();
-            try {
-                mSrvConfig
-                        .getNetdInstance()
-                        .ipSecAddSecurityAssociation(
-                                resourceId,
-                                c.getMode(),
-                                direction,
-                                c.getLocalAddress(),
-                                c.getRemoteAddress(),
-                                (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
-                                spi,
-                                (auth != null) ? auth.getName() : "",
-                                (auth != null) ? auth.getKey() : new byte[] {},
-                                (auth != null) ? auth.getTruncationLengthBits() : 0,
-                                (crypt != null) ? crypt.getName() : "",
-                                (crypt != null) ? crypt.getKey() : new byte[] {},
-                                (crypt != null) ? crypt.getTruncationLengthBits() : 0,
-                                (authCrypt != null) ? authCrypt.getName() : "",
-                                (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
-                                (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
-                                encapType,
-                                encapLocalPort,
-                                encapRemotePort);
-            } catch (ServiceSpecificException e) {
-                // FIXME: get the error code and throw is at an IOException from Errno Exception
-                return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
-            }
+    /**
+     * Create a IPsec transform, which represents a single security association in the kernel. The
+     * transform will be cached by the system server and must be freed when no longer needed. It is
+     * possible to free one, deleting the SA from underneath sockets that are using it, which will
+     * result in all of those sockets becoming unable to send or receive data.
+     */
+    @Override
+    public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
+            throws RemoteException {
+        checkIpSecConfig(c);
+        checkNotNull(binder, "Null Binder passed to createTransform");
+        final int resourceId = mNextResourceId++;
+
+        UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+        List<RefcountedResource> dependencies = new ArrayList<>();
+
+        if (!userRecord.mTransformQuotaTracker.isAvailable()) {
+            return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
         }
-        // Both SAs were created successfully, time to construct a record and lock it away
+
+        EncapSocketRecord socketRecord = null;
+        if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
+            RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
+                    userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
+                            c.getEncapSocketResourceId());
+            dependencies.add(refcountedSocketRecord);
+            socketRecord = refcountedSocketRecord.getResource();
+        }
+
+        RefcountedResource<SpiRecord> refcountedSpiRecord =
+                userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
+        dependencies.add(refcountedSpiRecord);
+        SpiRecord spiRecord = refcountedSpiRecord.getResource();
+
+        try {
+            createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
+        } catch (ServiceSpecificException e) {
+            // FIXME: get the error code and throw is at an IOException from Errno Exception
+            return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
+        }
+
+        // SA was created successfully, time to construct a record and lock it away
         userRecord.mTransformRecords.put(
                 resourceId,
                 new RefcountedResource<TransformRecord>(
-                        new TransformRecord(resourceId, c, spis, socketRecord),
+                        new TransformRecord(resourceId, c, spiRecord, socketRecord),
                         binder,
                         dependencies.toArray(new RefcountedResource[dependencies.size()])));
         return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
@@ -1206,7 +1534,7 @@
      * other reasons.
      */
     @Override
-    public synchronized void deleteTransportModeTransform(int resourceId) throws RemoteException {
+    public synchronized void deleteTransform(int resourceId) throws RemoteException {
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
         releaseResource(userRecord.mTransformRecords, resourceId);
     }
@@ -1217,9 +1545,9 @@
      */
     @Override
     public synchronized void applyTransportModeTransform(
-            ParcelFileDescriptor socket, int resourceId) throws RemoteException {
+            ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
+        checkDirection(direction);
         // Get transform record; if no transform is found, will throw IllegalArgumentException
         TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
 
@@ -1228,19 +1556,22 @@
             throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
         }
 
+        // Get config and check that to-be-applied transform has the correct mode
         IpSecConfig c = info.getConfig();
+        Preconditions.checkArgument(
+                c.getMode() == IpSecTransform.MODE_TRANSPORT,
+                "Transform mode was not Transport mode; cannot be applied to a socket");
+
         try {
-            for (int direction : DIRECTIONS) {
-                mSrvConfig
-                        .getNetdInstance()
-                        .ipSecApplyTransportModeTransform(
-                                socket.getFileDescriptor(),
-                                resourceId,
-                                direction,
-                                c.getLocalAddress(),
-                                c.getRemoteAddress(),
-                                info.getSpiRecord(direction).getSpi());
-            }
+            mSrvConfig
+                    .getNetdInstance()
+                    .ipSecApplyTransportModeTransform(
+                            socket.getFileDescriptor(),
+                            resourceId,
+                            direction,
+                            c.getSourceAddress(),
+                            c.getDestinationAddress(),
+                            info.getSpiRecord().getSpi());
         } catch (ServiceSpecificException e) {
             if (e.errorCode == EINVAL) {
                 throw new IllegalArgumentException(e.toString());
@@ -1251,13 +1582,13 @@
     }
 
     /**
-     * Remove a transport mode transform from a socket, applying the default (empty) policy. This
-     * will ensure that NO IPsec policy is applied to the socket (would be the equivalent of
-     * applying a policy that performs no IPsec). Today the resourceId parameter is passed but not
-     * used: reserved for future improved input validation.
+     * Remove transport mode transforms from a socket, applying the default (empty) policy. This
+     * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
+     * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
+     * reserved for future improved input validation.
      */
     @Override
-    public synchronized void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
+    public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
             throws RemoteException {
         try {
             mSrvConfig
@@ -1268,6 +1599,76 @@
         }
     }
 
+    /**
+     * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
+     * security association as a correspondent policy to the provided interface
+     */
+    @Override
+    public synchronized void applyTunnelModeTransform(
+            int tunnelResourceId, int direction, int transformResourceId) throws RemoteException {
+        checkDirection(direction);
+
+        UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+
+        // Get transform record; if no transform is found, will throw IllegalArgumentException
+        TransformRecord transformInfo =
+                userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
+
+        // Get tunnelInterface record; if no such interface is found, will throw
+        // IllegalArgumentException
+        TunnelInterfaceRecord tunnelInterfaceInfo =
+                userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
+
+        // Get config and check that to-be-applied transform has the correct mode
+        IpSecConfig c = transformInfo.getConfig();
+        Preconditions.checkArgument(
+                c.getMode() == IpSecTransform.MODE_TUNNEL,
+                "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
+
+        EncapSocketRecord socketRecord = null;
+        if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
+            socketRecord =
+                    userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
+        }
+        SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
+
+        int mark =
+                (direction == IpSecManager.DIRECTION_IN)
+                        ? tunnelInterfaceInfo.getIkey()
+                        : tunnelInterfaceInfo.getOkey();
+
+        try {
+            c.setMarkValue(mark);
+            c.setMarkMask(0xffffffff);
+
+            if (direction == IpSecManager.DIRECTION_OUT) {
+                // Set output mark via underlying network (output only)
+                c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
+
+                // If outbound, also add SPI to the policy.
+                mSrvConfig
+                        .getNetdInstance()
+                        .ipSecUpdateSecurityPolicy(
+                                0, // Use 0 for reqId
+                                direction,
+                                "",
+                                "",
+                                transformInfo.getSpiRecord().getSpi(),
+                                mark,
+                                0xffffffff);
+            }
+
+            // Update SA with tunnel mark (ikey or okey based on direction)
+            createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == EINVAL) {
+                throw new IllegalArgumentException(e.toString());
+            } else {
+                throw e;
+            }
+        }
+    }
+
     @Override
     protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mContext.enforceCallingOrSelfPermission(DUMP, TAG);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 40e6d26..88ae224 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -21,9 +21,6 @@
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.Manifest.permission.NETWORK_STACK;
 import static android.Manifest.permission.SHUTDOWN;
-import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE;
@@ -1957,15 +1954,6 @@
         }
     }
 
-    private static boolean shouldUseTls(ContentResolver cr) {
-        String privateDns = Settings.Global.getString(cr, Settings.Global.PRIVATE_DNS_MODE);
-        if (TextUtils.isEmpty(privateDns)) {
-            privateDns = PRIVATE_DNS_DEFAULT_MODE;
-        }
-        return privateDns.equals(PRIVATE_DNS_MODE_OPPORTUNISTIC) ||
-               privateDns.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
-    }
-
     @Override
     public void addVpnUidRanges(int netId, UidRange[] ranges) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -2508,12 +2496,16 @@
 
     @Override
     public void removeNetwork(int netId) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
 
         try {
-            mConnector.execute("network", "destroy", netId);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.networkDestroy(netId);
+        } catch (ServiceSpecificException e) {
+            Log.w(TAG, "removeNetwork(" + netId + "): ", e);
+            throw e;
+        } catch (RemoteException e) {
+            Log.w(TAG, "removeNetwork(" + netId + "): ", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 831c9cb..6747be3 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -147,6 +147,8 @@
 
     private int[] mDataActivationState;
 
+    private boolean[] mUserMobileDataState;
+
     private SignalStrength[] mSignalStrength;
 
     private boolean[] mMessageWaiting;
@@ -304,6 +306,7 @@
         mServiceState = new ServiceState[numPhones];
         mVoiceActivationState = new int[numPhones];
         mDataActivationState = new int[numPhones];
+        mUserMobileDataState = new boolean[numPhones];
         mSignalStrength = new SignalStrength[numPhones];
         mMessageWaiting = new boolean[numPhones];
         mCallForwarding = new boolean[numPhones];
@@ -320,6 +323,7 @@
             mCallIncomingNumber[i] =  "";
             mServiceState[i] =  new ServiceState();
             mSignalStrength[i] =  new SignalStrength();
+            mUserMobileDataState[i] = false;
             mMessageWaiting[i] =  false;
             mCallForwarding[i] =  false;
             mCellLocation[i] = new Bundle();
@@ -656,6 +660,13 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
+                        try {
+                            r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                 }
             }
         } else {
@@ -1012,6 +1023,33 @@
         }
     }
 
+    public void notifyUserMobileDataStateChangedForPhoneId(int phoneId, int subId, boolean state) {
+        if (!checkNotifyPermission("notifyUserMobileDataStateChanged()")) {
+            return;
+        }
+        if (VDBG) {
+            log("notifyUserMobileDataStateChangedForSubscriberPhoneID: subId=" + phoneId
+                    + " state=" + state);
+        }
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                mMessageWaiting[phoneId] = state;
+                for (Record r : mRecords) {
+                    if (r.matchPhoneStateListenerEvent(
+                            PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) &&
+                            idMatch(r.subId, subId, phoneId)) {
+                        try {
+                            r.callback.onUserMobileDataStateChanged(state);
+                        } catch (RemoteException ex) {
+                            mRemoveList.add(r.binder);
+                        }
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
     public void notifyCallForwardingChanged(boolean cfi) {
         notifyCallForwardingChangedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cfi);
     }
@@ -1374,6 +1412,7 @@
                 pw.println("mServiceState=" + mServiceState[i]);
                 pw.println("mVoiceActivationState= " + mVoiceActivationState[i]);
                 pw.println("mDataActivationState= " + mDataActivationState[i]);
+                pw.println("mUserMobileDataState= " + mUserMobileDataState[i]);
                 pw.println("mSignalStrength=" + mSignalStrength[i]);
                 pw.println("mMessageWaiting=" + mMessageWaiting[i]);
                 pw.println("mCallForwarding=" + mCallForwarding[i]);
@@ -1755,6 +1794,18 @@
             }
         }
 
+        if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
+            try {
+                if (VDBG) {
+                    log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId="
+                            + phoneId + " umds=" + mUserMobileDataState[phoneId]);
+                }
+                r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
+            } catch (RemoteException ex) {
+                mRemoveList.add(r.binder);
+            }
+        }
+
         if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
             try {
                 if (VDBG) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 8d46d1e..18b00ac 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -87,8 +87,10 @@
         "/system/bin/sdcard",
         "/system/bin/surfaceflinger",
         "media.extractor", // system/bin/mediaextractor
+        "media.metrics", // system/bin/mediametrics
         "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
         "com.android.bluetooth",  // Bluetooth service
+        "statsd",  // Stats daemon
     };
 
     public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6cdf071..e944326 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -63,6 +63,7 @@
 import static android.os.Process.SCHED_FIFO;
 import static android.os.Process.SCHED_OTHER;
 import static android.os.Process.SCHED_RESET_ON_FORK;
+import static android.os.Process.SE_UID;
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SIGNAL_QUIT;
 import static android.os.Process.SIGNAL_USR1;
@@ -1493,6 +1494,14 @@
     String mProfileApp = null;
     ProcessRecord mProfileProc = null;
     ProfilerInfo mProfilerInfo = null;
+
+    /**
+     * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
+     * is not null, this map is checked and the mapped agent installed during bind-time. Note:
+     * A non-null agent in mProfileInfo overrides this.
+     */
+    private @Nullable Map<String, String> mAppAgentMap = null;
+
     int mProfileType = 0;
     final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
     String mMemWatchDumpProcName;
@@ -3877,6 +3886,12 @@
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
             }
 
+            if (app.info.isAllowedToUseHiddenApi()) {
+                // This app is allowed to use undocumented and private APIs. Set
+                // up its runtime with the appropriate flag.
+                runtimeFlags |= Zygote.DISABLE_HIDDEN_API_CHECKS;
+            }
+
             String invokeWith = null;
             if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
                 // Debuggable apps may include a wrapper script with their library directory.
@@ -7011,18 +7026,6 @@
                 }
             }
 
-            ProfilerInfo profilerInfo = null;
-            String agent = null;
-            if (mProfileApp != null && mProfileApp.equals(processName)) {
-                mProfileProc = app;
-                profilerInfo = (mProfilerInfo != null && mProfilerInfo.profileFile != null) ?
-                        new ProfilerInfo(mProfilerInfo) : null;
-                agent = mProfilerInfo != null ? mProfilerInfo.agent : null;
-            } else if (app.instr != null && app.instr.mProfileFile != null) {
-                profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
-                        null);
-            }
-
             boolean enableTrackAllocation = false;
             if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
                 enableTrackAllocation = true;
@@ -7047,6 +7050,39 @@
             ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
             app.compat = compatibilityInfoForPackageLocked(appInfo);
 
+            ProfilerInfo profilerInfo = null;
+            String preBindAgent = null;
+            if (mProfileApp != null && mProfileApp.equals(processName)) {
+                mProfileProc = app;
+                if (mProfilerInfo != null) {
+                    // Send a profiler info object to the app if either a file is given, or
+                    // an agent should be loaded at bind-time.
+                    boolean needsInfo = mProfilerInfo.profileFile != null
+                            || mProfilerInfo.attachAgentDuringBind;
+                    profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
+                    if (mProfilerInfo.agent != null) {
+                        preBindAgent = mProfilerInfo.agent;
+                    }
+                }
+            } else if (app.instr != null && app.instr.mProfileFile != null) {
+                profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
+                        null, false);
+            }
+            if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
+                // We need to do a debuggable check here. See setAgentApp for why the check is
+                // postponed to here.
+                if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                    String agent = mAppAgentMap.get(processName);
+                    // Do not overwrite already requested agent.
+                    if (profilerInfo == null) {
+                        profilerInfo = new ProfilerInfo(null, null, 0, false, false,
+                                mAppAgentMap.get(processName), true);
+                    } else if (profilerInfo.agent == null) {
+                        profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
+                    }
+                }
+            }
+
             if (profilerInfo != null && profilerInfo.profileFd != null) {
                 profilerInfo.profileFd = profilerInfo.profileFd.dup();
             }
@@ -7087,8 +7123,8 @@
 
             // If we were asked to attach an agent on startup, do so now, before we're binding
             // application code.
-            if (agent != null) {
-                thread.attachAgent(agent);
+            if (preBindAgent != null) {
+                thread.attachAgent(preBindAgent);
             }
 
             checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
@@ -12780,6 +12816,52 @@
         }
     }
 
+    /**
+     * Set or remove an agent to be run whenever an app with the given process name starts.
+     *
+     * This method will not check whether the given process name matches a debuggable app. That
+     * would require scanning all current packages, and a rescan when new packages are installed
+     * or updated.
+     *
+     * Instead, do the check when an application is started and matched to a stored agent.
+     *
+     * @param packageName the process name of the app.
+     * @param agent the agent string to be used, or null to remove any previously set agent.
+     */
+    @Override
+    public void setAgentApp(@NonNull String packageName, @Nullable String agent) {
+        synchronized (this) {
+            // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+            // its own permission.
+            if (checkCallingPermission(
+                    android.Manifest.permission.SET_ACTIVITY_WATCHER) !=
+                        PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+            }
+
+            if (agent == null) {
+                if (mAppAgentMap != null) {
+                    mAppAgentMap.remove(packageName);
+                    if (mAppAgentMap.isEmpty()) {
+                        mAppAgentMap = null;
+                    }
+                }
+            } else {
+                if (mAppAgentMap == null) {
+                    mAppAgentMap = new HashMap<>();
+                }
+                if (mAppAgentMap.size() >= 100) {
+                    // Limit the size of the map, to avoid OOMEs.
+                    Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
+                            + "/" + agent);
+                    return;
+                }
+                mAppAgentMap.put(packageName, agent);
+            }
+        }
+    }
+
     void setTrackAllocationApp(ApplicationInfo app, String processName) {
         synchronized (this) {
             boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
@@ -19177,6 +19259,7 @@
             case PHONE_UID:
             case BLUETOOTH_UID:
             case NFC_UID:
+            case SE_UID:
                 isCallerSystem = true;
                 break;
             default:
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 8488e52..59c0ed1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -114,6 +114,7 @@
     private boolean mAutoStop;
     private boolean mStreaming;   // Streaming the profiling output to a file.
     private String mAgent;  // Agent to attach on startup.
+    private boolean mAttachAgentDuringBind;  // Whether agent should be attached late.
     private int mDisplayId;
     private int mStackId;
     private int mTaskId;
@@ -163,6 +164,8 @@
                     return runDumpHeap(pw);
                 case "set-debug-app":
                     return runSetDebugApp(pw);
+                case "set-agent-app":
+                    return runSetAgentApp(pw);
                 case "clear-debug-app":
                     return runClearDebugApp(pw);
                 case "set-watch-heap":
@@ -295,7 +298,21 @@
                 } else if (opt.equals("--streaming")) {
                     mStreaming = true;
                 } else if (opt.equals("--attach-agent")) {
+                    if (mAgent != null) {
+                        cmd.getErrPrintWriter().println(
+                                "Multiple --attach-agent(-bind) not supported");
+                        return false;
+                    }
                     mAgent = getNextArgRequired();
+                    mAttachAgentDuringBind = false;
+                } else if (opt.equals("--attach-agent-bind")) {
+                    if (mAgent != null) {
+                        cmd.getErrPrintWriter().println(
+                                "Multiple --attach-agent(-bind) not supported");
+                        return false;
+                    }
+                    mAgent = getNextArgRequired();
+                    mAttachAgentDuringBind = true;
                 } else if (opt.equals("-R")) {
                     mRepeat = Integer.parseInt(getNextArgRequired());
                 } else if (opt.equals("-S")) {
@@ -381,7 +398,7 @@
                     }
                 }
                 profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop,
-                        mStreaming, mAgent);
+                        mStreaming, mAgent, mAttachAgentDuringBind);
             }
 
             pw.println("Starting: " + intent);
@@ -755,7 +772,7 @@
                 return -1;
             }
             profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming,
-                    null);
+                    null, false);
         }
 
         try {
@@ -847,6 +864,13 @@
         return 0;
     }
 
+    int runSetAgentApp(PrintWriter pw) throws RemoteException {
+        String pkg = getNextArgRequired();
+        String agent = getNextArg();
+        mInterface.setAgentApp(pkg, agent);
+        return 0;
+    }
+
     int runClearDebugApp(PrintWriter pw) throws RemoteException {
         mInterface.setDebugApp(null, false, true);
         return 0;
@@ -2679,6 +2703,7 @@
             pw.println("          (use with --start-profiler)");
             pw.println("      -P <FILE>: like above, but profiling stops when app goes idle");
             pw.println("      --attach-agent <agent>: attach the given agent before binding");
+            pw.println("      --attach-agent-bind <agent>: attach the given agent during binding");
             pw.println("      -R: repeat the activity launch <COUNT> times.  Prior to each repeat,");
             pw.println("          the top activity will be finished.");
             pw.println("      -S: force stop the target app before starting the activity");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5eb2a8d..4293d45 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -136,7 +136,6 @@
 import java.io.PrintWriter;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -768,7 +767,7 @@
         // Register for device connection intent broadcasts.
         IntentFilter intentFilter =
                 new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
-        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -2929,14 +2928,28 @@
     }
 
     public void setBluetoothScoOnInt(boolean on, String eventSource) {
+        if (DEBUG_DEVICES) {
+            Log.d(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
+        }
         if (on) {
             // do not accept SCO ON if SCO audio is not connected
-            synchronized(mScoClients) {
-                if ((mBluetoothHeadset != null) &&
-                    (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
-                             != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
-                    mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
-                    return;
+            synchronized (mScoClients) {
+                if (mBluetoothHeadset != null) {
+                    if (mBluetoothHeadsetDevice == null) {
+                        BluetoothDevice activeDevice = mBluetoothHeadset.getActiveDevice();
+                        if (activeDevice != null) {
+                            // setBtScoActiveDevice() might trigger resetBluetoothSco() which
+                            // will call setBluetoothScoOnInt(false, "resetBluetoothSco")
+                            setBtScoActiveDevice(activeDevice);
+                        }
+                    }
+                    if (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
+                            != BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+                        mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
+                        Log.w(TAG, "setBluetoothScoOnInt(true) failed because "
+                                + mBluetoothHeadsetDevice + " is not in audio connected mode");
+                        return;
+                    }
                 }
             }
             mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
@@ -3324,24 +3337,23 @@
         }
     }
 
-    void setBtScoDeviceConnectionState(BluetoothDevice btDevice, int state) {
+    private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
         if (btDevice == null) {
-            return;
+            return true;
         }
-
         String address = btDevice.getAddress();
         BluetoothClass btClass = btDevice.getBluetoothClass();
         int outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
         int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
         if (btClass != null) {
             switch (btClass.getDeviceClass()) {
-            case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
-            case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
-                outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
-                break;
-            case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
-                outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
-                break;
+                case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
+                case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
+                    outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+                    break;
+                case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+                    outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+                    break;
             }
         }
 
@@ -3349,34 +3361,33 @@
             address = "";
         }
 
-        boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
-
         String btDeviceName =  btDevice.getName();
-        boolean success =
-            handleDeviceConnection(connected, outDevice, address, btDeviceName) &&
-            handleDeviceConnection(connected, inDevice, address, btDeviceName);
+        boolean result = handleDeviceConnection(isActive, outDevice, address, btDeviceName);
+        // handleDeviceConnection() && result to make sure the method get executed
+        result = handleDeviceConnection(isActive, inDevice, address, btDeviceName) && result;
+        return result;
+    }
 
-        if (!success) {
-          return;
+    void setBtScoActiveDevice(BluetoothDevice btDevice) {
+        if (DEBUG_DEVICES) {
+            Log.d(TAG, "setBtScoActiveDevice(" + btDevice + ")");
         }
-
-        /* When one BT headset is disconnected while another BT headset
-         * is connected, don't mess with the headset device.
-         */
-        if ((state == BluetoothProfile.STATE_DISCONNECTED ||
-            state == BluetoothProfile.STATE_DISCONNECTING) &&
-            mBluetoothHeadset != null &&
-            mBluetoothHeadset.getAudioState(btDevice) == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
-            Log.w(TAG, "SCO connected through another device, returning");
-            return;
-        }
-
         synchronized (mScoClients) {
-            if (connected) {
+            final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
+            if (!Objects.equals(btDevice, previousActiveDevice)) {
+                if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
+                    Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device "
+                            + previousActiveDevice);
+                }
+                if (!handleBtScoActiveDeviceChange(btDevice, true)) {
+                    Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice);
+                    // set mBluetoothHeadsetDevice to null when failing to add new device
+                    btDevice = null;
+                }
                 mBluetoothHeadsetDevice = btDevice;
-            } else {
-                mBluetoothHeadsetDevice = null;
-                resetBluetoothSco();
+                if (mBluetoothHeadsetDevice == null) {
+                    resetBluetoothSco();
+                }
             }
         }
     }
@@ -3431,12 +3442,7 @@
                     // Discard timeout message
                     mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
                     mBluetoothHeadset = (BluetoothHeadset) proxy;
-                    deviceList = mBluetoothHeadset.getConnectedDevices();
-                    if (deviceList.size() > 0) {
-                        mBluetoothHeadsetDevice = deviceList.get(0);
-                    } else {
-                        mBluetoothHeadsetDevice = null;
-                    }
+                    setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice());
                     // Refresh SCO audio state
                     checkScoAudioState();
                     // Continue pending action if any
@@ -3557,10 +3563,7 @@
 
     void disconnectHeadset() {
         synchronized (mScoClients) {
-            if (mBluetoothHeadsetDevice != null) {
-                setBtScoDeviceConnectionState(mBluetoothHeadsetDevice,
-                        BluetoothProfile.STATE_DISCONNECTED);
-            }
+            setBtScoActiveDevice(null);
             mBluetoothHeadset = null;
         }
     }
@@ -4103,22 +4106,30 @@
 
     public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
     {
+        return setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+                device, state, profile, false /* suppressNoisyIntent */);
+    }
+
+    public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
+                int state, int profile, boolean suppressNoisyIntent)
+    {
         if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
             return 0;
         }
         return setBluetoothA2dpDeviceConnectionStateInt(
-                device, state, profile, AudioSystem.DEVICE_NONE);
+                device, state, profile, suppressNoisyIntent, AudioSystem.DEVICE_NONE);
     }
 
     public int setBluetoothA2dpDeviceConnectionStateInt(
-            BluetoothDevice device, int state, int profile, int musicDevice)
+            BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent,
+            int musicDevice)
     {
         int delay;
         if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
             throw new IllegalArgumentException("invalid profile " + profile);
         }
         synchronized (mConnectedDevices) {
-            if (profile == BluetoothProfile.A2DP) {
+            if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) {
                 int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
                 delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                         intState, musicDevice);
@@ -5368,7 +5379,7 @@
                    // consistent with audio policy manager state
                    setBluetoothA2dpDeviceConnectionStateInt(
                            btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP,
-                           musicDevice);
+                           false /* suppressNoisyIntent */, musicDevice);
                }
             }
         }
@@ -5724,11 +5735,9 @@
                     AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
                 }
                 mDockState = dockState;
-            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
-                state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
-                                               BluetoothProfile.STATE_DISCONNECTED);
+            } else if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                setBtScoDeviceConnectionState(btDevice, state);
+                setBtScoActiveDevice(btDevice);
             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                 boolean broadcast = false;
                 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
diff --git a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
new file mode 100644
index 0000000..24865bc
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+/**
+ * A class encapsulating various constants used by Connectivity.
+ * @hide
+ */
+public class ConnectivityConstants {
+    // IPC constants
+    public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
+            "android.net.conn.NETWORK_CONDITIONS_MEASURED";
+    public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
+    public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
+    public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
+    public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
+    public static final String EXTRA_CELL_ID = "extra_cellid";
+    public static final String EXTRA_SSID = "extra_ssid";
+    public static final String EXTRA_BSSID = "extra_bssid";
+    /** real time since boot */
+    public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
+    public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
+
+    public static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
+            "android.permission.ACCESS_NETWORK_CONDITIONS";
+
+    // Penalty applied to scores of Networks that have not been validated.
+    public static final int UNVALIDATED_SCORE_PENALTY = 40;
+
+    // Score for explicitly connected network.
+    //
+    // This ensures that a) the explicitly selected network is never trumped by anything else, and
+    // b) the explicitly selected network is never torn down.
+    public static final int MAXIMUM_NETWORK_SCORE = 100;
+    // VPNs typically have priority over other networks. Give them a score that will
+    // let them win every single time.
+    public static final int VPN_DEFAULT_SCORE = 101;
+}
diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
index bd2e96e..e43d152 100644
--- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
+++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
@@ -150,7 +150,8 @@
             fillLinkInfo(ev, newNai);
             ev.initialScore = newNai.getCurrentScore();
             if (newNai.lastValidated) {
-                logDefaultNetworkValidity(timeMs, true);
+                mIsCurrentlyValid = true;
+                mLastValidationTimeMs = timeMs;
             }
         } else {
             mIsCurrentlyValid = false;
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index a4170ce..a1c54bd 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -17,6 +17,7 @@
 package com.android.server.connectivity;
 
 import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
@@ -29,19 +30,32 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.LinkProperties;
+import android.net.Network;
 import android.net.NetworkUtils;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.INetworkManagementService;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.system.GaiException;
+import android.system.OsConstants;
+import android.system.StructAddrinfo;
 import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.server.connectivity.MockableSystemProperties;
 
+import libcore.io.Libcore;
+
 import java.net.InetAddress;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.StringJoiner;
 
 
 /**
@@ -61,10 +75,86 @@
     private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
     private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
 
+    public static class PrivateDnsConfig {
+        public final boolean useTls;
+        public final String hostname;
+        public final InetAddress[] ips;
+
+        public PrivateDnsConfig() {
+            this(false);
+        }
+
+        public PrivateDnsConfig(boolean useTls) {
+            this.useTls = useTls;
+            this.hostname = "";
+            this.ips = new InetAddress[0];
+        }
+
+        public PrivateDnsConfig(String hostname, InetAddress[] ips) {
+            this.useTls = !TextUtils.isEmpty(hostname);
+            this.hostname = useTls ? hostname : "";
+            this.ips = (ips != null) ? ips : new InetAddress[0];
+        }
+
+        public PrivateDnsConfig(PrivateDnsConfig cfg) {
+            useTls = cfg.useTls;
+            hostname = cfg.hostname;
+            ips = cfg.ips;
+        }
+
+        public boolean inStrictMode() {
+            return useTls && !TextUtils.isEmpty(hostname);
+        }
+
+        public String toString() {
+            return PrivateDnsConfig.class.getSimpleName() +
+                    "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}";
+        }
+    }
+
+    public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
+        final String mode = getPrivateDnsMode(cr);
+
+        final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
+
+        if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) {
+            final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER);
+            return new PrivateDnsConfig(specifier, null);
+        }
+
+        return new PrivateDnsConfig(useTls);
+    }
+
+    public static PrivateDnsConfig tryBlockingResolveOf(Network network, String name) {
+        final StructAddrinfo hints = new StructAddrinfo();
+        // Unnecessary, but expressly no AI_ADDRCONFIG.
+        hints.ai_flags = 0;
+        // Fetch all IP addresses at once to minimize re-resolution.
+        hints.ai_family = OsConstants.AF_UNSPEC;
+        hints.ai_socktype = OsConstants.SOCK_DGRAM;
+
+        try {
+            final InetAddress[] ips = Libcore.os.android_getaddrinfo(name, hints, network.netId);
+            if (ips != null && ips.length > 0) {
+                return new PrivateDnsConfig(name, ips);
+            }
+        } catch (GaiException ignored) {}
+
+        return null;
+    }
+
+    public static Uri[] getPrivateDnsSettingsUris() {
+        final Uri[] uris = new Uri[2];
+        uris[0] = Settings.Global.getUriFor(PRIVATE_DNS_MODE);
+        uris[1] = Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER);
+        return uris;
+    }
+
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private final INetworkManagementService mNMS;
     private final MockableSystemProperties mSystemProperties;
+    private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
 
     private int mNumDnsEntries;
     private int mSampleValidity;
@@ -79,44 +169,55 @@
         mContentResolver = mContext.getContentResolver();
         mNMS = nms;
         mSystemProperties = sp;
+        mPrivateDnsMap = new HashMap<>();
 
         // TODO: Create and register ContentObservers to track every setting
         // used herein, posting messages to respond to changes.
     }
 
-    public boolean isPrivateDnsInStrictMode() {
-        return !TextUtils.isEmpty(mPrivateDnsMode) &&
-               mPrivateDnsMode.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME) &&
-               !TextUtils.isEmpty(mPrivateDnsSpecifier);
+    public PrivateDnsConfig getPrivateDnsConfig() {
+        return getPrivateDnsConfig(mContentResolver);
+    }
+
+    public void removeNetwork(Network network) {
+        mPrivateDnsMap.remove(network.netId);
+    }
+
+    public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
+        Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
+        return (cfg != null)
+                ? mPrivateDnsMap.put(network.netId, cfg)
+                : mPrivateDnsMap.remove(network);
     }
 
     public void setDnsConfigurationForNetwork(
-            int netId, Collection<InetAddress> servers, String domains, boolean isDefaultNetwork) {
-        updateParametersSettings();
-        updatePrivateDnsSettings();
+            int netId, LinkProperties lp, boolean isDefaultNetwork) {
+        // We only use the PrivateDnsConfig data pushed to this class instance
+        // from ConnectivityService because it works in coordination with
+        // NetworkMonitor to decide which networks need validation and runs the
+        // blocking calls to resolve Private DNS strict mode hostnames.
+        //
+        // At this time we do attempt to enable Private DNS on non-Internet
+        // networks like IMS.
+        final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
 
-        final String[] serverStrs = NetworkUtils.makeStrings(servers);
-        final String[] domainStrs = (domains == null) ? new String[0] : domains.split(" ");
+        final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
+        final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
+        final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
+
+        final String[] serverStrs = NetworkUtils.makeStrings(
+                strictMode ? Arrays.stream(privateDnsCfg.ips)
+                                   .filter((ip) -> lp.isReachable(ip))
+                                   .collect(Collectors.toList())
+                           : lp.getDnsServers());
+        final String[] domainStrs = getDomainStrings(lp.getDomains());
+
+        updateParametersSettings();
         final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples };
-        final boolean useTls = shouldUseTls(mPrivateDnsMode);
-        // TODO: Populate tlsHostname once it's decided how the hostname's IP
-        // addresses will be resolved:
-        //
-        //     [1] network-provided DNS servers are included here with the
-        //         hostname and netd will use the network-provided servers to
-        //         resolve the hostname and fix up its internal structures, or
-        //
-        //     [2] network-provided DNS servers are included here without the
-        //         hostname, the ConnectivityService layer resolves the given
-        //         hostname, and then reconfigures netd with this information.
-        //
-        // In practice, there will always be a need for ConnectivityService or
-        // the captive portal app to use the network-provided services to make
-        // some queries. This argues in favor of [1], in concert with another
-        // mechanism, perhaps setting a high bit in the netid, to indicate
-        // via existing DNS APIs which set of servers (network-provided or
-        // non-network-provided private DNS) should be queried.
-        final String tlsHostname = "";
+
+        Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
+                netId, Arrays.toString(serverStrs), Arrays.toString(domainStrs),
+                Arrays.toString(params), useTls, tlsHostname));
         try {
             mNMS.setDnsConfigurationForNetwork(
                     netId, serverStrs, domainStrs, params, useTls, tlsHostname);
@@ -129,7 +230,7 @@
         // default network, and we should just set net.dns1 to ::1, not least
         // because applications attempting to use net.dns resolvers will bypass
         // the privacy protections of things like DNS-over-TLS.
-        if (isDefaultNetwork) setDefaultDnsSystemProperties(servers);
+        if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers());
         flushVmDnsCache();
     }
 
@@ -163,11 +264,6 @@
         }
     }
 
-    private void updatePrivateDnsSettings() {
-        mPrivateDnsMode = getStringSetting(PRIVATE_DNS_MODE);
-        mPrivateDnsSpecifier = getStringSetting(PRIVATE_DNS_SPECIFIER);
-    }
-
     private void updateParametersSettings() {
         mSampleValidity = getIntSetting(
                 DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
@@ -198,10 +294,6 @@
         }
     }
 
-    private String getStringSetting(String which) {
-        return Settings.Global.getString(mContentResolver, which);
-    }
-
     private int getIntSetting(String which, int dflt) {
         return Settings.Global.getInt(mContentResolver, which, dflt);
     }
@@ -216,11 +308,16 @@
         }
     }
 
-    private static boolean shouldUseTls(String mode) {
-        if (TextUtils.isEmpty(mode)) {
-            mode = PRIVATE_DNS_DEFAULT_MODE;
-        }
-        return mode.equals(PRIVATE_DNS_MODE_OPPORTUNISTIC) ||
-               mode.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+    private static String getPrivateDnsMode(ContentResolver cr) {
+        final String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
+        return !TextUtils.isEmpty(mode) ? mode : PRIVATE_DNS_DEFAULT_MODE;
+    }
+
+    private static String getStringSetting(ContentResolver cr, String which) {
+        return Settings.Global.getString(cr, which);
+    }
+
+    private static String[] getDomainStrings(String domains) {
+        return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" ");
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
index 2ccfdd1..f6b73b7 100644
--- a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
+++ b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
@@ -16,6 +16,9 @@
 
 package com.android.server.connectivity;
 
+import static android.net.util.NetworkConstants.IPV4_HEADER_MIN_LEN;
+import static android.net.util.NetworkConstants.UDP_HEADER_LEN;
+
 import android.system.OsConstants;
 import android.net.ConnectivityManager;
 import android.net.NetworkUtils;
@@ -57,9 +60,6 @@
     /** Packet data. A raw byte string of packet data, not including the link-layer header. */
     public final byte[] data;
 
-    private static final int IPV4_HEADER_LENGTH = 20;
-    private static final int UDP_HEADER_LENGTH = 8;
-
     protected KeepalivePacketData(InetAddress srcAddress, int srcPort,
             InetAddress dstAddress, int dstPort, byte[] data) throws InvalidPacketException {
         this.srcAddress = srcAddress;
@@ -111,7 +111,7 @@
             throw new InvalidPacketException(ERROR_INVALID_PORT);
         }
 
-        int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
+        int length = IPV4_HEADER_MIN_LEN + UDP_HEADER_LEN + 1;
         ByteBuffer buf = ByteBuffer.allocate(length);
         buf.order(ByteOrder.BIG_ENDIAN);
         buf.putShort((short) 0x4500);             // IP version and TOS
@@ -130,7 +130,7 @@
         buf.putShort((short) 0);                  // UDP checksum
         buf.put((byte) 0xff);                     // NAT-T keepalive
         buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
-        buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
+        buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_MIN_LEN));
 
         return new KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
     }
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 25b52da..e786bed 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -252,6 +252,29 @@
         addWakeupEvent(event);
     }
 
+    @Override
+    public synchronized void onTcpSocketStatsEvent(int[] networkIds,
+            int[] sentPackets, int[] lostPackets, int[] rttsUs, int[] sentAckDiffsMs) {
+        if (networkIds.length != sentPackets.length
+                || networkIds.length != lostPackets.length
+                || networkIds.length != rttsUs.length
+                || networkIds.length != sentAckDiffsMs.length) {
+            Log.e(TAG, "Mismatched lengths of TCP socket stats data arrays");
+            return;
+        }
+
+        long timestamp = System.currentTimeMillis();
+        for (int i = 0; i < networkIds.length; i++) {
+            int netId = networkIds[i];
+            int sent = sentPackets[i];
+            int lost = lostPackets[i];
+            int rttUs = rttsUs[i];
+            int sentAckDiffMs = sentAckDiffsMs[i];
+            getMetricsForNetwork(timestamp, netId)
+                    .addTcpStatsResult(sent, lost, rttUs, sentAckDiffMs);
+        }
+    }
+
     private void addWakeupEvent(WakeupEvent event) {
         String iface = event.iface;
         mWakeupEvents.append(event);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index a4d7242..85b70ca 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -223,14 +223,6 @@
 
     // This represents the last score received from the NetworkAgent.
     private int currentScore;
-    // Penalty applied to scores of Networks that have not been validated.
-    private static final int UNVALIDATED_SCORE_PENALTY = 40;
-
-    // Score for explicitly connected network.
-    //
-    // This ensures that a) the explicitly selected network is never trumped by anything else, and
-    // b) the explicitly selected network is never torn down.
-    private static final int MAXIMUM_NETWORK_SCORE = 100;
 
     // The list of NetworkRequests being satisfied by this Network.
     private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
@@ -428,12 +420,12 @@
         // 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)) {
-            return MAXIMUM_NETWORK_SCORE;
+            return ConnectivityConstants.MAXIMUM_NETWORK_SCORE;
         }
 
         int score = currentScore;
         if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
-            score -= UNVALIDATED_SCORE_PENALTY;
+            score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
         }
         if (score < 0) score = 0;
         return score;
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 85d1d1e..c471f0c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -24,6 +24,7 @@
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.net.TrafficStats;
+import android.net.util.NetworkConstants;
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -421,8 +422,6 @@
     private class IcmpCheck extends SimpleSocketCheck implements Runnable {
         private static final int TIMEOUT_SEND = 100;
         private static final int TIMEOUT_RECV = 300;
-        private static final int ICMPV4_ECHO_REQUEST = 8;
-        private static final int ICMPV6_ECHO_REQUEST = 128;
         private static final int PACKET_BUFSIZE = 512;
         private final int mProtocol;
         private final int mIcmpType;
@@ -432,11 +431,11 @@
 
             if (mAddressFamily == AF_INET6) {
                 mProtocol = IPPROTO_ICMPV6;
-                mIcmpType = ICMPV6_ECHO_REQUEST;
+                mIcmpType = NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
                 mMeasurement.description = "ICMPv6";
             } else {
                 mProtocol = IPPROTO_ICMP;
-                mIcmpType = ICMPV4_ECHO_REQUEST;
+                mIcmpType = NetworkConstants.ICMPV4_ECHO_REQUEST_TYPE;
                 mMeasurement.description = "ICMPv4";
             }
 
@@ -504,7 +503,6 @@
     private class DnsUdpCheck extends SimpleSocketCheck implements Runnable {
         private static final int TIMEOUT_SEND = 100;
         private static final int TIMEOUT_RECV = 500;
-        private static final int DNS_SERVER_PORT = 53;
         private static final int RR_TYPE_A = 1;
         private static final int RR_TYPE_AAAA = 28;
         private static final int PACKET_BUFSIZE = 512;
@@ -546,7 +544,8 @@
             }
 
             try {
-                setupSocket(SOCK_DGRAM, IPPROTO_UDP, TIMEOUT_SEND, TIMEOUT_RECV, DNS_SERVER_PORT);
+                setupSocket(SOCK_DGRAM, IPPROTO_UDP, TIMEOUT_SEND, TIMEOUT_RECV,
+                        NetworkConstants.DNS_SERVER_PORT);
             } catch (ErrnoException | IOException e) {
                 mMeasurement.recordFailure(e.toString());
                 return;
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 7684030..8a2e71c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -29,6 +29,7 @@
 import android.net.ConnectivityManager;
 import android.net.ICaptivePortal;
 import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.ProxyInfo;
 import android.net.TrafficStats;
@@ -121,22 +122,6 @@
         }
     }
 
-    public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
-            "android.net.conn.NETWORK_CONDITIONS_MEASURED";
-    public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
-    public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
-    public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
-    public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
-    public static final String EXTRA_CELL_ID = "extra_cellid";
-    public static final String EXTRA_SSID = "extra_ssid";
-    public static final String EXTRA_BSSID = "extra_bssid";
-    /** real time since boot */
-    public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
-    public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
-
-    private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
-            "android.permission.ACCESS_NETWORK_CONDITIONS";
-
     // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
     // The network should be used as a default internet connection.  It was found to be:
     // 1. a functioning network providing internet access, or
@@ -215,6 +200,15 @@
      */
     private static final int CMD_CAPTIVE_PORTAL_RECHECK = BASE + 12;
 
+    /**
+     * ConnectivityService notifies NetworkMonitor of settings changes to
+     * Private DNS. If a DNS resolution is required, e.g. for DNS-over-TLS in
+     * strict mode, then an event is sent back to ConnectivityService with the
+     * result of the resolution attempt.
+     */
+    private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13;
+    public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14;
+
     // Start mReevaluateDelayMs at this value and double.
     private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
     private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
@@ -230,6 +224,12 @@
 
     private static final int NUM_VALIDATION_LOG_LINES = 20;
 
+    public static boolean isValidationRequired(
+            NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
+        // TODO: Consider requiring validation for DUN networks.
+        return dfltNetCap.satisfiedByNetworkCapabilities(nc);
+    }
+
     private final Context mContext;
     private final Handler mConnectivityServiceHandler;
     private final NetworkAgentInfo mNetworkAgentInfo;
@@ -261,6 +261,8 @@
 
     public boolean systemReady = false;
 
+    private DnsManager.PrivateDnsConfig mPrivateDnsCfg = null;
+
     private final State mDefaultState = new DefaultState();
     private final State mValidatedState = new ValidatedState();
     private final State mMaybeNotifyState = new MaybeNotifyState();
@@ -342,6 +344,11 @@
         return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION;
     }
 
+    private boolean isValidationRequired() {
+        return isValidationRequired(
+                mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities);
+    }
+
     // DefaultState is the parent of all States.  It exists only to handle CMD_* messages but
     // does not entail any real state (hence no enter() or exit() routines).
     private class DefaultState extends State {
@@ -405,6 +412,18 @@
                             break;
                     }
                     return HANDLED;
+                case CMD_PRIVATE_DNS_SETTINGS_CHANGED:
+                    if (isValidationRequired()) {
+                        // This performs a blocking DNS resolution of the
+                        // strict mode hostname, if required.
+                        resolvePrivateDnsConfig((DnsManager.PrivateDnsConfig) message.obj);
+                        if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode()) {
+                            mConnectivityServiceHandler.sendMessage(obtainMessage(
+                                    EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId,
+                                    new DnsManager.PrivateDnsConfig(mPrivateDnsCfg)));
+                        }
+                    }
+                    return HANDLED;
                 default:
                     return HANDLED;
             }
@@ -421,7 +440,7 @@
             maybeLogEvaluationResult(
                     networkEventType(validationStage(), EvaluationResult.VALIDATED));
             mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
-                    NETWORK_TEST_RESULT_VALID, mNetId, null));
+                    NETWORK_TEST_RESULT_VALID, mNetId, mPrivateDnsCfg));
             mValidations++;
         }
 
@@ -567,9 +586,9 @@
                     //    the network so don't bother validating here.  Furthermore sending HTTP
                     //    packets over the network may be undesirable, for example an extremely
                     //    expensive metered network, or unwanted leaking of the User Agent string.
-                    if (!mDefaultRequest.networkCapabilities.satisfiedByNetworkCapabilities(
-                            mNetworkAgentInfo.networkCapabilities)) {
+                    if (!isValidationRequired()) {
                         validationLog("Network would not satisfy default request, not validating");
+                        mPrivateDnsCfg = null;
                         transitionTo(mValidatedState);
                         return HANDLED;
                     }
@@ -582,6 +601,7 @@
                     // if this is found to cause problems.
                     CaptivePortalProbeResult probeResult = isCaptivePortal();
                     if (probeResult.isSuccessful()) {
+                        resolvePrivateDnsConfig();
                         transitionTo(mValidatedState);
                     } else if (probeResult.isPortal()) {
                         mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
@@ -1045,6 +1065,44 @@
         return null;
     }
 
+    public void notifyPrivateDnsSettingsChanged(DnsManager.PrivateDnsConfig newCfg) {
+        // Cancel any outstanding resolutions.
+        removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
+        // Send the update to the proper thread.
+        sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
+    }
+
+    private void resolvePrivateDnsConfig() {
+        resolvePrivateDnsConfig(DnsManager.getPrivateDnsConfig(mContext.getContentResolver()));
+    }
+
+    private void resolvePrivateDnsConfig(DnsManager.PrivateDnsConfig cfg) {
+        // Nothing to do.
+        if (cfg == null) {
+            mPrivateDnsCfg = null;
+            return;
+        }
+
+        // No DNS resolution required.
+        if (!cfg.inStrictMode()) {
+            mPrivateDnsCfg = cfg;
+            return;
+        }
+
+        if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode() &&
+                (mPrivateDnsCfg.ips.length > 0) && mPrivateDnsCfg.hostname.equals(cfg.hostname)) {
+            // We have already resolved this strict mode hostname. Assume that
+            // Private DNS services won't be changing serving IP addresses very
+            // frequently and save ourselves one re-resolve.
+            return;
+        }
+
+        mPrivateDnsCfg = cfg;
+        final DnsManager.PrivateDnsConfig resolvedCfg = DnsManager.tryBlockingResolveOf(
+                mNetwork, mPrivateDnsCfg.hostname);
+        if (resolvedCfg != null) mPrivateDnsCfg = resolvedCfg;
+    }
+
     /**
      * @param responseReceived - whether or not we received a valid HTTP response to our request.
      * If false, isCaptivePortal and responseTimestampMs are ignored
@@ -1062,7 +1120,8 @@
             return;
         }
 
-        Intent latencyBroadcast = new Intent(ACTION_NETWORK_CONDITIONS_MEASURED);
+        Intent latencyBroadcast =
+                new Intent(ConnectivityConstants.ACTION_NETWORK_CONDITIONS_MEASURED);
         switch (mNetworkAgentInfo.networkInfo.getType()) {
             case ConnectivityManager.TYPE_WIFI:
                 WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
@@ -1074,15 +1133,18 @@
                     // not change it here as it would become impossible to tell whether the SSID is
                     // simply being surrounded by quotes due to the API, or whether those quotes
                     // are actually part of the SSID.
-                    latencyBroadcast.putExtra(EXTRA_SSID, currentWifiInfo.getSSID());
-                    latencyBroadcast.putExtra(EXTRA_BSSID, currentWifiInfo.getBSSID());
+                    latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_SSID,
+                            currentWifiInfo.getSSID());
+                    latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_BSSID,
+                            currentWifiInfo.getBSSID());
                 } else {
                     if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found");
                     return;
                 }
                 break;
             case ConnectivityManager.TYPE_MOBILE:
-                latencyBroadcast.putExtra(EXTRA_NETWORK_TYPE, mTelephonyManager.getNetworkType());
+                latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_NETWORK_TYPE,
+                        mTelephonyManager.getNetworkType());
                 List<CellInfo> info = mTelephonyManager.getAllCellInfo();
                 if (info == null) return;
                 int numRegisteredCellInfo = 0;
@@ -1096,16 +1158,16 @@
                         }
                         if (cellInfo instanceof CellInfoCdma) {
                             CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
-                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
+                            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
                         } else if (cellInfo instanceof CellInfoGsm) {
                             CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
-                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
+                            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
                         } else if (cellInfo instanceof CellInfoLte) {
                             CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
-                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
+                            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
                         } else if (cellInfo instanceof CellInfoWcdma) {
                             CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
-                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
+                            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
                         } else {
                             if (VDBG) logw("Registered cellinfo is unrecognized");
                             return;
@@ -1116,16 +1178,21 @@
             default:
                 return;
         }
-        latencyBroadcast.putExtra(EXTRA_CONNECTIVITY_TYPE, mNetworkAgentInfo.networkInfo.getType());
-        latencyBroadcast.putExtra(EXTRA_RESPONSE_RECEIVED, responseReceived);
-        latencyBroadcast.putExtra(EXTRA_REQUEST_TIMESTAMP_MS, requestTimestampMs);
+        latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CONNECTIVITY_TYPE,
+                mNetworkAgentInfo.networkInfo.getType());
+        latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_RECEIVED,
+                responseReceived);
+        latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_REQUEST_TIMESTAMP_MS,
+                requestTimestampMs);
 
         if (responseReceived) {
-            latencyBroadcast.putExtra(EXTRA_IS_CAPTIVE_PORTAL, isCaptivePortal);
-            latencyBroadcast.putExtra(EXTRA_RESPONSE_TIMESTAMP_MS, responseTimestampMs);
+            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_IS_CAPTIVE_PORTAL,
+                    isCaptivePortal);
+            latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_TIMESTAMP_MS,
+                    responseTimestampMs);
         }
         mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT,
-                PERMISSION_ACCESS_NETWORK_CONDITIONS);
+                ConnectivityConstants.PERMISSION_ACCESS_NETWORK_CONDITIONS);
     }
 
     private void logNetworkEvent(int evtype) {
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index d56fb1a..3a27fcb3 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -54,12 +54,12 @@
  * @hide
  */
 public class PacManager {
-    public static final String PAC_PACKAGE = "com.android.pacprocessor";
-    public static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
-    public static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
+    private static final String PAC_PACKAGE = "com.android.pacprocessor";
+    private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
+    private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
 
-    public static final String PROXY_PACKAGE = "com.android.proxyhandler";
-    public static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
+    private static final String PROXY_PACKAGE = "com.android.proxyhandler";
+    private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
 
     private static final String TAG = "PacManager";
 
@@ -71,8 +71,6 @@
     private static final int DELAY_LONG = 4;
     private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
 
-    /** Keep these values up-to-date with ProxyService.java */
-    public static final String KEY_PROXY = "keyProxy";
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
     private volatile Uri mPacUrl = Uri.EMPTY;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 7b171b3..69dec2d 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -495,6 +495,7 @@
         Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
         intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
         intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         final long ident = Binder.clearCallingIdentity();
         try {
             mContext.startActivityAsUser(intent, UserHandle.CURRENT);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 3c2d724..bb46d5e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.BIND_VPN_SERVICE;
 import static android.net.ConnectivityManager.NETID_UNSET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.RouteInfo.RTN_THROW;
@@ -163,19 +164,6 @@
     private boolean mLockdown = false;
 
     /**
-     * List of UIDs that are set to use this VPN by default. Normally, every UID in the user is
-     * added to this set but that can be changed by adding allowed or disallowed applications. It
-     * is non-null iff the VPN is connected.
-     *
-     * Unless the VPN has set allowBypass=true, these UIDs are forced into the VPN.
-     *
-     * @see VpnService.Builder#addAllowedApplication(String)
-     * @see VpnService.Builder#addDisallowedApplication(String)
-     */
-    @GuardedBy("this")
-    private Set<UidRange> mVpnUsers = null;
-
-    /**
      * List of UIDs for which networking should be blocked until VPN is ready, during brief periods
      * when VPN is not running. For example, during system startup or after a crash.
      * @see mLockdown
@@ -298,11 +286,13 @@
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         boolean metered = false;
         boolean roaming = false;
+        boolean congested = false;
 
         if (ArrayUtils.isEmpty(underlyingNetworks)) {
             // No idea what the underlying networks are; assume sane defaults
             metered = true;
             roaming = false;
+            congested = false;
         } else {
             for (Network underlying : underlyingNetworks) {
                 final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
@@ -319,22 +309,16 @@
                         underlyingCaps.getLinkUpstreamBandwidthKbps());
                 metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
                 roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+                congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
             }
         }
 
         caps.setTransportTypes(transportTypes);
         caps.setLinkDownstreamBandwidthKbps(downKbps);
         caps.setLinkUpstreamBandwidthKbps(upKbps);
-        if (metered) {
-            caps.removeCapability(NET_CAPABILITY_NOT_METERED);
-        } else {
-            caps.addCapability(NET_CAPABILITY_NOT_METERED);
-        }
-        if (roaming) {
-            caps.removeCapability(NET_CAPABILITY_NOT_ROAMING);
-        } else {
-            caps.addCapability(NET_CAPABILITY_NOT_ROAMING);
-        }
+        caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
+        caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
+        caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
     }
 
     /**
@@ -691,7 +675,7 @@
                 agentDisconnect();
                 jniReset(mInterface);
                 mInterface = null;
-                mVpnUsers = null;
+                mNetworkCapabilities.setUids(null);
             }
 
             // Revoke the connection or stop LegacyVpnRunner.
@@ -860,10 +844,14 @@
         NetworkMisc networkMisc = new NetworkMisc();
         networkMisc.allowBypass = mConfig.allowBypass && !mLockdown;
 
+        mNetworkCapabilities.setEstablishingVpnAppUid(Binder.getCallingUid());
+        mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle,
+                mConfig.allowedApplications, mConfig.disallowedApplications));
         long token = Binder.clearCallingIdentity();
         try {
             mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
-                    mNetworkInfo, mNetworkCapabilities, lp, 0 /* score */, networkMisc) {
+                    mNetworkInfo, mNetworkCapabilities, lp,
+                    ConnectivityConstants.VPN_DEFAULT_SCORE, networkMisc) {
                             @Override
                             public void unwanted() {
                                 // We are user controlled, not driven by NetworkRequest.
@@ -872,11 +860,6 @@
         } finally {
             Binder.restoreCallingIdentity(token);
         }
-
-        mVpnUsers = createUserAndRestrictedProfilesRanges(mUserHandle,
-                mConfig.allowedApplications, mConfig.disallowedApplications);
-        mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
-
         mNetworkInfo.setIsAvailable(true);
         updateState(DetailedState.CONNECTED, "agentConnect");
     }
@@ -956,7 +939,7 @@
         Connection oldConnection = mConnection;
         NetworkAgent oldNetworkAgent = mNetworkAgent;
         mNetworkAgent = null;
-        Set<UidRange> oldUsers = mVpnUsers;
+        Set<UidRange> oldUsers = mNetworkCapabilities.getUids();
 
         // Configure the interface. Abort if any of these steps fails.
         ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
@@ -1014,7 +997,7 @@
             // restore old state
             mConfig = oldConfig;
             mConnection = oldConnection;
-            mVpnUsers = oldUsers;
+            mNetworkCapabilities.setUids(oldUsers);
             mNetworkAgent = oldNetworkAgent;
             mInterface = oldInterface;
             throw e;
@@ -1134,10 +1117,12 @@
 
     // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
     // apply to userHandle.
-    private List<UidRange> uidRangesForUser(int userHandle) {
+    static private List<UidRange> uidRangesForUser(int userHandle, Set<UidRange> existingRanges) {
+        // UidRange#createForUser returns the entire range of UIDs available to a macro-user.
+        // This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE}
         final UidRange userRange = UidRange.createForUser(userHandle);
         final List<UidRange> ranges = new ArrayList<UidRange>();
-        for (UidRange range : mVpnUsers) {
+        for (UidRange range : existingRanges) {
             if (userRange.containsRange(range)) {
                 ranges.add(range);
             }
@@ -1145,30 +1130,18 @@
         return ranges;
     }
 
-    private void removeVpnUserLocked(int userHandle) {
-        if (mVpnUsers == null) {
-            throw new IllegalStateException("VPN is not active");
-        }
-        final List<UidRange> ranges = uidRangesForUser(userHandle);
-        if (mNetworkAgent != null) {
-            mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
-        }
-        mVpnUsers.removeAll(ranges);
-    }
-
     public void onUserAdded(int userHandle) {
         // If the user is restricted tie them to the parent user's VPN
         UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
             synchronized(Vpn.this) {
-                if (mVpnUsers != null) {
+                final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
+                if (existingRanges != null) {
                     try {
-                        addUserToRanges(mVpnUsers, userHandle, mConfig.allowedApplications,
+                        addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications,
                                 mConfig.disallowedApplications);
-                        if (mNetworkAgent != null) {
-                            final List<UidRange> ranges = uidRangesForUser(userHandle);
-                            mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
-                        }
+                        mNetworkCapabilities.setUids(existingRanges);
+                        updateCapabilities();
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to add restricted user to owner", e);
                     }
@@ -1183,9 +1156,14 @@
         UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
             synchronized(Vpn.this) {
-                if (mVpnUsers != null) {
+                final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
+                if (existingRanges != null) {
                     try {
-                        removeVpnUserLocked(userHandle);
+                        final List<UidRange> removedRanges =
+                            uidRangesForUser(userHandle, existingRanges);
+                        existingRanges.removeAll(removedRanges);
+                        mNetworkCapabilities.setUids(existingRanges);
+                        updateCapabilities();
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to remove restricted user to owner", e);
                     }
@@ -1229,15 +1207,6 @@
     private void setVpnForcedLocked(boolean enforce) {
         final List<String> exemptedPackages =
                 isNullOrLegacyVpn(mPackage) ? null : Collections.singletonList(mPackage);
-        setVpnForcedWithExemptionsLocked(enforce, exemptedPackages);
-    }
-
-    /**
-     * @see #setVpnForcedLocked
-     */
-    @GuardedBy("this")
-    private void setVpnForcedWithExemptionsLocked(boolean enforce,
-            @Nullable List<String> exemptedPackages) {
         final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers);
 
         Set<UidRange> addedRanges = Collections.emptySet();
@@ -1317,7 +1286,7 @@
             synchronized (Vpn.this) {
                 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
                     mStatusIntent = null;
-                    mVpnUsers = null;
+                    mNetworkCapabilities.setUids(null);
                     mConfig = null;
                     mInterface = null;
                     if (mConnection != null) {
@@ -1436,12 +1405,7 @@
         if (!isRunningLocked()) {
             return false;
         }
-        for (UidRange uidRange : mVpnUsers) {
-            if (uidRange.contains(uid)) {
-                return true;
-            }
-        }
-        return false;
+        return mNetworkCapabilities.appliesToUid(uid);
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index acbc10b..09bce7f 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -28,6 +28,8 @@
 import android.telephony.TelephonyManager;
 import android.net.util.SharedLog;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -49,6 +51,7 @@
 public class TetheringConfiguration {
     private static final String TAG = TetheringConfiguration.class.getSimpleName();
 
+    @VisibleForTesting
     public static final int DUN_NOT_REQUIRED = 0;
     public static final int DUN_REQUIRED = 1;
     public static final int DUN_UNSPECIFIED = 2;
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index b35ed75..3413291 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Process;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.IpPrefix;
@@ -476,6 +477,7 @@
                        ConnectivityManager.getNetworkTypeName(type));
                 continue;
             }
+            nc.setSingleUid(Process.myUid());
 
             for (NetworkState value : netStates) {
                 if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
old mode 100644
new mode 100755
index 97a6e85..db8dedb
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -228,12 +228,20 @@
                 if (cmd.getOpcode() == Constants.MESSAGE_SET_OSD_NAME) {
                     handleSetOsdName(cmd);
                     return true;
+                } else if ((cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) &&
+                        ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_GIVE_OSD_NAME)) {
+                    handleSetOsdName(cmd);
+                    return true;
                 }
                 return false;
             case STATE_WAITING_FOR_VENDOR_ID:
                 if (cmd.getOpcode() == Constants.MESSAGE_DEVICE_VENDOR_ID) {
                     handleVendorId(cmd);
                     return true;
+                } else if ((cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) &&
+                        ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID)) {
+                    handleVendorId(cmd);
+                    return true;
                 }
                 return false;
             case STATE_WAITING_FOR_DEVICE_POLLING:
@@ -281,7 +289,11 @@
 
         String displayName = null;
         try {
-            displayName = new String(cmd.getParams(), "US-ASCII");
+            if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) {
+                displayName = HdmiUtils.getDefaultDeviceName(current.mLogicalAddress);
+            } else {
+                displayName = new String(cmd.getParams(), "US-ASCII");
+            }
         } catch (UnsupportedEncodingException e) {
             Slog.w(TAG, "Failed to decode display name: " + cmd.toString());
             // If failed to get display name, use the default name of device.
@@ -302,9 +314,12 @@
             return;
         }
 
-        byte[] params = cmd.getParams();
-        int vendorId = HdmiUtils.threeBytesToInt(params);
-        current.mVendorId = vendorId;
+        if (cmd.getOpcode() != Constants.MESSAGE_FEATURE_ABORT) {
+            byte[] params = cmd.getParams();
+            int vendorId = HdmiUtils.threeBytesToInt(params);
+            current.mVendorId = vendorId;
+        }
+
         increaseProcessedDeviceCount();
         checkAndProceedStage();
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 81bccdc..1e09383 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -698,10 +698,9 @@
     protected boolean handleReportAudioStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
 
-        byte params[] = message.getParams();
-        int mute = params[0] & 0x80;
-        int volume = params[0] & 0x7F;
-        setAudioStatus(mute == 0x80, volume);
+        boolean mute = HdmiUtils.isAudioStatusMute(message);
+        int volume = HdmiUtils.getAudioStatusVolume(message);
+        setAudioStatus(mute, volume);
         return true;
     }
 
@@ -1004,6 +1003,9 @@
     }
 
     void setAudioStatus(boolean mute, int volume) {
+        if (!isSystemAudioActivated()) {
+            return;
+        }
         synchronized (mLock) {
             mSystemAudioMute = mute;
             mSystemAudioVolume = volume;
@@ -1019,6 +1021,10 @@
     @ServiceThreadOnly
     void changeVolume(int curVolume, int delta, int maxVolume) {
         assertRunOnServiceThread();
+        if (getAvrDeviceInfo() == null) {
+            // On initialization process, getAvrDeviceInfo() may return null and cause exception
+            return;
+        }
         if (delta == 0 || !isSystemAudioActivated()) {
             return;
         }
@@ -1048,6 +1054,10 @@
     @ServiceThreadOnly
     void changeMute(boolean mute) {
         assertRunOnServiceThread();
+        if (getAvrDeviceInfo() == null) {
+            // On initialization process, getAvrDeviceInfo() may return null and cause exception
+            return;
+        }
         HdmiLogger.debug("[A]:Change mute:%b", mute);
         synchronized (mLock) {
             if (mSystemAudioMute == mute) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 807b1b1..3d079cc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -989,8 +989,12 @@
             }
             // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
             // volume change notification back to hdmi control service.
-            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
-                    AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
+            int flag = AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME;
+            if (0 <= volume && volume <= 100) {
+                Slog.i(TAG, "volume: " + volume);
+                flag |= AudioManager.FLAG_SHOW_UI;
+                audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, flag);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 8b16411..4ac3bba 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -152,6 +152,32 @@
     }
 
     /**
+     * Parse the <Report Audio Status> message and check if it is mute
+     *
+     * @param cmd the CEC message to parse
+     * @return true if the given parameter has [MUTE]
+     */
+    static boolean isAudioStatusMute(HdmiCecMessage cmd) {
+        byte params[] = cmd.getParams();
+        return (params[0] & 0x80) == 0x80;
+    }
+
+    /**
+     * Parse the <Report Audio Status> message and extract the volume
+     *
+     * @param cmd the CEC message to parse
+     * @return device's volume. Constants.UNKNOWN_VOLUME in case it is out of range
+     */
+    static int getAudioStatusVolume(HdmiCecMessage cmd) {
+        byte params[] = cmd.getParams();
+        int volume = params[0] & 0x7F;
+        if (volume < 0x00 || 0x64 < volume) {
+            volume = Constants.UNKNOWN_VOLUME;
+        }
+        return volume;
+    }
+
+    /**
      * Convert integer array to list of {@link Integer}.
      *
      * <p>The result is immutable.
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
index cab8439..d41a36c 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
@@ -92,8 +92,8 @@
 
     private void handleReportAudioStatus(HdmiCecMessage cmd) {
         byte[] params = cmd.getParams();
-        boolean mute = (params[0] & 0x80) == 0x80;
-        int volume = params[0] & 0x7F;
+        boolean mute = HdmiUtils.isAudioStatusMute(cmd);
+        int volume = HdmiUtils.getAudioStatusVolume(cmd);
         tv().setAudioStatus(mute, volume);
 
         if (!(tv().isSystemAudioActivated() ^ mute)) {
diff --git a/services/core/java/com/android/server/hdmi/VolumeControlAction.java b/services/core/java/com/android/server/hdmi/VolumeControlAction.java
index cd38b1f..0011387 100644
--- a/services/core/java/com/android/server/hdmi/VolumeControlAction.java
+++ b/services/core/java/com/android/server/hdmi/VolumeControlAction.java
@@ -139,8 +139,8 @@
 
     private boolean handleReportAudioStatus(HdmiCecMessage cmd) {
         byte params[] = cmd.getParams();
-        boolean mute = (params[0] & 0x80) == 0x80;
-        int volume = params[0] & 0x7F;
+        boolean mute = HdmiUtils.isAudioStatusMute(cmd);
+        int volume = HdmiUtils.getAudioStatusVolume(cmd);
         mLastAvrVolume = volume;
         mLastAvrMute = mute;
         if (shouldUpdateAudioVolume(mute)) {
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 78367fe..1644956 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -201,7 +201,7 @@
         }
     };
 
-    private final INetworkPolicyListener mNetPolicyListener = new INetworkPolicyListener.Stub() {
+    private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() {
         @Override
         public void onUidRulesChanged(int uid, int uidRules) {
             if (DEBUG) {
@@ -211,11 +211,6 @@
         }
 
         @Override
-        public void onMeteredIfacesChanged(String[] meteredIfaces) {
-            // We track this via our NetworkCallback
-        }
-
-        @Override
         public void onRestrictBackgroundChanged(boolean restrictBackground) {
             if (DEBUG) {
                 Slog.v(TAG, "Background restriction change to " + restrictBackground);
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
index ee00fdc..68cd5e7 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java
@@ -39,6 +39,7 @@
     private static final int VERSION_ADD_ROAMING = 2;
     private static final int VERSION_ADD_NETWORK_ID = 3;
     private static final int VERSION_ADD_METERED = 4;
+    private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
 
     public NetworkIdentitySet() {
     }
@@ -76,12 +77,20 @@
                 metered = (type == TYPE_MOBILE);
             }
 
-            add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered));
+            final boolean defaultNetwork;
+            if (version >= VERSION_ADD_DEFAULT_NETWORK) {
+                defaultNetwork = in.readBoolean();
+            } else {
+                defaultNetwork = true;
+            }
+
+            add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+                    defaultNetwork));
         }
     }
 
     public void writeToStream(DataOutputStream out) throws IOException {
-        out.writeInt(VERSION_ADD_METERED);
+        out.writeInt(VERSION_ADD_DEFAULT_NETWORK);
         out.writeInt(size());
         for (NetworkIdentity ident : this) {
             out.writeInt(ident.getType());
@@ -90,6 +99,7 @@
             writeOptionalString(out, ident.getNetworkId());
             out.writeBoolean(ident.getRoaming());
             out.writeBoolean(ident.getMetered());
+            out.writeBoolean(ident.getDefaultNetwork());
         }
     }
 
@@ -119,6 +129,20 @@
         return false;
     }
 
+    /** @return whether any {@link NetworkIdentity} in this set is considered on the default
+            network. */
+    public boolean areAllMembersOnDefaultNetwork() {
+        if (isEmpty()) {
+            return true;
+        }
+        for (NetworkIdentity ident : this) {
+            if (!ident.getDefaultNetwork()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     private static void writeOptionalString(DataOutputStream out, String value) throws IOException {
         if (value != null) {
             out.writeByte(1);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5159c70..948e75e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1103,7 +1103,8 @@
             for (int subId : subIds) {
                 final String subscriberId = tele.getSubscriberId(subId);
                 final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                        TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
+                        TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
+                        true);
                 if (template.matches(probeIdent)) {
                     return true;
                 }
@@ -1304,7 +1305,7 @@
 
         // find and update the mobile NetworkPolicy for this subscriber id
         final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true);
         for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
             final NetworkTemplate template = mNetworkPolicy.keyAt(i);
             if (template.matches(probeIdent)) {
@@ -1511,7 +1512,8 @@
             for (int subId : subIds) {
                 final String subscriberId = tm.getSubscriberId(subId);
                 final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                        TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
+                        TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
+                        true);
                 // Template is matched when subscriber id matches.
                 if (template.matches(probeIdent)) {
                     tm.setPolicyDataEnabled(enabled, subId);
@@ -1557,7 +1559,8 @@
         final ArrayMap<NetworkState, NetworkIdentity> identified = new ArrayMap<>();
         for (NetworkState state : states) {
             if (state.networkInfo != null && state.networkInfo.isConnected()) {
-                final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
+                final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+                        true);
                 identified.put(state, ident);
             }
         }
@@ -1692,7 +1695,7 @@
     private boolean ensureActiveMobilePolicyAL(int subId, String subscriberId) {
         // Poke around to see if we already have a policy
         final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true);
         for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
             final NetworkTemplate template = mNetworkPolicy.keyAt(i);
             if (template.matches(probeIdent)) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 4ceb592..961a451 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -17,6 +17,8 @@
 package com.android.server.net;
 
 import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
 import static android.net.NetworkStats.ROAMING_NO;
@@ -364,6 +366,8 @@
                 entry.uid = key.uid;
                 entry.set = key.set;
                 entry.tag = key.tag;
+                entry.defaultNetwork = key.ident.areAllMembersOnDefaultNetwork() ?
+                        DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO;
                 entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO;
                 entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
                 entry.rxBytes = historyEntry.rxBytes;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index db61ef5..bfc150e 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -25,6 +25,7 @@
 import static android.content.Intent.EXTRA_UID;
 import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.ConnectivityManager.isNetworkTypeMobile;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.METERED_ALL;
 import static android.net.NetworkStats.ROAMING_ALL;
@@ -83,6 +84,7 @@
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
 import android.net.LinkProperties;
+import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
 import android.net.NetworkInfo;
@@ -231,14 +233,24 @@
     private final Object mStatsLock = new Object();
 
     /** Set of currently active ifaces. */
+    @GuardedBy("mStatsLock")
     private final ArrayMap<String, NetworkIdentitySet> mActiveIfaces = new ArrayMap<>();
+
     /** Set of currently active ifaces for UID stats. */
+    @GuardedBy("mStatsLock")
     private final ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces = new ArrayMap<>();
+
     /** Current default active iface. */
     private String mActiveIface;
+
     /** Set of any ifaces associated with mobile networks since boot. */
+    @GuardedBy("mStatsLock")
     private String[] mMobileIfaces = new String[0];
 
+    /** Set of all ifaces currently used by traffic that does not explicitly specify a Network. */
+    @GuardedBy("mStatsLock")
+    private Network[] mDefaultNetworks = new Network[0];
+
     private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
             new DropBoxNonMonotonicObserver();
 
@@ -666,9 +678,9 @@
         final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
 
         final NetworkStats stats = new NetworkStats(end - start, 1);
-        stats.addValues(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL,
-                ROAMING_ALL, entry.rxBytes, entry.rxPackets, entry.txBytes, entry.txPackets,
-                entry.operations));
+        stats.addValues(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
+                METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, entry.rxBytes, entry.rxPackets,
+                entry.txBytes, entry.txPackets, entry.operations));
         return stats;
     }
 
@@ -779,13 +791,13 @@
     }
 
     @Override
-    public void forceUpdateIfaces() {
+    public void forceUpdateIfaces(Network[] defaultNetworks) {
         mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
         assertBandwidthControlEnabled();
 
         final long token = Binder.clearCallingIdentity();
         try {
-            updateIfaces();
+            updateIfaces(defaultNetworks);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -875,17 +887,21 @@
 
     @Override
     public long getUidStats(int uid, int type) {
-        return nativeGetUidStat(uid, type);
+        return nativeGetUidStat(uid, type, checkBpfStatsEnable());
     }
 
     @Override
     public long getIfaceStats(String iface, int type) {
-        return nativeGetIfaceStat(iface, type);
+        return nativeGetIfaceStat(iface, type, checkBpfStatsEnable());
     }
 
     @Override
     public long getTotalStats(int type) {
-        return nativeGetTotalStat(type);
+        return nativeGetTotalStat(type, checkBpfStatsEnable());
+    }
+
+    private boolean checkBpfStatsEnable() {
+        return new File("/sys/fs/bpf/traffic_uid_stats_map").exists();
     }
 
     /**
@@ -996,11 +1012,11 @@
         }
     };
 
-    private void updateIfaces() {
+    private void updateIfaces(Network[] defaultNetworks) {
         synchronized (mStatsLock) {
             mWakeLock.acquire();
             try {
-                updateIfacesLocked();
+                updateIfacesLocked(defaultNetworks);
             } finally {
                 mWakeLock.release();
             }
@@ -1013,7 +1029,7 @@
      * are active on a single {@code iface}, they are combined under a single
      * {@link NetworkIdentitySet}.
      */
-    private void updateIfacesLocked() {
+    private void updateIfacesLocked(Network[] defaultNetworks) {
         if (!mSystemReady) return;
         if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
 
@@ -1040,12 +1056,18 @@
         // Rebuild active interfaces based on connected networks
         mActiveIfaces.clear();
         mActiveUidIfaces.clear();
+        if (defaultNetworks != null) {
+            // Caller is ConnectivityService. Update the list of default networks.
+            mDefaultNetworks = defaultNetworks;
+        }
 
         final ArraySet<String> mobileIfaces = new ArraySet<>();
         for (NetworkState state : states) {
             if (state.networkInfo.isConnected()) {
                 final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType());
-                final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
+                final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
+                final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+                        isDefault);
 
                 // Traffic occurring on the base interface is always counted for
                 // both total usage and UID details.
@@ -1065,7 +1087,8 @@
                         // Copy the identify from IMS one but mark it as metered.
                         NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
                                 ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
-                                ident.getRoaming(), true);
+                                ident.getRoaming(), true /* metered */,
+                                true /* onDefaultNetwork */);
                         findOrCreateNetworkIdentitySet(mActiveIfaces, VT_INTERFACE).add(vtIdent);
                         findOrCreateNetworkIdentitySet(mActiveUidIfaces, VT_INTERFACE).add(vtIdent);
                     }
@@ -1511,7 +1534,7 @@
                     return true;
                 }
                 case MSG_UPDATE_IFACES: {
-                    mService.updateIfaces();
+                    mService.updateIfaces(null);
                     return true;
                 }
                 case MSG_REGISTER_GLOBAL_ALERT: {
@@ -1649,7 +1672,7 @@
     private static int TYPE_TCP_RX_PACKETS;
     private static int TYPE_TCP_TX_PACKETS;
 
-    private static native long nativeGetTotalStat(int type);
-    private static native long nativeGetIfaceStat(String iface, int type);
-    private static native long nativeGetUidStat(int uid, int type);
+    private static native long nativeGetTotalStat(int type, boolean useBpfStats);
+    private static native long nativeGetIfaceStat(String iface, int type, boolean useBpfStats);
+    private static native long nativeGetUidStat(int uid, int type, boolean useBpfStats);
 }
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b7b91a7..625764c 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -39,8 +39,10 @@
 import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -82,6 +84,7 @@
     protected final String TAG = getClass().getSimpleName();
     protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
     protected static final String ENABLED_SERVICES_SEPARATOR = ":";
 
     /**
@@ -101,12 +104,15 @@
     private final IPackageManager mPm;
     private final UserManager mUm;
     private final Config mConfig;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     // contains connections to all connected services, including app services
     // and system services
     private final ArrayList<ManagedServiceInfo> mServices = new ArrayList<>();
     // things that will be put into mServices as soon as they're ready
     private final ArrayList<String> mServicesBinding = new ArrayList<>();
+    private final ArraySet<String> mServicesRebinding = new ArraySet<>();
+
     // lists the component names of all enabled (and therefore potentially connected)
     // app services for current profiles.
     private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
@@ -823,6 +829,7 @@
 
         final String servicesBindingTag = name.toString() + "/" + userid;
         if (mServicesBinding.contains(servicesBindingTag)) {
+            Slog.v(TAG, "Not registering " + name + " as bind is already in progress");
             // stop registering this thing already! we're working on it
             return;
         }
@@ -871,6 +878,7 @@
                     boolean added = false;
                     ManagedServiceInfo info = null;
                     synchronized (mMutex) {
+                        mServicesRebinding.remove(servicesBindingTag);
                         mServicesBinding.remove(servicesBindingTag);
                         try {
                             mService = asInterface(binder);
@@ -892,6 +900,27 @@
                     mServicesBinding.remove(servicesBindingTag);
                     Slog.v(TAG, getCaption() + " connection lost: " + name);
                 }
+
+                @Override
+                public void onBindingDied(ComponentName name) {
+                    Slog.w(TAG, getCaption() + " binding died: " + name);
+                    synchronized (mMutex) {
+                        mServicesBinding.remove(servicesBindingTag);
+                        mContext.unbindService(this);
+                        if (!mServicesRebinding.contains(servicesBindingTag)) {
+                            mServicesRebinding.add(servicesBindingTag);
+                            mHandler.postDelayed(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        registerService(name, userid);
+                                    }
+                               }, ON_BINDING_DIED_REBIND_DELAY_MS);
+                        } else {
+                            Slog.v(TAG, getCaption() + " not rebinding as "
+                                    + "a previous rebind attempt was made: " + name);
+                        }
+                    }
+                }
             };
             if (!mContext.bindServiceAsUser(intent,
                 serviceConnection,
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 210eb13..2cd128d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -58,6 +58,9 @@
     public static final int DEXOPT_STORAGE_DE     = 1 << 8;
     /** Indicates that dexopt is invoked from the background service. */
     public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
+    /* Indicates that dexopt should not restrict access to private APIs.
+     * Must be kept in sync with com.android.internal.os.ZygoteInit. */
+    public static final int DEXOPT_DISABLE_HIDDEN_API_CHECKS = 1 << 10;
 
     // NOTE: keep in sync with installd
     public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
@@ -281,13 +284,14 @@
     public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
             int dexoptNeeded, @Nullable String outputPath, int dexFlags,
             String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
-            @Nullable String seInfo, boolean downgrade)
+            @Nullable String seInfo, boolean downgrade, int targetSdkVersion)
             throws InstallerException {
         assertValidInstructionSet(instructionSet);
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
-                    dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade);
+                    dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
+                    targetSdkVersion);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 6253857..5dbd3ca 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -260,12 +260,13 @@
             public void dexopt(String apkPath, int uid, @Nullable String pkgName,
                     String instructionSet, int dexoptNeeded, @Nullable String outputPath,
                     int dexFlags, String compilerFilter, @Nullable String volumeUuid,
-                    @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade)
+                    @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
+                    int targetSdkVersion)
                     throws InstallerException {
                 final StringBuilder builder = new StringBuilder();
 
-                // The version. Right now it's 3.
-                builder.append("3 ");
+                // The version. Right now it's 4.
+                builder.append("4 ");
 
                 builder.append("dexopt");
 
@@ -281,6 +282,7 @@
                 encodeParameter(builder, sharedLibraries);
                 encodeParameter(builder, seInfo);
                 encodeParameter(builder, downgrade);
+                encodeParameter(builder, targetSdkVersion);
 
                 commands.add(builder.toString());
             }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 300f15f..1f219c1 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -55,6 +55,7 @@
 import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
 import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
 import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
+import static com.android.server.pm.Installer.DEXOPT_DISABLE_HIDDEN_API_CHECKS;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 
@@ -274,7 +275,7 @@
             // primary dex files.
             mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
                     compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
-                    false /* downgrade*/);
+                    false /* downgrade*/, pkg.applicationInfo.targetSdkVersion);
 
             if (packageStats != null) {
                 long endTime = System.currentTimeMillis();
@@ -395,7 +396,7 @@
                 mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
                         /*oatDir*/ null, dexoptFlags,
                         compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
-                        options.isDowngrade());
+                        options.isDowngrade(), info.targetSdkVersion);
             }
 
             return DEX_OPT_PERFORMED;
@@ -509,12 +510,18 @@
         boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
         boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter;
         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
+        // System apps are invoked with a runtime flag which exempts them from
+        // restrictions on hidden API usage. We dexopt with the same runtime flag
+        // otherwise offending methods would have to be re-verified at runtime
+        // and we want to avoid the performance overhead of that.
+        int hiddenApiFlag = info.isAllowedToUseHiddenApi() ? DEXOPT_DISABLE_HIDDEN_API_CHECKS : 0;
         int dexFlags =
                 (isPublic ? DEXOPT_PUBLIC : 0)
                 | (debuggable ? DEXOPT_DEBUGGABLE : 0)
                 | profileFlag
                 | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
-                | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
+                | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
+                | hiddenApiFlag;
         return adjustDexoptFlags(dexFlags);
     }
 
@@ -629,6 +636,9 @@
         if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) {
             flagsList.add("idle_background_job");
         }
+        if ((flags & DEXOPT_DISABLE_HIDDEN_API_CHECKS) == DEXOPT_DISABLE_HIDDEN_API_CHECKS) {
+            flagsList.add("disable_hidden_api_checks");
+        }
 
         return String.join(",", flagsList);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f182c05..6a53e48 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -426,6 +426,7 @@
     private static final int NFC_UID = Process.NFC_UID;
     private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
     private static final int SHELL_UID = Process.SHELL_UID;
+    private static final int SE_UID = Process.SE_UID;
 
     // Cap the size of permission trees that 3rd party apps can define
     private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;     // characters of text
@@ -2436,6 +2437,8 @@
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
         mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+        mSettings.addSharedUserLPw("android.uid.se", SE_UID,
+                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
 
         String separateProcesses = SystemProperties.get("debug.separate_processes");
         if (separateProcesses != null && separateProcesses.length() > 0) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 04fd3e3..fc2ea60 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -95,6 +95,8 @@
         "libhwbinder",
         "libutils",
         "libhwui",
+        "libbpf",
+        "libnetdutils",
         "android.hardware.audio.common@2.0",
         "android.hardware.broadcastradio@1.0",
         "android.hardware.broadcastradio@1.1",
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 8de24e5..3302dea 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -29,6 +29,15 @@
 #include <utils/misc.h>
 #include <utils/Log.h>
 
+#include "android-base/unique_fd.h"
+#include "bpf/BpfNetworkStats.h"
+#include "bpf/BpfUtils.h"
+
+using android::bpf::Stats;
+using android::bpf::hasBpfSupport;
+using android::bpf::bpfGetUidStats;
+using android::bpf::bpfGetIfaceStats;
+
 namespace android {
 
 static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt";
@@ -46,15 +55,6 @@
     TCP_TX_PACKETS = 5
 };
 
-struct Stats {
-    uint64_t rxBytes;
-    uint64_t rxPackets;
-    uint64_t txBytes;
-    uint64_t txPackets;
-    uint64_t tcpRxPackets;
-    uint64_t tcpTxPackets;
-};
-
 static uint64_t getStatsType(struct Stats* stats, StatsType type) {
     switch (type) {
         case RX_BYTES:
@@ -150,9 +150,18 @@
     return 0;
 }
 
-static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
+static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) {
     struct Stats stats;
     memset(&stats, 0, sizeof(Stats));
+
+    if (useBpfStats) {
+        if (bpfGetIfaceStats(NULL, &stats) == 0) {
+            return getStatsType(&stats, (StatsType) type);
+        } else {
+            return UNKNOWN;
+        }
+    }
+
     if (parseIfaceStats(NULL, &stats) == 0) {
         return getStatsType(&stats, (StatsType) type);
     } else {
@@ -160,7 +169,8 @@
     }
 }
 
-static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
+static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type,
+                          jboolean useBpfStats) {
     ScopedUtfChars iface8(env, iface);
     if (iface8.c_str() == NULL) {
         return UNKNOWN;
@@ -168,6 +178,15 @@
 
     struct Stats stats;
     memset(&stats, 0, sizeof(Stats));
+
+    if (useBpfStats) {
+        if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
+            return getStatsType(&stats, (StatsType) type);
+        } else {
+            return UNKNOWN;
+        }
+    }
+
     if (parseIfaceStats(iface8.c_str(), &stats) == 0) {
         return getStatsType(&stats, (StatsType) type);
     } else {
@@ -175,9 +194,18 @@
     }
 }
 
-static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
+static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) {
     struct Stats stats;
     memset(&stats, 0, sizeof(Stats));
+
+    if (useBpfStats) {
+        if (bpfGetUidStats(uid, &stats) == 0) {
+            return getStatsType(&stats, (StatsType) type);
+        } else {
+            return UNKNOWN;
+        }
+    }
+
     if (parseUidStats(uid, &stats) == 0) {
         return getStatsType(&stats, (StatsType) type);
     } else {
@@ -186,9 +214,9 @@
 }
 
 static const JNINativeMethod gMethods[] = {
-    {"nativeGetTotalStat", "(I)J", (void*) getTotalStat},
-    {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*) getIfaceStat},
-    {"nativeGetUidStat", "(II)J", (void*) getUidStat},
+    {"nativeGetTotalStat", "(IZ)J", (void*) getTotalStat},
+    {"nativeGetIfaceStat", "(Ljava/lang/String;IZ)J", (void*) getIfaceStat},
+    {"nativeGetUidStat", "(IIZ)J", (void*) getUidStat},
 };
 
 int register_android_server_net_NetworkStatsService(JNIEnv* env) {
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 5a3a8be..984c9f8 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -121,6 +121,14 @@
     public static final int ICMP_ECHO_DATA_OFFSET = 8;
 
     /**
+     * ICMPv4 constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc792
+     */
+    public static final int ICMPV4_ECHO_REQUEST_TYPE = 8;
+
+    /**
      * ICMPv6 constants.
      *
      * See also:
@@ -139,6 +147,8 @@
     public static final int ICMPV6_ND_OPTION_TLLA = 2;
     public static final int ICMPV6_ND_OPTION_MTU  = 5;
 
+    public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
+
     /**
      * UDP constants.
      *
@@ -157,6 +167,14 @@
     public static final int DHCP4_CLIENT_PORT = 68;
 
     /**
+     * DNS constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc1035
+     */
+    public static final int DNS_SERVER_PORT = 53;
+
+    /**
      * Utility functions.
      */
     public static byte asByte(int i) { return (byte) i; }
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 0cd9ac3..feb7b76 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -10,5 +10,6 @@
     static_libs: [
         "android.hardware.usb-V1.0-java",
         "android.hardware.usb-V1.1-java",
+        "android.hardware.usb.gadget-V1.0-java",
     ],
 }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 1b057f9..e3e5e3e 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -16,6 +16,9 @@
 
 package com.android.server.usb;
 
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
@@ -26,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -37,12 +41,21 @@
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.gadget.V1_0.GadgetFunction;
+import android.hardware.usb.gadget.V1_0.IUsbGadget;
+import android.hardware.usb.gadget.V1_0.IUsbGadgetCallback;
+import android.hardware.usb.gadget.V1_0.Status;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
 import android.os.BatteryManager;
+import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.HwBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UEventObserver;
@@ -60,6 +73,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.FgThread;
+import com.android.server.LocalServices;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -69,32 +83,24 @@
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Scanner;
 import java.util.Set;
+import java.util.StringJoiner;
 
 /**
  * UsbDeviceManager manages USB state in device mode.
  */
-public class UsbDeviceManager {
+public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver {
 
     private static final String TAG = "UsbDeviceManager";
     private static final boolean DEBUG = false;
 
     /**
-     * The persistent property which stores whether adb is enabled or not.
-     * May also contain vendor-specific default functions for testing purposes.
+     * The SharedPreference setting per user that stores the screen unlocked functions between
+     * sessions.
      */
-    private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
-
-    /**
-     * The non-persistent property which stores the current USB settings.
-     */
-    private static final String USB_CONFIG_PROPERTY = "sys.usb.config";
-
-    /**
-     * The non-persistent property which stores the current USB actual state.
-     */
-    private static final String USB_STATE_PROPERTY = "sys.usb.state";
+    private static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d";
 
     /**
      * ro.bootmode value when phone boots into usual Android.
@@ -128,6 +134,12 @@
     private static final int MSG_UPDATE_CHARGING_STATE = 9;
     private static final int MSG_UPDATE_HOST_STATE = 10;
     private static final int MSG_LOCALE_CHANGED = 11;
+    private static final int MSG_SET_SCREEN_UNLOCKED_FUNCTIONS = 12;
+    private static final int MSG_UPDATE_SCREEN_LOCK = 13;
+    private static final int MSG_SET_CHARGING_FUNCTIONS = 14;
+    private static final int MSG_SET_FUNCTIONS_TIMEOUT = 15;
+    private static final int MSG_GET_CURRENT_USB_FUNCTIONS = 16;
+    private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17;
 
     private static final int AUDIO_MODE_SOURCE = 1;
 
@@ -143,9 +155,9 @@
     private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
 
     private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
-
     private UsbHandler mHandler;
     private boolean mBootCompleted;
+    private boolean mSystemReady;
 
     private final Object mLock = new Object();
 
@@ -161,7 +173,6 @@
     private boolean mMidiEnabled;
     private int mMidiCard;
     private int mMidiDevice;
-    private HashMap<String, HashMap<String, Pair<String, String>>> mOemModeMap;
     private String[] mAccessoryStrings;
     private UsbDebuggingManager mDebuggingManager;
     private final UsbAlsaManager mUsbAlsaManager;
@@ -169,6 +180,7 @@
     private Intent mBroadcastedIntent;
     private boolean mPendingBootBroadcast;
     private static Set<Integer> sBlackListedInterfaces;
+    private SharedPreferences mSettings;
 
     static {
         sBlackListedInterfaces = new HashSet<>();
@@ -217,6 +229,31 @@
         }
     };
 
+    @Override
+    public void onKeyguardStateChanged(boolean isShowing) {
+        int userHandle = ActivityManager.getCurrentUser();
+        boolean secure = mContext.getSystemService(KeyguardManager.class)
+                .isDeviceSecure(userHandle);
+        boolean unlocking = mContext.getSystemService(UserManager.class)
+                .isUserUnlockingOrUnlocked(userHandle);
+        if (DEBUG) {
+            Slog.v(TAG, "onKeyguardStateChanged: isShowing:" + isShowing + " secure:" + secure
+                    + " unlocking:" + unlocking + " user:" + userHandle);
+        }
+        // We are unlocked when the keyguard is down or non-secure, and user storage is unlocked.
+        mHandler.sendMessage(MSG_UPDATE_SCREEN_LOCK, (isShowing && secure) || !unlocking);
+    }
+
+    @Override
+    public void onAwakeStateChanged(boolean isAwake) {
+        // ignore
+    }
+
+    /** Called when a user is unlocked. */
+    public void onUnlockUser(int userHandle) {
+        onKeyguardStateChanged(false);
+    }
+
     public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
             UsbSettingsManager settingsManager) {
         mContext = context;
@@ -227,9 +264,27 @@
         mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
         initRndisAddress();
 
-        readOemUsbOverrideConfig();
+        boolean halNotPresent = false;
+        try {
+            IUsbGadget.getService(true);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "USB GADGET HAL present but exception thrown", e);
+        } catch (NoSuchElementException e) {
+            halNotPresent = true;
+            Slog.i(TAG, "USB GADGET HAL not present in the device", e);
+        }
 
-        mHandler = new UsbHandler(FgThread.get().getLooper());
+        if (halNotPresent) {
+            /**
+             * Initialze the legacy UsbHandler
+             */
+            mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext);
+        } else {
+            /**
+             * Initialize HAL based UsbHandler
+             */
+            mHandler = new UsbHandlerHal(FgThread.get().getLooper());
+        }
 
         if (nativeIsStartRequested()) {
             if (DEBUG) Slog.d(TAG, "accessory attached at boot");
@@ -303,6 +358,8 @@
     public void systemReady() {
         if (DEBUG) Slog.d(TAG, "systemReady");
 
+        LocalServices.getService(ActivityManagerInternal.class).registerScreenObserver(this);
+
         mNotificationManager = (NotificationManager)
                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
 
@@ -325,15 +382,6 @@
         massStorageSupported = primary != null && primary.allowMassStorage();
         mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_usbChargingMessage);
-
-        // make sure the ADB_ENABLED setting value matches the current state
-        try {
-            Settings.Global.putInt(mContentResolver,
-                    Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
-        } catch (SecurityException e) {
-            // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed.
-            Slog.d(TAG, "ADB_ENABLED is restricted.");
-        }
         mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
     }
 
@@ -407,7 +455,15 @@
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
     }
 
-    private final class UsbHandler extends Handler {
+    private SharedPreferences getPinnedSharedPrefs(Context context) {
+        final File prefsFile = new File(new File(
+                Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
+                        context.getUserId(), context.getPackageName()), "shared_prefs"),
+                UsbDeviceManager.class.getSimpleName() + ".xml");
+        return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+    }
+
+    private abstract class UsbHandler extends Handler {
 
         // current USB state
         private boolean mConnected;
@@ -415,73 +471,53 @@
         private boolean mSourcePower;
         private boolean mSinkPower;
         private boolean mConfigured;
-        private boolean mUsbDataUnlocked;
+        protected boolean mUsbDataUnlocked;
         private boolean mAudioAccessoryConnected;
         private boolean mAudioAccessorySupported;
-        private String mCurrentFunctions;
-        private boolean mCurrentFunctionsApplied;
+        protected String mCurrentFunctions;
+        protected boolean mCurrentFunctionsApplied;
         private UsbAccessory mCurrentAccessory;
         private int mUsbNotificationId;
         private boolean mAdbNotificationShown;
-        private int mCurrentUser = UserHandle.USER_NULL;
+        private int mCurrentUser;
         private boolean mUsbCharging;
-        private String mCurrentOemFunctions;
         private boolean mHideUsbNotification;
         private boolean mSupportsAllCombinations;
+        private String mScreenUnlockedFunctions = UsbManager.USB_FUNCTION_NONE;
+        private boolean mScreenLocked;
+        protected boolean mCurrentUsbFunctionsRequested;
+        protected boolean mCurrentUsbFunctionsReceived;
+
+        /**
+         * The persistent property which stores whether adb is enabled or not.
+         * May also contain vendor-specific default functions for testing purposes.
+         */
+        protected static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
 
         public UsbHandler(Looper looper) {
             super(looper);
-            try {
-                // Restore default functions.
 
-                mCurrentOemFunctions = SystemProperties.get(UsbDeviceManager.getPersistProp(false),
-                        UsbManager.USB_FUNCTION_NONE);
-                if (isNormalBoot()) {
-                    mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY,
-                            UsbManager.USB_FUNCTION_NONE);
-                    mCurrentFunctionsApplied = mCurrentFunctions.equals(
-                            SystemProperties.get(USB_STATE_PROPERTY));
-                } else {
-                    mCurrentFunctions = SystemProperties.get(getPersistProp(true),
-                            UsbManager.USB_FUNCTION_NONE);
-                    mCurrentFunctionsApplied = SystemProperties.get(USB_CONFIG_PROPERTY,
-                            UsbManager.USB_FUNCTION_NONE).equals(
-                            SystemProperties.get(USB_STATE_PROPERTY));
-                }
+            mCurrentUser = ActivityManager.getCurrentUser();
+            mScreenLocked = true;
 
-                /*
-                 * Use the normal bootmode persistent prop to maintain state of adb across
-                 * all boot modes.
-                 */
-                mAdbEnabled = UsbManager.containsFunction(
-                        SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY),
-                        UsbManager.USB_FUNCTION_ADB);
+            /*
+             * Use the normal bootmode persistent prop to maintain state of adb across
+             * all boot modes.
+             */
+            mAdbEnabled = UsbManager.containsFunction(
+                    SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY),
+                    UsbManager.USB_FUNCTION_ADB);
 
-                /*
-                 * Previous versions can set persist config to mtp/ptp but it does not
-                 * get reset on OTA. Reset the property here instead.
-                 */
-                String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY);
-                if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP)
-                        || UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_PTP)) {
-                    SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY,
-                            UsbManager.removeFunction(UsbManager.removeFunction(persisted,
-                                    UsbManager.USB_FUNCTION_MTP), UsbManager.USB_FUNCTION_PTP));
-                }
-
-                String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
-                updateState(state);
-
-                // register observer to listen for settings changes
-                mContentResolver.registerContentObserver(
-                        Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
-                        false, new AdbSettingsObserver());
-
-                // Watch for USB configuration changes
-                mUEventObserver.startObserving(USB_STATE_MATCH);
-                mUEventObserver.startObserving(ACCESSORY_START_MATCH);
-            } catch (Exception e) {
-                Slog.e(TAG, "Error initializing UsbHandler", e);
+            /*
+             * Previous versions can set persist config to mtp/ptp but it does not
+             * get reset on OTA. Reset the property here instead.
+             */
+            String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY);
+            if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP)
+                    || UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_PTP)) {
+                SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY,
+                        UsbManager.removeFunction(UsbManager.removeFunction(persisted,
+                                UsbManager.USB_FUNCTION_MTP), UsbManager.USB_FUNCTION_PTP));
             }
         }
 
@@ -507,6 +543,21 @@
             sendMessage(m);
         }
 
+        public void sendMessage(int what, boolean arg1, boolean arg2) {
+            removeMessages(what);
+            Message m = Message.obtain(this, what);
+            m.arg1 = (arg1 ? 1 : 0);
+            m.arg2 = (arg2 ? 1 : 0);
+            sendMessage(m);
+        }
+
+        public void sendMessageDelayed(int what, boolean arg, long delayMillis) {
+            removeMessages(what);
+            Message m = Message.obtain(this, what);
+            m.arg1 = (arg ? 1 : 0);
+            sendMessageDelayed(m, delayMillis);
+        }
+
         public void updateState(String state) {
             int connected, configured;
 
@@ -524,6 +575,7 @@
                 return;
             }
             removeMessages(MSG_UPDATE_STATE);
+            if (connected == 1) removeMessages(MSG_FUNCTION_SWITCH_TIMEOUT);
             Message msg = Message.obtain(this, MSG_UPDATE_STATE);
             msg.arg1 = connected;
             msg.arg2 = configured;
@@ -546,28 +598,6 @@
             sendMessageDelayed(msg, UPDATE_DELAY);
         }
 
-        private boolean waitForState(String state) {
-            // wait for the transition to complete.
-            // give up after 1 second.
-            String value = null;
-            for (int i = 0; i < 20; i++) {
-                // State transition is done when sys.usb.state is set to the new configuration
-                value = SystemProperties.get(USB_STATE_PROPERTY);
-                if (state.equals(value)) return true;
-                SystemClock.sleep(50);
-            }
-            Slog.e(TAG, "waitForState(" + state + ") FAILED: got " + value);
-            return false;
-        }
-
-        private void setUsbConfig(String config) {
-            if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
-            // set the new configuration
-            // we always set it due to b/23631400, where adbd was getting killed
-            // and not restarted due to property timeouts on some devices
-            SystemProperties.set(USB_CONFIG_PROPERTY, config);
-        }
-
         private void setAdbEnabled(boolean enable) {
             if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
             if (enable != mAdbEnabled) {
@@ -594,114 +624,7 @@
             }
         }
 
-        /**
-         * Evaluates USB function policies and applies the change accordingly.
-         */
-        private void setEnabledFunctions(String functions, boolean forceRestart,
-                boolean usbDataUnlocked) {
-            if (DEBUG) {
-                Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
-                        + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
-            }
-
-            if (usbDataUnlocked != mUsbDataUnlocked) {
-                mUsbDataUnlocked = usbDataUnlocked;
-                updateUsbNotification(false);
-                forceRestart = true;
-            }
-
-            // Try to set the enabled functions.
-            final String oldFunctions = mCurrentFunctions;
-            final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
-            if (trySetEnabledFunctions(functions, forceRestart)) {
-                return;
-            }
-
-            // Didn't work.  Try to revert changes.
-            // We always reapply the policy in case certain constraints changed such as
-            // user restrictions independently of any other new functions we were
-            // trying to activate.
-            if (oldFunctionsApplied && !oldFunctions.equals(functions)) {
-                Slog.e(TAG, "Failsafe 1: Restoring previous USB functions.");
-                if (trySetEnabledFunctions(oldFunctions, false)) {
-                    return;
-                }
-            }
-
-            // Still didn't work.  Try to restore the default functions.
-            Slog.e(TAG, "Failsafe 2: Restoring default USB functions.");
-            if (trySetEnabledFunctions(null, false)) {
-                return;
-            }
-
-            // Now we're desperate.  Ignore the default functions.
-            // Try to get ADB working if enabled.
-            Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled).");
-            if (trySetEnabledFunctions(UsbManager.USB_FUNCTION_NONE, false)) {
-                return;
-            }
-
-            // Ouch.
-            Slog.e(TAG, "Unable to set any USB functions!");
-        }
-
-        private boolean isNormalBoot() {
-            String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
-            return bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown");
-        }
-
-        private boolean trySetEnabledFunctions(String functions, boolean forceRestart) {
-            if (functions == null || applyAdbFunction(functions)
-                    .equals(UsbManager.USB_FUNCTION_NONE)) {
-                functions = getDefaultFunctions();
-            }
-            functions = applyAdbFunction(functions);
-
-            String oemFunctions = applyOemOverrideFunction(functions);
-
-            if (!isNormalBoot() && !mCurrentFunctions.equals(functions)) {
-                SystemProperties.set(getPersistProp(true), functions);
-            }
-
-            if ((!functions.equals(oemFunctions) &&
-                            !mCurrentOemFunctions.equals(oemFunctions))
-                    || !mCurrentFunctions.equals(functions)
-                    || !mCurrentFunctionsApplied
-                    || forceRestart) {
-                Slog.i(TAG, "Setting USB config to " + functions);
-                mCurrentFunctions = functions;
-                mCurrentOemFunctions = oemFunctions;
-                mCurrentFunctionsApplied = false;
-
-                // Kick the USB stack to close existing connections.
-                setUsbConfig(UsbManager.USB_FUNCTION_NONE);
-
-                if (!waitForState(UsbManager.USB_FUNCTION_NONE)) {
-                    Slog.e(TAG, "Failed to kick USB config");
-                    return false;
-                }
-
-                // Set the new USB configuration.
-                setUsbConfig(oemFunctions);
-
-                if (mBootCompleted
-                        && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
-                        || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
-                    // Start up dependent services.
-                    updateUsbStateBroadcastIfNeeded(true);
-                }
-
-                if (!waitForState(oemFunctions)) {
-                    Slog.e(TAG, "Failed to switch USB config to " + functions);
-                    return false;
-                }
-
-                mCurrentFunctionsApplied = true;
-            }
-            return true;
-        }
-
-        private String applyAdbFunction(String functions) {
+        protected String applyAdbFunction(String functions) {
             // Do not pass null pointer to the UsbManager.
             // There isnt a check there.
             if (functions == null) {
@@ -783,7 +706,7 @@
             return false;
         }
 
-        private void updateUsbStateBroadcastIfNeeded(boolean configChanged) {
+        protected void updateUsbStateBroadcastIfNeeded(boolean configChanged) {
             // send a sticky broadcast containing current USB state
             Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -876,6 +799,14 @@
                     mMidiEnabled && mConfigured, mMidiCard, mMidiDevice);
         }
 
+        private void setScreenUnlockedFunctions() {
+            setEnabledFunctions(mScreenUnlockedFunctions, false,
+                    UsbManager.containsFunction(mScreenUnlockedFunctions,
+                            UsbManager.USB_FUNCTION_MTP)
+                            || UsbManager.containsFunction(mScreenUnlockedFunctions,
+                            UsbManager.USB_FUNCTION_PTP));
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -893,9 +824,16 @@
                         updateCurrentAccessory();
                     }
                     if (mBootCompleted) {
-                        if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_TIMEOUT)) {
+                        if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_TIMEOUT)
+                                && !hasMessages(MSG_FUNCTION_SWITCH_TIMEOUT)) {
                             // restore defaults when USB is disconnected
-                            setEnabledFunctions(null, !mAdbEnabled, false);
+                            if (!mScreenLocked
+                                    && !UsbManager.USB_FUNCTION_NONE.equals(
+                                    mScreenUnlockedFunctions)) {
+                                setScreenUnlockedFunctions();
+                            } else {
+                                setEnabledFunctions(null, !mAdbEnabled, false);
+                            }
                         }
                         updateUsbFunctions();
                     } else {
@@ -978,6 +916,47 @@
                     String functions = (String) msg.obj;
                     setEnabledFunctions(functions, false, msg.arg1 == 1);
                     break;
+                case MSG_SET_SCREEN_UNLOCKED_FUNCTIONS:
+                    mScreenUnlockedFunctions = (String) msg.obj;
+                    SharedPreferences.Editor editor = mSettings.edit();
+                    editor.putString(String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF,
+                            mCurrentUser), mScreenUnlockedFunctions);
+                    editor.commit();
+                    if (!mScreenLocked && !UsbManager.USB_FUNCTION_NONE.equals(
+                            mScreenUnlockedFunctions)) {
+                        // If the screen is unlocked, also set current functions.
+                        setScreenUnlockedFunctions();
+                    }
+                    break;
+                case MSG_UPDATE_SCREEN_LOCK:
+                    if (msg.arg1 == 1 == mScreenLocked) {
+                        break;
+                    }
+                    mScreenLocked = msg.arg1 == 1;
+                    if (mSettings == null && !mScreenLocked) {
+                        // Shared preferences aren't accessible until the user has been unlocked.
+                        mSettings = getPinnedSharedPrefs(mContext);
+                        mScreenUnlockedFunctions = mSettings.getString(
+                                String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser),
+                                UsbManager.USB_FUNCTION_NONE);
+                    }
+                    if (!mBootCompleted) {
+                        break;
+                    }
+                    if (mScreenLocked) {
+                        if (!mConnected) {
+                            setEnabledFunctions(null, false, false);
+                        }
+                    } else {
+                        if (!UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)
+                                && (UsbManager.USB_FUNCTION_ADB.equals(mCurrentFunctions)
+                                || (UsbManager.USB_FUNCTION_MTP.equals(mCurrentFunctions)
+                                && !mUsbDataUnlocked))) {
+                            // Set the screen unlocked functions if current function is charging.
+                            setScreenUnlockedFunctions();
+                        }
+                    }
+                    break;
                 case MSG_UPDATE_USER_RESTRICTIONS:
                     // Restart the USB stack if USB transfer is enabled but no longer allowed.
                     final boolean forceRestart = mUsbDataUnlocked
@@ -987,9 +966,8 @@
                             mCurrentFunctions, forceRestart, mUsbDataUnlocked && !forceRestart);
                     break;
                 case MSG_SYSTEM_READY:
-                    updateUsbNotification(false);
-                    updateAdbNotification(false);
-                    updateUsbFunctions();
+                    mSystemReady = true;
+                    finishBoot();
                     break;
                 case MSG_LOCALE_CHANGED:
                     updateAdbNotification(true);
@@ -997,30 +975,19 @@
                     break;
                 case MSG_BOOT_COMPLETED:
                     mBootCompleted = true;
-                    if (mPendingBootBroadcast) {
-                        updateUsbStateBroadcastIfNeeded(false);
-                        mPendingBootBroadcast = false;
-                    }
-                    setEnabledFunctions(null, false, false);
-                    if (mCurrentAccessory != null) {
-                        getCurrentSettings().accessoryAttached(mCurrentAccessory);
-                    }
-                    if (mDebuggingManager != null) {
-                        mDebuggingManager.setAdbEnabled(mAdbEnabled);
-                    }
+                    finishBoot();
                     break;
                 case MSG_USER_SWITCHED: {
                     if (mCurrentUser != msg.arg1) {
-                        // Restart the USB stack and re-apply user restrictions for MTP or PTP.
-                        if (mUsbDataUnlocked
-                                && isUsbDataTransferActive()
-                                && mCurrentUser != UserHandle.USER_NULL) {
-                            Slog.v(TAG, "Current user switched to " + msg.arg1
-                                    + "; resetting USB host stack for MTP or PTP");
-                            // avoid leaking sensitive data from previous user
-                            setEnabledFunctions(null, true, false);
+                        if (DEBUG) {
+                            Slog.v(TAG, "Current user switched to " + msg.arg1);
                         }
                         mCurrentUser = msg.arg1;
+                        mScreenLocked = true;
+                        mScreenUnlockedFunctions = mSettings.getString(
+                                String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser),
+                                UsbManager.USB_FUNCTION_NONE);
+                        setEnabledFunctions(null, false, false);
                     }
                     break;
                 }
@@ -1038,6 +1005,41 @@
             }
         }
 
+        protected void finishBoot() {
+            if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
+                if (mPendingBootBroadcast) {
+                    updateUsbStateBroadcastIfNeeded(false);
+                    mPendingBootBroadcast = false;
+                }
+                if (!mScreenLocked
+                        && !UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)) {
+                    setScreenUnlockedFunctions();
+                } else {
+                    setEnabledFunctions(null, false, false);
+                }
+                if (mCurrentAccessory != null) {
+                    getCurrentSettings().accessoryAttached(mCurrentAccessory);
+                }
+                if (mDebuggingManager != null) {
+                    mDebuggingManager.setAdbEnabled(mAdbEnabled);
+                }
+
+                // make sure the ADB_ENABLED setting value matches the current state
+                try {
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+                } catch (SecurityException e) {
+                    // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
+                    // be changed.
+                    Slog.d(TAG, "ADB_ENABLED is restricted.");
+                }
+
+                updateUsbNotification(false);
+                updateAdbNotification(false);
+                updateUsbFunctions();
+            }
+        }
+
         private boolean isUsbDataTransferActive() {
             return UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)
                     || UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP);
@@ -1047,7 +1049,7 @@
             return mCurrentAccessory;
         }
 
-        private void updateUsbNotification(boolean force) {
+        protected void updateUsbNotification(boolean force) {
             if (mNotificationManager == null || !mUseUsbNotification
                     || ("0".equals(SystemProperties.get("persist.charging.notify")))) {
                 return;
@@ -1072,20 +1074,12 @@
                 titleRes = com.android.internal.R.string.usb_unsupported_audio_accessory_title;
                 id = SystemMessage.NOTE_USB_AUDIO_ACCESSORY_NOT_SUPPORTED;
             } else if (mConnected) {
-                if (!mUsbDataUnlocked) {
-                    if (mSourcePower) {
-                        titleRes = com.android.internal.R.string.usb_supplying_notification_title;
-                        id = SystemMessage.NOTE_USB_SUPPLYING;
-                    } else {
-                        titleRes = com.android.internal.R.string.usb_charging_notification_title;
-                        id = SystemMessage.NOTE_USB_CHARGING;
-                    }
-                } else if (UsbManager.containsFunction(mCurrentFunctions,
-                        UsbManager.USB_FUNCTION_MTP)) {
+                if (UsbManager.containsFunction(mCurrentFunctions,
+                        UsbManager.USB_FUNCTION_MTP) && mUsbDataUnlocked) {
                     titleRes = com.android.internal.R.string.usb_mtp_notification_title;
                     id = SystemMessage.NOTE_USB_MTP;
                 } else if (UsbManager.containsFunction(mCurrentFunctions,
-                        UsbManager.USB_FUNCTION_PTP)) {
+                        UsbManager.USB_FUNCTION_PTP) && mUsbDataUnlocked) {
                     titleRes = com.android.internal.R.string.usb_ptp_notification_title;
                     id = SystemMessage.NOTE_USB_PTP;
                 } else if (UsbManager.containsFunction(mCurrentFunctions,
@@ -1155,18 +1149,18 @@
                     }
 
                     Notification.Builder builder = new Notification.Builder(mContext, channel)
-                                    .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
-                                    .setWhen(0)
-                                    .setOngoing(true)
-                                    .setTicker(title)
-                                    .setDefaults(0)  // please be quiet
-                                    .setColor(mContext.getColor(
-                                            com.android.internal.R.color
-                                                    .system_notification_accent_color))
-                                    .setContentTitle(title)
-                                    .setContentText(message)
-                                    .setContentIntent(pi)
-                                    .setVisibility(Notification.VISIBILITY_PUBLIC);
+                            .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                            .setWhen(0)
+                            .setOngoing(true)
+                            .setTicker(title)
+                            .setDefaults(0)  // please be quiet
+                            .setColor(mContext.getColor(
+                                    com.android.internal.R.color
+                                            .system_notification_accent_color))
+                            .setContentTitle(title)
+                            .setContentText(message)
+                            .setContentIntent(pi)
+                            .setVisibility(Notification.VISIBILITY_PUBLIC);
 
                     if (titleRes
                             == com.android.internal.R.string
@@ -1184,7 +1178,7 @@
             }
         }
 
-        private void updateAdbNotification(boolean force) {
+        protected void updateAdbNotification(boolean force) {
             if (mNotificationManager == null) return;
             final int id = SystemMessage.NOTE_ADB_ACTIVE;
             final int titleRes = com.android.internal.R.string.adb_active_notification_title;
@@ -1236,23 +1230,26 @@
             }
         }
 
-        private String getDefaultFunctions() {
-            String func = SystemProperties.get(getPersistProp(true),
-                    UsbManager.USB_FUNCTION_NONE);
+        protected String getChargingFunctions() {
             // if ADB is enabled, reset functions to ADB
             // else enable MTP as usual.
-            if (UsbManager.containsFunction(func, UsbManager.USB_FUNCTION_ADB)) {
+            if (mAdbEnabled) {
                 return UsbManager.USB_FUNCTION_ADB;
             } else {
                 return UsbManager.USB_FUNCTION_MTP;
             }
         }
 
+        public boolean isFunctionEnabled(String function) {
+            return UsbManager.containsFunction(mCurrentFunctions, function);
+        }
+
         public void dump(IndentingPrintWriter pw) {
             pw.println("USB Device State:");
             pw.println("  mCurrentFunctions: " + mCurrentFunctions);
-            pw.println("  mCurrentOemFunctions: " + mCurrentOemFunctions);
             pw.println("  mCurrentFunctionsApplied: " + mCurrentFunctionsApplied);
+            pw.println("  mScreenUnlockedFunctions: " + mScreenUnlockedFunctions);
+            pw.println("  mScreenLocked: " + mScreenLocked);
             pw.println("  mConnected: " + mConnected);
             pw.println("  mConfigured: " + mConfigured);
             pw.println("  mUsbDataUnlocked: " + mUsbDataUnlocked);
@@ -1263,6 +1260,7 @@
             pw.println("  mUsbCharging: " + mUsbCharging);
             pw.println("  mHideUsbNotification: " + mHideUsbNotification);
             pw.println("  mAudioAccessoryConnected: " + mAudioAccessoryConnected);
+            pw.println("  mAdbEnabled: " + mAdbEnabled);
 
             try {
                 pw.println("  Kernel state: "
@@ -1273,6 +1271,675 @@
                 pw.println("IOException: " + e);
             }
         }
+
+        /**
+         * Evaluates USB function policies and applies the change accordingly.
+         */
+        protected abstract void setEnabledFunctions(String functions, boolean forceRestart,
+                boolean usbDataUnlocked);
+
+    }
+
+    private final class UsbHandlerLegacy extends UsbHandler {
+        /**
+         * The non-persistent property which stores the current USB settings.
+         */
+        private static final String USB_CONFIG_PROPERTY = "sys.usb.config";
+
+        /**
+         * The non-persistent property which stores the current USB actual state.
+         */
+        private static final String USB_STATE_PROPERTY = "sys.usb.state";
+
+        private HashMap<String, HashMap<String, Pair<String, String>>> mOemModeMap;
+        private String mCurrentOemFunctions;
+
+        UsbHandlerLegacy(Looper looper, Context context) {
+            super(looper);
+            try {
+                readOemUsbOverrideConfig(context);
+                // Restore default functions.
+                mCurrentOemFunctions = SystemProperties.get(getPersistProp(false),
+                        UsbManager.USB_FUNCTION_NONE);
+                if (isNormalBoot()) {
+                    mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY,
+                            UsbManager.USB_FUNCTION_NONE);
+                    mCurrentFunctionsApplied = mCurrentFunctions.equals(
+                            SystemProperties.get(USB_STATE_PROPERTY));
+                } else {
+                    mCurrentFunctions = SystemProperties.get(getPersistProp(true),
+                            UsbManager.USB_FUNCTION_NONE);
+                    mCurrentFunctionsApplied = SystemProperties.get(USB_CONFIG_PROPERTY,
+                            UsbManager.USB_FUNCTION_NONE).equals(
+                            SystemProperties.get(USB_STATE_PROPERTY));
+                }
+                mCurrentUsbFunctionsReceived = true;
+
+                String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
+                updateState(state);
+
+                // register observer to listen for settings changes
+                mContentResolver.registerContentObserver(
+                        Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
+                        false, new AdbSettingsObserver());
+
+                // Watch for USB configuration changes
+                mUEventObserver.startObserving(USB_STATE_MATCH);
+                mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+            } catch (Exception e) {
+                Slog.e(TAG, "Error initializing UsbHandler", e);
+            }
+        }
+
+        private void readOemUsbOverrideConfig(Context context) {
+            String[] configList = mContext.getResources().getStringArray(
+                    com.android.internal.R.array.config_oemUsbModeOverride);
+
+            if (configList != null) {
+                for (String config : configList) {
+                    String[] items = config.split(":");
+                    if (items.length == 3 || items.length == 4) {
+                        if (mOemModeMap == null) {
+                            mOemModeMap = new HashMap<>();
+                        }
+                        HashMap<String, Pair<String, String>> overrideMap =
+                                mOemModeMap.get(items[0]);
+                        if (overrideMap == null) {
+                            overrideMap = new HashMap<>();
+                            mOemModeMap.put(items[0], overrideMap);
+                        }
+
+                        // Favoring the first combination if duplicate exists
+                        if (!overrideMap.containsKey(items[1])) {
+                            if (items.length == 3) {
+                                overrideMap.put(items[1], new Pair<>(items[2], ""));
+                            } else {
+                                overrideMap.put(items[1], new Pair<>(items[2], items[3]));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        private String applyOemOverrideFunction(String usbFunctions) {
+            if ((usbFunctions == null) || (mOemModeMap == null)) {
+                return usbFunctions;
+            }
+
+            String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+            Slog.d(TAG, "applyOemOverride usbfunctions=" + usbFunctions + " bootmode=" + bootMode);
+
+            Map<String, Pair<String, String>> overridesMap =
+                    mOemModeMap.get(bootMode);
+            // Check to ensure that the oem is not overriding in the normal
+            // boot mode
+            if (overridesMap != null && !(bootMode.equals(NORMAL_BOOT)
+                    || bootMode.equals("unknown"))) {
+                Pair<String, String> overrideFunctions =
+                        overridesMap.get(usbFunctions);
+                if (overrideFunctions != null) {
+                    Slog.d(TAG, "OEM USB override: " + usbFunctions
+                            + " ==> " + overrideFunctions.first
+                            + " persist across reboot "
+                            + overrideFunctions.second);
+                    if (!overrideFunctions.second.equals("")) {
+                        String newFunction;
+                        if (mAdbEnabled) {
+                            newFunction = UsbManager.addFunction(overrideFunctions.second,
+                                    UsbManager.USB_FUNCTION_ADB);
+                        } else {
+                            newFunction = overrideFunctions.second;
+                        }
+                        Slog.d(TAG, "OEM USB override persisting: " + newFunction + "in prop: "
+                                + getPersistProp(false));
+                        SystemProperties.set(getPersistProp(false),
+                                newFunction);
+                    }
+                    return overrideFunctions.first;
+                } else if (mAdbEnabled) {
+                    String newFunction = UsbManager.addFunction(UsbManager.USB_FUNCTION_NONE,
+                            UsbManager.USB_FUNCTION_ADB);
+                    SystemProperties.set(getPersistProp(false),
+                            newFunction);
+                } else {
+                    SystemProperties.set(getPersistProp(false),
+                            UsbManager.USB_FUNCTION_NONE);
+                }
+            }
+            // return passed in functions as is.
+            return usbFunctions;
+        }
+
+        private boolean waitForState(String state) {
+            // wait for the transition to complete.
+            // give up after 1 second.
+            String value = null;
+            for (int i = 0; i < 20; i++) {
+                // State transition is done when sys.usb.state is set to the new configuration
+                value = SystemProperties.get(USB_STATE_PROPERTY);
+                if (state.equals(value)) return true;
+                SystemClock.sleep(50);
+            }
+            Slog.e(TAG, "waitForState(" + state + ") FAILED: got " + value);
+            return false;
+        }
+
+        private void setUsbConfig(String config) {
+            if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
+            /**
+             * set the new configuration
+             * we always set it due to b/23631400, where adbd was getting killed
+             * and not restarted due to property timeouts on some devices
+             */
+            SystemProperties.set(USB_CONFIG_PROPERTY, config);
+        }
+
+        @Override
+        protected void setEnabledFunctions(String functions, boolean forceRestart,
+                boolean usbDataUnlocked) {
+            if (DEBUG) {
+                Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
+                        + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
+            }
+
+            if (usbDataUnlocked != mUsbDataUnlocked) {
+                mUsbDataUnlocked = usbDataUnlocked;
+                updateUsbNotification(false);
+                forceRestart = true;
+            }
+
+            /**
+             * Try to set the enabled functions.
+             */
+            final String oldFunctions = mCurrentFunctions;
+            final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
+            if (trySetEnabledFunctions(functions, forceRestart)) {
+                return;
+            }
+
+            /**
+             * Didn't work.  Try to revert changes.
+             * We always reapply the policy in case certain constraints changed such as
+             * user restrictions independently of any other new functions we were
+             * trying to activate.
+             */
+            if (oldFunctionsApplied && !oldFunctions.equals(functions)) {
+                Slog.e(TAG, "Failsafe 1: Restoring previous USB functions.");
+                if (trySetEnabledFunctions(oldFunctions, false)) {
+                    return;
+                }
+            }
+
+            /**
+             * Still didn't work.  Try to restore the default functions.
+             */
+            Slog.e(TAG, "Failsafe 2: Restoring default USB functions.");
+            if (trySetEnabledFunctions(null, false)) {
+                return;
+            }
+
+            /**
+             * Now we're desperate.  Ignore the default functions.
+             * Try to get ADB working if enabled.
+             */
+            Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled).");
+            if (trySetEnabledFunctions(UsbManager.USB_FUNCTION_NONE, false)) {
+                return;
+            }
+
+            /**
+             * Ouch.
+             */
+            Slog.e(TAG, "Unable to set any USB functions!");
+        }
+
+        private boolean isNormalBoot() {
+            String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+            return bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown");
+        }
+
+        private boolean trySetEnabledFunctions(String functions, boolean forceRestart) {
+            if (functions == null || applyAdbFunction(functions)
+                    .equals(UsbManager.USB_FUNCTION_NONE)) {
+                functions = getChargingFunctions();
+            }
+            functions = applyAdbFunction(functions);
+
+            String oemFunctions = applyOemOverrideFunction(functions);
+
+            if (!isNormalBoot() && !mCurrentFunctions.equals(functions)) {
+                SystemProperties.set(getPersistProp(true), functions);
+            }
+
+            if ((!functions.equals(oemFunctions)
+                    && !mCurrentOemFunctions.equals(oemFunctions))
+                    || !mCurrentFunctions.equals(functions)
+                    || !mCurrentFunctionsApplied
+                    || forceRestart) {
+                Slog.i(TAG, "Setting USB config to " + functions);
+                mCurrentFunctions = functions;
+                mCurrentOemFunctions = oemFunctions;
+                mCurrentFunctionsApplied = false;
+
+                /**
+                 * Kick the USB stack to close existing connections.
+                 */
+                setUsbConfig(UsbManager.USB_FUNCTION_NONE);
+
+                if (!waitForState(UsbManager.USB_FUNCTION_NONE)) {
+                    Slog.e(TAG, "Failed to kick USB config");
+                    return false;
+                }
+
+                /**
+                 * Set the new USB configuration.
+                 */
+                setUsbConfig(oemFunctions);
+
+                if (mBootCompleted
+                        && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
+                        || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
+                    /**
+                     * Start up dependent services.
+                     */
+                    updateUsbStateBroadcastIfNeeded(true);
+                }
+
+                if (!waitForState(oemFunctions)) {
+                    Slog.e(TAG, "Failed to switch USB config to " + functions);
+                    return false;
+                }
+
+                mCurrentFunctionsApplied = true;
+            }
+            return true;
+        }
+
+        private String getPersistProp(boolean functions) {
+            String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+            String persistProp = USB_PERSISTENT_CONFIG_PROPERTY;
+            if (!(bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown"))) {
+                if (functions) {
+                    persistProp = "persist.sys.usb." + bootMode + ".func";
+                } else {
+                    persistProp = "persist.sys.usb." + bootMode + ".config";
+                }
+            }
+            return persistProp;
+        }
+    }
+
+    private final class UsbHandlerHal extends UsbHandler {
+
+        /**
+         * Proxy object for the usb gadget hal daemon.
+         */
+        @GuardedBy("mGadgetProxyLock")
+        private IUsbGadget mGadgetProxy;
+
+        private final Object mGadgetProxyLock = new Object();
+
+        /**
+         * Cookie sent for usb gadget hal death notification.
+         */
+        private static final int USB_GADGET_HAL_DEATH_COOKIE = 2000;
+
+        /**
+         * Keeps track of the latest setCurrentUsbFunctions request number.
+         */
+        private int mCurrentRequest = 0;
+
+        /**
+         * The maximum time for which the UsbDeviceManager would wait once
+         * setCurrentUsbFunctions is called.
+         */
+        private static final int SET_FUNCTIONS_TIMEOUT_MS = 3000;
+
+        /**
+         * Conseration leeway to make sure that the hal callback arrives before
+         * SET_FUNCTIONS_TIMEOUT_MS expires. If the callback does not arrive
+         * within SET_FUNCTIONS_TIMEOUT_MS, UsbDeviceManager retries enabling
+         * default functions.
+         */
+        private static final int SET_FUNCTIONS_LEEWAY_MS = 500;
+
+        /**
+         * While switching functions, a disconnect is excpect as the usb gadget
+         * us torn down and brought back up. Wait for SET_FUNCTIONS_TIMEOUT_MS +
+         * ENUMERATION_TIME_OUT_MS before switching back to default fumctions when
+         * switching functions.
+         */
+        private static final int ENUMERATION_TIME_OUT_MS = 2000;
+
+        /**
+         * Command to start native service.
+         */
+        protected static final String CTL_START = "ctl.start";
+
+        /**
+         * Command to start native service.
+         */
+        protected static final String CTL_STOP = "ctl.stop";
+
+        /**
+         * Adb natvie daemon
+         */
+        protected static final String ADBD = "adbd";
+
+
+        UsbHandlerHal(Looper looper) {
+            super(looper);
+            try {
+                ServiceNotification serviceNotification = new ServiceNotification();
+
+                boolean ret = IServiceManager.getService()
+                        .registerForNotifications("android.hardware.usb.gadget@1.0::IUsbGadget",
+                                "", serviceNotification);
+                if (!ret) {
+                    Slog.e(TAG, "Failed to register usb gadget service start notification");
+                    return;
+                }
+
+                synchronized (mGadgetProxyLock) {
+                    mGadgetProxy = IUsbGadget.getService(true);
+                    mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
+                            USB_GADGET_HAL_DEATH_COOKIE);
+                    mCurrentFunctions = UsbManager.USB_FUNCTION_NONE;
+                    mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback());
+                    mCurrentUsbFunctionsRequested = true;
+                }
+                String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
+                updateState(state);
+
+                /**
+                 * Register observer to listen for settings changes.
+                 */
+                mContentResolver.registerContentObserver(
+                        Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
+                        false, new AdbSettingsObserver());
+
+                /**
+                 * Watch for USB configuration changes.
+                 */
+                mUEventObserver.startObserving(USB_STATE_MATCH);
+                mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+            } catch (NoSuchElementException e) {
+                Slog.e(TAG, "Usb gadget hal not found", e);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Usb Gadget hal not responding", e);
+            } catch (Exception e) {
+                Slog.e(TAG, "Error initializing UsbHandler", e);
+            }
+        }
+
+
+        final class UsbGadgetDeathRecipient implements HwBinder.DeathRecipient {
+            @Override
+            public void serviceDied(long cookie) {
+                if (cookie == USB_GADGET_HAL_DEATH_COOKIE) {
+                    Slog.e(TAG, "Usb Gadget hal service died cookie: " + cookie);
+                    synchronized (mGadgetProxyLock) {
+                        mGadgetProxy = null;
+                    }
+                }
+            }
+        }
+
+        final class ServiceNotification extends IServiceNotification.Stub {
+            @Override
+            public void onRegistration(String fqName, String name, boolean preexisting) {
+                Slog.i(TAG, "Usb gadget hal service started " + fqName + " " + name);
+                synchronized (mGadgetProxyLock) {
+                    try {
+                        mGadgetProxy = IUsbGadget.getService();
+                        mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
+                                USB_GADGET_HAL_DEATH_COOKIE);
+                        if (!mCurrentFunctionsApplied) {
+                            setCurrentFunctions(mCurrentFunctions, mUsbDataUnlocked);
+                        }
+                    } catch (NoSuchElementException e) {
+                        Slog.e(TAG, "Usb gadget hal not found", e);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Usb Gadget hal not responding", e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SET_CHARGING_FUNCTIONS:
+                    setEnabledFunctions(null, false, mUsbDataUnlocked);
+                    break;
+                case MSG_SET_FUNCTIONS_TIMEOUT:
+                    Slog.e(TAG, "Set functions timed out! no reply from usb hal");
+                    if (msg.arg1 != 1) {
+                        setEnabledFunctions(null, false, mUsbDataUnlocked);
+                    }
+                    break;
+                case MSG_GET_CURRENT_USB_FUNCTIONS:
+                    Slog.e(TAG, "prcessing MSG_GET_CURRENT_USB_FUNCTIONS");
+                    mCurrentUsbFunctionsReceived = true;
+
+                    if (mCurrentUsbFunctionsRequested) {
+                        Slog.e(TAG, "updating mCurrentFunctions");
+                        mCurrentFunctions = functionListToString((Long) msg.obj);
+                        Slog.e(TAG,
+                                "mCurrentFunctions:" + mCurrentFunctions + "applied:" + msg.arg1);
+                        mCurrentFunctionsApplied = msg.arg1 == 1;
+                    }
+                    finishBoot();
+                    break;
+                case MSG_FUNCTION_SWITCH_TIMEOUT:
+                    /**
+                     * Dont force to default when the configuration is already set to default.
+                     */
+                    if (msg.arg1 != 1) {
+                        setEnabledFunctions(null, !mAdbEnabled, false);
+                    }
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+
+        private class UsbGadgetCallback extends IUsbGadgetCallback.Stub {
+            int mRequest;
+            long mFunctions;
+            boolean mChargingFunctions;
+
+            UsbGadgetCallback() {
+            }
+
+            UsbGadgetCallback(int request, long functions,
+                    boolean chargingFunctions) {
+                mRequest = request;
+                mFunctions = functions;
+                mChargingFunctions = chargingFunctions;
+            }
+
+            @Override
+            public void setCurrentUsbFunctionsCb(long functions,
+                    int status) {
+                /**
+                 * Callback called for a previous setCurrenUsbFunction
+                 */
+                if ((mCurrentRequest != mRequest) || !hasMessages(MSG_SET_FUNCTIONS_TIMEOUT)
+                        || (mFunctions != functions)) {
+                    return;
+                }
+
+                removeMessages(MSG_SET_FUNCTIONS_TIMEOUT);
+                Slog.e(TAG, "notifyCurrentFunction request:" + mRequest + " status:" + status);
+                if (status == Status.SUCCESS) {
+                    mCurrentFunctionsApplied = true;
+                } else if (!mChargingFunctions) {
+                    Slog.e(TAG, "Setting default fuctions");
+                    sendEmptyMessage(MSG_SET_CHARGING_FUNCTIONS);
+                }
+            }
+
+            @Override
+            public void getCurrentUsbFunctionsCb(long functions,
+                    int status) {
+                sendMessage(MSG_GET_CURRENT_USB_FUNCTIONS, functions,
+                        status == Status.FUNCTIONS_APPLIED);
+            }
+        }
+
+        private long stringToFunctionList(String config) {
+            long functionsMask = 0;
+            String[] functions = config.split(",");
+            for (int i = 0; i < functions.length; i++) {
+                switch (functions[i]) {
+                    case "none":
+                        functionsMask |= GadgetFunction.NONE;
+                        break;
+                    case "adb":
+                        functionsMask |= GadgetFunction.ADB;
+                        break;
+                    case "mtp":
+                        functionsMask |= GadgetFunction.MTP;
+                        break;
+                    case "ptp":
+                        functionsMask |= GadgetFunction.PTP;
+                        break;
+                    case "midi":
+                        functionsMask |= GadgetFunction.MIDI;
+                        break;
+                    case "accessory":
+                        functionsMask |= GadgetFunction.ACCESSORY;
+                        break;
+                    case "rndis":
+                        functionsMask |= GadgetFunction.RNDIS;
+                        break;
+                }
+            }
+            return functionsMask;
+        }
+
+        private String functionListToString(Long functionList) {
+            StringJoiner functions = new StringJoiner(",");
+            if (functionList == GadgetFunction.NONE) {
+                functions.add("none");
+                return functions.toString();
+            }
+            if ((functionList & GadgetFunction.ADB) != 0) {
+                functions.add("adb");
+            }
+            if ((functionList & GadgetFunction.MTP) != 0) {
+                functions.add("mtp");
+            }
+            if ((functionList & GadgetFunction.PTP) != 0) {
+                functions.add("ptp");
+            }
+            if ((functionList & GadgetFunction.MIDI) != 0) {
+                functions.add("midi");
+            }
+            if ((functionList & GadgetFunction.ACCESSORY) != 0) {
+                functions.add("accessory");
+            }
+            if ((functionList & GadgetFunction.RNDIS) != 0) {
+                functions.add("rndis");
+            }
+            /**
+             * Remove the trailing comma.
+             */
+            return functions.toString();
+        }
+
+
+        private void setUsbConfig(String config, boolean chargingFunctions) {
+            Long functions = stringToFunctionList(config);
+            if (true) Slog.d(TAG, "setUsbConfig(" + config + ") request:" + ++mCurrentRequest);
+            /**
+             * Cancel any ongoing requests, if present.
+             */
+            removeMessages(MSG_FUNCTION_SWITCH_TIMEOUT);
+            removeMessages(MSG_SET_FUNCTIONS_TIMEOUT);
+            removeMessages(MSG_SET_CHARGING_FUNCTIONS);
+
+            synchronized (mGadgetProxyLock) {
+                if (mGadgetProxy == null) {
+                    Slog.e(TAG, "setUsbConfig mGadgetProxy is null");
+                    return;
+                }
+                try {
+                    if ((functions & GadgetFunction.ADB) != 0) {
+                        /**
+                         * Start adbd if ADB function is included in the configuration.
+                         */
+                        SystemProperties.set(CTL_START, ADBD);
+                    } else {
+                        /**
+                         * Stop adbd otherwise.
+                         */
+                        SystemProperties.set(CTL_STOP, ADBD);
+                    }
+                    UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest,
+                            functions, chargingFunctions);
+                    mGadgetProxy.setCurrentUsbFunctions(functions, usbGadgetCallback,
+                            SET_FUNCTIONS_TIMEOUT_MS - SET_FUNCTIONS_LEEWAY_MS);
+                    sendMessageDelayed(MSG_SET_FUNCTIONS_TIMEOUT, chargingFunctions,
+                            SET_FUNCTIONS_TIMEOUT_MS);
+                    sendMessageDelayed(MSG_FUNCTION_SWITCH_TIMEOUT, chargingFunctions,
+                            SET_FUNCTIONS_TIMEOUT_MS + ENUMERATION_TIME_OUT_MS);
+                    if (DEBUG) Slog.d(TAG, "timeout message queued");
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remoteexception while calling setCurrentUsbFunctions", e);
+                }
+            }
+        }
+
+        @Override
+        protected void setEnabledFunctions(String functions, boolean forceRestart,
+                boolean usbDataUnlocked) {
+            if (DEBUG) {
+                Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
+                        + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
+            }
+
+            if (usbDataUnlocked != mUsbDataUnlocked) {
+                mUsbDataUnlocked = usbDataUnlocked;
+                updateUsbNotification(false);
+                forceRestart = true;
+            }
+
+            trySetEnabledFunctions(functions, forceRestart);
+        }
+
+        private void trySetEnabledFunctions(String functions, boolean forceRestart) {
+            boolean chargingFunctions = false;
+
+            if (functions == null || applyAdbFunction(functions)
+                    .equals(UsbManager.USB_FUNCTION_NONE)) {
+                functions = getChargingFunctions();
+                chargingFunctions = true;
+            }
+            functions = applyAdbFunction(functions);
+
+            if (!mCurrentFunctions.equals(functions)
+                    || !mCurrentFunctionsApplied
+                    || forceRestart) {
+                Slog.i(TAG, "Setting USB config to " + functions);
+                mCurrentFunctions = functions;
+                mCurrentFunctionsApplied = false;
+                // set the flag to false as that would be stale value
+                mCurrentUsbFunctionsRequested = false;
+
+                // Set the new USB configuration.
+                setUsbConfig(mCurrentFunctions, chargingFunctions);
+
+                if (mBootCompleted
+                        && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
+                        || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
+                    // Start up dependent services.
+                    updateUsbStateBroadcastIfNeeded(true);
+                }
+            }
+        }
     }
 
     /* returns the currently attached USB accessory */
@@ -1280,7 +1947,11 @@
         return mHandler.getCurrentAccessory();
     }
 
-    /* opens the currently attached USB accessory */
+    /**
+     * opens the currently attached USB accessory.
+     *
+     * @param accessory accessory to be openened.
+     */
     public ParcelFileDescriptor openAccessory(UsbAccessory accessory,
             UsbUserSettingsManager settings) {
         UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
@@ -1297,113 +1968,41 @@
         return nativeOpenAccessory();
     }
 
+    /**
+     * Checks whether the function is present in the USB configuration.
+     *
+     * @param function function to be checked.
+     */
     public boolean isFunctionEnabled(String function) {
-        return UsbManager.containsFunction(SystemProperties.get(USB_CONFIG_PROPERTY), function);
+        return mHandler.isFunctionEnabled(function);
     }
 
+    /**
+     * Adds function to the current USB configuration.
+     *
+     * @param functions name of the USB function, or null to restore the default function.
+     * @param usbDataUnlocked whether user data is accessible.
+     */
     public void setCurrentFunctions(String functions, boolean usbDataUnlocked) {
         if (DEBUG) {
-            Slog.d(TAG, "setCurrentFunctions(" + functions + ", " +
-                    usbDataUnlocked + ")");
+            Slog.d(TAG, "setCurrentFunctions(" + functions + ", "
+                    + usbDataUnlocked + ")");
         }
         mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked);
     }
 
-    private void readOemUsbOverrideConfig() {
-        String[] configList = mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_oemUsbModeOverride);
-
-        if (configList != null) {
-            for (String config : configList) {
-                String[] items = config.split(":");
-                if (items.length == 3 || items.length == 4) {
-                    if (mOemModeMap == null) {
-                        mOemModeMap = new HashMap<>();
-                    }
-                    HashMap<String, Pair<String, String>> overrideMap
-                            = mOemModeMap.get(items[0]);
-                    if (overrideMap == null) {
-                        overrideMap = new HashMap<>();
-                        mOemModeMap.put(items[0], overrideMap);
-                    }
-
-                    // Favoring the first combination if duplicate exists
-                    if (!overrideMap.containsKey(items[1])) {
-                        if (items.length == 3) {
-                            overrideMap.put(items[1], new Pair<>(items[2], ""));
-                        } else {
-                            overrideMap.put(items[1], new Pair<>(items[2], items[3]));
-                        }
-                    }
-                }
-            }
+    /**
+     * Sets the functions which are set when the screen is unlocked.
+     *
+     * @param functions Functions to set.
+     */
+    public void setScreenUnlockedFunctions(String functions) {
+        if (DEBUG) {
+            Slog.d(TAG, "setScreenUnlockedFunctions(" + functions + ")");
         }
+        mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions);
     }
 
-    private String applyOemOverrideFunction(String usbFunctions) {
-        if ((usbFunctions == null) || (mOemModeMap == null)) {
-            return usbFunctions;
-        }
-
-        String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
-        Slog.d(TAG, "applyOemOverride usbfunctions=" + usbFunctions + " bootmode=" + bootMode);
-
-        Map<String, Pair<String, String>> overridesMap =
-                mOemModeMap.get(bootMode);
-        // Check to ensure that the oem is not overriding in the normal
-        // boot mode
-        if (overridesMap != null && !(bootMode.equals(NORMAL_BOOT) ||
-                bootMode.equals("unknown"))) {
-            Pair<String, String> overrideFunctions =
-                    overridesMap.get(usbFunctions);
-            if (overrideFunctions != null) {
-                Slog.d(TAG, "OEM USB override: " + usbFunctions
-                        + " ==> " + overrideFunctions.first
-                        + " persist across reboot "
-                        + overrideFunctions.second);
-                if (!overrideFunctions.second.equals("")) {
-                    String newFunction;
-                    if (mAdbEnabled) {
-                        newFunction = UsbManager.addFunction(overrideFunctions.second,
-                                UsbManager.USB_FUNCTION_ADB);
-                    } else {
-                        newFunction = overrideFunctions.second;
-                    }
-                    Slog.d(TAG, "OEM USB override persisting: " + newFunction + "in prop: "
-                            + UsbDeviceManager.getPersistProp(false));
-                    SystemProperties.set(UsbDeviceManager.getPersistProp(false),
-                            newFunction);
-                }
-                return overrideFunctions.first;
-            } else if (mAdbEnabled) {
-                String newFunction = UsbManager.addFunction(UsbManager.USB_FUNCTION_NONE,
-                        UsbManager.USB_FUNCTION_ADB);
-                SystemProperties.set(UsbDeviceManager.getPersistProp(false),
-                        newFunction);
-            } else {
-                SystemProperties.set(UsbDeviceManager.getPersistProp(false),
-                        UsbManager.USB_FUNCTION_NONE);
-            }
-        }
-        // return passed in functions as is.
-        return usbFunctions;
-    }
-
-    public static String getPersistProp(boolean functions) {
-        String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
-        String persistProp = USB_PERSISTENT_CONFIG_PROPERTY;
-        if (!(bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown"))) {
-            if (functions) {
-                persistProp = "persist.sys.usb." + bootMode + ".func";
-            } else {
-                persistProp = "persist.sys.usb." + bootMode + ".config";
-            }
-        }
-
-        return persistProp;
-    }
-
-
     public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
         if (mDebuggingManager != null) {
             mDebuggingManager.allowUsbDebugging(alwaysAllow, publicKey);
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index e4fcea7..039597c 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -87,6 +87,11 @@
         public void onStopUser(int userHandle) {
             mUsbService.onStopUser(UserHandle.of(userHandle));
         }
+
+        @Override
+        public void onUnlockUser(int userHandle) {
+            mUsbService.onUnlockUser(userHandle);
+        }
     }
 
     private static final String TAG = "UsbService";
@@ -205,6 +210,13 @@
         }
     }
 
+    /** Called when a user is unlocked. */
+    public void onUnlockUser(int user) {
+        if (mDeviceManager != null) {
+            mDeviceManager.onUnlockUser(user);
+        }
+    }
+
     /* Returns a list of all currently attached USB devices (host mdoe) */
     @Override
     public void getDeviceList(Bundle devices) {
@@ -389,6 +401,23 @@
         }
     }
 
+    @Override
+    public void setScreenUnlockedFunctions(String function) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+        if (!isSupportedCurrentFunction(function)) {
+            Slog.w(TAG, "Caller of setScreenUnlockedFunctions() requested unsupported USB function:"
+                    + function);
+            function = UsbManager.USB_FUNCTION_NONE;
+        }
+
+        if (mDeviceManager != null) {
+            mDeviceManager.setScreenUnlockedFunctions(function);
+        } else {
+            throw new IllegalStateException("USB device mode not supported");
+        }
+    }
+
     private static boolean isSupportedCurrentFunction(String function) {
         if (function == null) return true;
 
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2091101..8c7d6b3 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -1408,7 +1408,7 @@
      * @param extras Bundle containing extra information associated with the event.
      */
     public void sendCallEvent(String event, Bundle extras) {
-        mInCallAdapter.sendCallEvent(mTelecomCallId, event, extras);
+        mInCallAdapter.sendCallEvent(mTelecomCallId, event, mTargetSdkVersion, extras);
     }
 
     /**
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 4bc2a9b..658685f 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -286,11 +286,12 @@
      *
      * @param callId The callId to send the event for.
      * @param event The event.
+     * @param targetSdkVer Target sdk version of the app calling this api
      * @param extras Extras associated with the event.
      */
-    public void sendCallEvent(String callId, String event, Bundle extras) {
+    public void sendCallEvent(String callId, String event, int targetSdkVer, Bundle extras) {
         try {
-            mAdapter.sendCallEvent(callId, event, extras);
+            mAdapter.sendCallEvent(callId, event, targetSdkVer, extras);
         } catch (RemoteException ignored) {
         }
     }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a9bbd24..96c6e0a 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -236,6 +237,15 @@
             "android.telecom.extra.INCOMING_CALL_EXTRAS";
 
     /**
+     * Optional extra for {@link #ACTION_INCOMING_CALL} containing a boolean to indicate that the
+     * call has an externally generated ringer. Used by the HfpClientConnectionService when In Band
+     * Ringtone is enabled to prevent two ringers from being generated.
+     * @hide
+     */
+    public static final String EXTRA_CALL_EXTERNAL_RINGER =
+            "android.telecom.extra.CALL_EXTERNAL_RINGER";
+
+    /**
      * Optional extra for {@link android.content.Intent#ACTION_CALL} and
      * {@link android.content.Intent#ACTION_DIAL} {@code Intent} containing a {@link Bundle}
      * which contains metadata about the call. This {@link Bundle} will be saved into
@@ -1423,6 +1433,13 @@
     public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
         try {
             if (isServiceConnected()) {
+                if (extras != null && extras.getBoolean(EXTRA_IS_HANDOVER) &&
+                        mContext.getApplicationContext().getApplicationInfo().targetSdkVersion >
+                                Build.VERSION_CODES.O_MR1) {
+                    Log.e("TAG", "addNewIncomingCall failed. Use public api " +
+                            "acceptHandover for API > O-MR1");
+                    // TODO add "return" after DUO team adds support for new handover API
+                }
                 getTelecomService().addNewIncomingCall(
                         phoneAccount, extras == null ? new Bundle() : extras);
             }
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 23ac940..87ccd3e 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -64,7 +64,7 @@
 
     void pullExternalCall(String callId);
 
-    void sendCallEvent(String callId, String event, in Bundle extras);
+    void sendCallEvent(String callId, String event, int targetSdkVer, in Bundle extras);
 
     void putExtras(String callId, in Bundle extras);
 
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index fc814be..7cd1612 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -16,12 +16,15 @@
 
 package android.telephony;
 
+import android.annotation.SystemApi;
+
 /**
  * Contains access network related constants.
  */
 public final class AccessNetworkConstants {
 
     public static final class AccessNetworkType {
+        public static final int UNKNOWN = 0;
         public static final int GERAN = 1;
         public static final int UTRAN = 2;
         public static final int EUTRAN = 3;
@@ -30,6 +33,18 @@
     }
 
     /**
+     * Wireless transportation type
+     * @hide
+     */
+    @SystemApi
+    public static final class TransportType {
+        /** Wireless Wide Area Networks (i.e. Cellular) */
+        public static final int WWAN = 1;
+        /** Wireless Local Area Networks (i.e. Wifi) */
+        public static final int WLAN = 2;
+    }
+
+    /**
      * Frenquency bands for GERAN.
      * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf
      */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d0fb982..94ed218 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -39,13 +39,29 @@
     private final static String TAG = "CarrierConfigManager";
 
     /**
+     * Extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate the slot index that the
+     * broadcast is for.
+     */
+    public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
+
+    /**
+     * Optional extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate the
+     * subscription index that the broadcast is for, if a valid one is available.
+     */
+    public static final String EXTRA_SUBSCRIPTION_INDEX =
+            SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX;
+
+    /**
      * @hide
      */
     public CarrierConfigManager() {
     }
 
     /**
-     * This intent is broadcast by the system when carrier config changes.
+     * This intent is broadcast by the system when carrier config changes. An int is specified in
+     * {@link #EXTRA_SLOT_INDEX} to indicate the slot index that this is for. An optional int extra
+     * {@link #EXTRA_SUBSCRIPTION_INDEX} is included to indicate the subscription index if a valid
+     * one is available for the slot index.
      */
     public static final String
             ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
@@ -950,8 +966,9 @@
     public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
 
     /**
-     * String to identify carrier name in CarrierConfig app. This string is used only if
-     * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true
+     * String to identify carrier name in CarrierConfig app. This string overrides SPN if
+     * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true; otherwise, it will be used if its value is provided
+     * and SPN is unavailable
      * @hide
      */
     public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
@@ -1737,6 +1754,13 @@
      */
     public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool";
 
+    /**
+     * List of thresholds of RSRP for determining the display level of LTE signal bar.
+     * @hide
+     */
+    public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY =
+            "lte_rsrp_thresholds_int_array";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -2023,6 +2047,15 @@
         sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
+        sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                new int[] {
+                        -140, /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
+                        -128, /* SIGNAL_STRENGTH_POOR */
+                        -118, /* SIGNAL_STRENGTH_MODERATE */
+                        -108, /* SIGNAL_STRENGTH_GOOD */
+                        -98,  /* SIGNAL_STRENGTH_GREAT */
+                        -44
+                });
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 7f20c8a..5f1f448 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -40,6 +40,8 @@
     private final String mAlphaLong;
     // short alpha Operator Name String or Enhanced Operator Name String
     private final String mAlphaShort;
+    // cell bandwidth, in kHz
+    private final int mBandwidth;
 
     /**
      * @hide
@@ -50,6 +52,7 @@
         mPci = Integer.MAX_VALUE;
         mTac = Integer.MAX_VALUE;
         mEarfcn = Integer.MAX_VALUE;
+        mBandwidth = Integer.MAX_VALUE;
         mAlphaLong = null;
         mAlphaShort = null;
     }
@@ -65,7 +68,8 @@
      * @hide
      */
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
-        this(ci, pci, tac, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc), null, null);
+        this(ci, pci, tac, Integer.MAX_VALUE, Integer.MAX_VALUE, String.valueOf(mcc),
+                String.valueOf(mnc), null, null);
     }
 
     /**
@@ -80,7 +84,8 @@
      * @hide
      */
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
-        this(ci, pci, tac, earfcn, String.valueOf(mcc), String.valueOf(mnc), null, null);
+        this(ci, pci, tac, earfcn, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+                null, null);
     }
 
     /**
@@ -89,6 +94,7 @@
      * @param pci Physical Cell Id 0..503
      * @param tac 16-bit Tracking Area Code
      * @param earfcn 18-bit LTE Absolute RF Channel Number
+     * @param bandwidth cell bandwidth in kHz
      * @param mccStr 3-digit Mobile Country Code in string format
      * @param mncStr 2 or 3-digit Mobile Network Code in string format
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
@@ -96,19 +102,20 @@
      *
      * @hide
      */
-    public CellIdentityLte(int ci, int pci, int tac, int earfcn, String mccStr,
-                            String mncStr, String alphal, String alphas) {
+    public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
+            String mncStr, String alphal, String alphas) {
         super(TAG, TYPE_LTE, mccStr, mncStr);
         mCi = ci;
         mPci = pci;
         mTac = tac;
         mEarfcn = earfcn;
+        mBandwidth = bandwidth;
         mAlphaLong = alphal;
         mAlphaShort = alphas;
     }
 
     private CellIdentityLte(CellIdentityLte cid) {
-        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mMccStr,
+        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
     }
 
@@ -163,6 +170,13 @@
     }
 
     /**
+     * @return Cell bandwidth in kHz, Integer.MAX_VALUE if unknown
+     */
+    public int getBandwidth() {
+        return mBandwidth;
+    }
+
+    /**
      * @return Mobile Country Code in string format, null if unknown
      */
     public String getMccStr() {
@@ -219,6 +233,7 @@
                 && mPci == o.mPci
                 && mTac == o.mTac
                 && mEarfcn == o.mEarfcn
+                && mBandwidth == o.mBandwidth
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
                 && TextUtils.equals(mAlphaLong, o.mAlphaLong)
@@ -232,6 +247,7 @@
         .append(" mPci=").append(mPci)
         .append(" mTac=").append(mTac)
         .append(" mEarfcn=").append(mEarfcn)
+        .append(" mBandwidth=").append(mBandwidth)
         .append(" mMcc=").append(mMccStr)
         .append(" mMnc=").append(mMncStr)
         .append(" mAlphaLong=").append(mAlphaLong)
@@ -248,6 +264,7 @@
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mEarfcn);
+        dest.writeInt(mBandwidth);
         dest.writeString(mAlphaLong);
         dest.writeString(mAlphaShort);
     }
@@ -259,6 +276,7 @@
         mPci = in.readInt();
         mTac = in.readInt();
         mEarfcn = in.readInt();
+        mBandwidth = in.readInt();
         mAlphaLong = in.readString();
         mAlphaShort = in.readString();
 
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index b5e4eef..9232ed7 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -16,8 +16,11 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Immutable cell information from a point in time.
@@ -47,6 +50,34 @@
     /** @hide */
     public static final int TIMESTAMP_TYPE_JAVA_RIL = 4;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        CONNECTION_NONE,
+        CONNECTION_PRIMARY_SERVING,
+        CONNECTION_SECONDARY_SERVING,
+        CONNECTION_UNKNOWN
+    })
+    public @interface CellConnectionStatus {}
+
+    /**
+     * Cell is not a serving cell.
+     *
+     * <p>The cell has been measured but is neither a camped nor serving cell (3GPP 36.304).
+     */
+    public static final int CONNECTION_NONE = 0;
+
+    /** UE is connected to cell for signalling and possibly data (3GPP 36.331, 25.331). */
+    public static final int CONNECTION_PRIMARY_SERVING = 1;
+
+    /** UE is connected to cell for data (3GPP 36.331, 25.331). */
+    public static final int CONNECTION_SECONDARY_SERVING = 2;
+
+    /** Connection status is unknown. */
+    public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
+
+    private int mCellConnectionStatus = CONNECTION_NONE;
+
     // True if device is mRegistered to the mobile network
     private boolean mRegistered;
 
@@ -69,6 +100,7 @@
         this.mRegistered = ci.mRegistered;
         this.mTimeStampType = ci.mTimeStampType;
         this.mTimeStamp = ci.mTimeStamp;
+        this.mCellConnectionStatus = ci.mCellConnectionStatus;
     }
 
     /** True if this cell is registered to the mobile network */
@@ -90,6 +122,25 @@
     }
 
     /**
+     * Gets the connection status of this cell.
+     *
+     * @see #CONNECTION_NONE
+     * @see #CONNECTION_PRIMARY_SERVING
+     * @see #CONNECTION_SECONDARY_SERVING
+     * @see #CONNECTION_UNKNOWN
+     *
+     * @return The connection status of the cell.
+     */
+    @CellConnectionStatus
+    public int getCellConnectionStatus() {
+        return mCellConnectionStatus;
+    }
+    /** @hide */
+    public void setCellConnectionStatus(@CellConnectionStatus int cellConnectionStatus) {
+        mCellConnectionStatus = cellConnectionStatus;
+    }
+
+    /**
      * Where time stamp gets recorded.
      * @return one of TIMESTAMP_TYPE_XXXX
      *
@@ -111,7 +162,7 @@
     public int hashCode() {
         int primeNum = 31;
         return ((mRegistered ? 0 : 1) * primeNum) + ((int)(mTimeStamp / 1000) * primeNum)
-                + (mTimeStampType * primeNum);
+                + (mTimeStampType * primeNum) + (mCellConnectionStatus * primeNum);
     }
 
     @Override
@@ -125,7 +176,9 @@
         try {
             CellInfo o = (CellInfo) other;
             return mRegistered == o.mRegistered
-                    && mTimeStamp == o.mTimeStamp && mTimeStampType == o.mTimeStampType;
+                    && mTimeStamp == o.mTimeStamp
+                    && mTimeStampType == o.mTimeStampType
+                    && mCellConnectionStatus == o.mCellConnectionStatus;
         } catch (ClassCastException e) {
             return false;
         }
@@ -155,6 +208,7 @@
         timeStampType = timeStampTypeToString(mTimeStampType);
         sb.append(" mTimeStampType=").append(timeStampType);
         sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
+        sb.append(" mCellConnectionStatus=").append(mCellConnectionStatus);
 
         return sb.toString();
     }
@@ -181,6 +235,7 @@
         dest.writeInt(mRegistered ? 1 : 0);
         dest.writeInt(mTimeStampType);
         dest.writeLong(mTimeStamp);
+        dest.writeInt(mCellConnectionStatus);
     }
 
     /**
@@ -192,6 +247,7 @@
         mRegistered = (in.readInt() == 1) ? true : false;
         mTimeStampType = in.readInt();
         mTimeStamp = in.readLong();
+        mCellConnectionStatus = in.readInt();
     }
 
     /** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
new file mode 100644
index 0000000..97e3037
--- /dev/null
+++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
@@ -0,0 +1,72 @@
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to data network registration.
+ * @hide
+ */
+public class DataSpecificRegistrationStates implements Parcelable{
+    /**
+     * The maximum number of simultaneous Data Calls that
+     * must be established using setupDataCall().
+     */
+    public final int maxDataCalls;
+
+    DataSpecificRegistrationStates(int maxDataCalls) {
+        this.maxDataCalls = maxDataCalls;
+    }
+
+    private DataSpecificRegistrationStates(Parcel source) {
+        maxDataCalls = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(maxDataCalls);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "DataSpecificRegistrationStates {" + " mMaxDataCalls=" + maxDataCalls + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(maxDataCalls);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof DataSpecificRegistrationStates)) {
+            return false;
+        }
+
+        DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o;
+        return this.maxDataCalls == other.maxDataCalls;
+    }
+
+    public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
+            new Parcelable.Creator<DataSpecificRegistrationStates>() {
+                @Override
+                public DataSpecificRegistrationStates createFromParcel(Parcel source) {
+                    return new DataSpecificRegistrationStates(source);
+                }
+
+                @Override
+                public DataSpecificRegistrationStates[] newArray(int size) {
+                    return new DataSpecificRegistrationStates[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 56e1e64..4fa304a 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -310,6 +310,13 @@
      * {@hide}
      */
     public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70;
+
+    /**
+     * The network has reported that an alternative emergency number has been dialed, but the user
+     * must exit airplane mode to place the call.
+     */
+    public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71;
+
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Update toString() with the newly added disconnect type.
@@ -462,6 +469,8 @@
             return "EMERGENCY_PERM_FAILURE";
         case NORMAL_UNSPECIFIED:
             return "NORMAL_UNSPECIFIED";
+        case IMS_SIP_ALTERNATE_EMERGENCY_CALL:
+            return "IMS_SIP_ALTERNATE_EMERGENCY_CALL";
         default:
             return "INVALID: " + cause;
         }
diff --git a/telephony/java/android/telephony/INetworkService.aidl b/telephony/java/android/telephony/INetworkService.aidl
new file mode 100644
index 0000000..9ef7186
--- /dev/null
+++ b/telephony/java/android/telephony/INetworkService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.telephony.INetworkServiceCallback;
+
+/**
+ * {@hide}
+ */
+oneway interface INetworkService
+{
+    void createNetworkServiceProvider(int slotId);
+    void removeNetworkServiceProvider(int slotId);
+    void getNetworkRegistrationState(int slotId, int domain, INetworkServiceCallback callback);
+    void registerForNetworkRegistrationStateChanged(int slotId, INetworkServiceCallback callback);
+    void unregisterForNetworkRegistrationStateChanged(int slotId, INetworkServiceCallback callback);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/INetworkServiceCallback.aidl
similarity index 66%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/INetworkServiceCallback.aidl
index d750363..520598f 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/INetworkServiceCallback.aidl
@@ -14,7 +14,16 @@
  * limitations under the License.
  */
 
-/** @hide */
-package android.telephony.data;
+package android.telephony;
 
-parcelable InterfaceAddress;
+import android.telephony.NetworkRegistrationState;
+
+/**
+ * Network service call back interface
+ * @hide
+ */
+oneway interface INetworkServiceCallback
+{
+    void onGetNetworkRegistrationStateComplete(int result, in NetworkRegistrationState state);
+    void onNetworkStateChanged();
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/NetworkRegistrationState.aidl
similarity index 88%
rename from telephony/java/android/telephony/data/InterfaceAddress.aidl
rename to telephony/java/android/telephony/NetworkRegistrationState.aidl
index d750363..98cba77 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/NetworkRegistrationState.aidl
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-/** @hide */
-package android.telephony.data;
+package android.telephony;
 
-parcelable InterfaceAddress;
+parcelable NetworkRegistrationState;
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
new file mode 100644
index 0000000..4f137be
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Description of a mobile network registration state
+ * @hide
+ */
+@SystemApi
+public class NetworkRegistrationState implements Parcelable {
+    /**
+     * Network domain
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "DOMAIN_", value = {DOMAIN_CS, DOMAIN_PS})
+    public @interface Domain {}
+
+    /** Circuit switching domain */
+    public static final int DOMAIN_CS = 1;
+    /** Packet switching domain */
+    public static final int DOMAIN_PS = 2;
+
+    /**
+     * Registration state
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "REG_STATE_",
+            value = {REG_STATE_NOT_REG_NOT_SEARCHING, REG_STATE_HOME, REG_STATE_NOT_REG_SEARCHING,
+                    REG_STATE_DENIED, REG_STATE_UNKNOWN, REG_STATE_ROAMING})
+    public @interface RegState {}
+
+    /** Not registered. The device is not currently searching a new operator to register */
+    public static final int REG_STATE_NOT_REG_NOT_SEARCHING = 0;
+    /** Registered on home network */
+    public static final int REG_STATE_HOME = 1;
+    /** Not registered. The device is currently searching a new operator to register */
+    public static final int REG_STATE_NOT_REG_SEARCHING = 2;
+    /** Registration denied */
+    public static final int REG_STATE_DENIED = 3;
+    /** Registration state is unknown */
+    public static final int REG_STATE_UNKNOWN = 4;
+    /** Registered on roaming network */
+    public static final int REG_STATE_ROAMING = 5;
+
+    /**
+     * Supported service type
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SERVICE_TYPE_",
+            value = {SERVICE_TYPE_VOICE, SERVICE_TYPE_DATA, SERVICE_TYPE_SMS, SERVICE_TYPE_VIDEO,
+                    SERVICE_TYPE_EMERGENCY})
+    public @interface ServiceType {}
+
+    public static final int SERVICE_TYPE_VOICE = 1;
+    public static final int SERVICE_TYPE_DATA = 2;
+    public static final int SERVICE_TYPE_SMS = 3;
+    public static final int SERVICE_TYPE_VIDEO = 4;
+    public static final int SERVICE_TYPE_EMERGENCY = 5;
+
+    /** {@link AccessNetworkConstants.TransportType}*/
+    private final int mTransportType;
+
+    @Domain
+    private final int mDomain;
+
+    @RegState
+    private final int mRegState;
+
+    private final int mAccessNetworkTechnology;
+
+    private final int mReasonForDenial;
+
+    private final boolean mEmergencyOnly;
+
+    private final int[] mAvailableServices;
+
+    @Nullable
+    private final CellIdentity mCellIdentity;
+
+    @Nullable
+    private VoiceSpecificRegistrationStates mVoiceSpecificStates;
+
+    @Nullable
+    private DataSpecificRegistrationStates mDataSpecificStates;
+
+    /**
+     * @param transportType Transport type. Must be {@link AccessNetworkConstants.TransportType}
+     * @param domain Network domain. Must be DOMAIN_CS or DOMAIN_PS.
+     * @param regState Network registration state.
+     * @param accessNetworkTechnology See TelephonyManager NETWORK_TYPE_XXXX.
+     * @param reasonForDenial Reason for denial if the registration state is DENIED.
+     * @param availableServices The supported service.
+     * @param cellIdentity The identity representing a unique cell
+     */
+    public NetworkRegistrationState(int transportType, int domain, int regState,
+            int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+            int[] availableServices, @Nullable CellIdentity cellIdentity) {
+        mTransportType = transportType;
+        mDomain = domain;
+        mRegState = regState;
+        mAccessNetworkTechnology = accessNetworkTechnology;
+        mReasonForDenial = reasonForDenial;
+        mAvailableServices = availableServices;
+        mCellIdentity = cellIdentity;
+        mEmergencyOnly = emergencyOnly;
+    }
+
+    /**
+     * Constructor for voice network registration states.
+     * @hide
+     */
+    public NetworkRegistrationState(int transportType, int domain, int regState,
+            int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+            int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported,
+            int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) {
+        this(transportType, domain, regState, accessNetworkTechnology,
+                reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+
+        mVoiceSpecificStates = new VoiceSpecificRegistrationStates(cssSupported, roamingIndicator,
+                systemIsInPrl, defaultRoamingIndicator);
+    }
+
+    /**
+     * Constructor for data network registration states.
+     * @hide
+     */
+    public NetworkRegistrationState(int transportType, int domain, int regState,
+            int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+            int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls) {
+        this(transportType, domain, regState, accessNetworkTechnology,
+                reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+
+        mDataSpecificStates = new DataSpecificRegistrationStates(maxDataCalls);
+    }
+
+    protected NetworkRegistrationState(Parcel source) {
+        mTransportType = source.readInt();
+        mDomain = source.readInt();
+        mRegState = source.readInt();
+        mAccessNetworkTechnology = source.readInt();
+        mReasonForDenial = source.readInt();
+        mEmergencyOnly = source.readBoolean();
+        mAvailableServices = source.createIntArray();
+        mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader());
+        mVoiceSpecificStates = source.readParcelable(
+                VoiceSpecificRegistrationStates.class.getClassLoader());
+        mDataSpecificStates = source.readParcelable(
+                DataSpecificRegistrationStates.class.getClassLoader());
+    }
+
+    /**
+     * @return The transport type.
+     */
+    public int getTransportType() { return mTransportType; }
+
+    /**
+     * @return The network domain.
+     */
+    public @Domain int getDomain() { return mDomain; }
+
+    /**
+     * @return The registration state.
+     */
+    public @RegState int getRegState() {
+        return mRegState;
+    }
+
+    /**
+     * @return Whether emergency is enabled.
+     */
+    public boolean isEmergencyEnabled() { return mEmergencyOnly; }
+
+    /**
+     * @return List of available service types.
+     */
+    public int[] getAvailableServices() { return mAvailableServices; }
+
+    /**
+     * @return The access network technology. Must be one of TelephonyManager.NETWORK_TYPE_XXXX.
+     */
+    public int getAccessNetworkTechnology() {
+        return mAccessNetworkTechnology;
+    }
+
+    /**
+     * @return Reason for denial from network.
+     */
+    public int getReasonForDenial() {
+        return mReasonForDenial;
+    }
+
+    /**
+     * @return The cell information.
+     */
+    public CellIdentity getCellIdentity() {
+        return mCellIdentity;
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public VoiceSpecificRegistrationStates getVoiceSpecificStates() {
+        return mVoiceSpecificStates;
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public DataSpecificRegistrationStates getDataSpecificStates() {
+        return mDataSpecificStates;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    private static String regStateToString(int regState) {
+        switch (regState) {
+            case REG_STATE_NOT_REG_NOT_SEARCHING: return "NOT_REG_NOT_SEARCHING";
+            case REG_STATE_HOME: return "HOME";
+            case REG_STATE_NOT_REG_SEARCHING: return "NOT_REG_SEARCHING";
+            case REG_STATE_DENIED: return "DENIED";
+            case REG_STATE_UNKNOWN: return "UNKNOWN";
+            case REG_STATE_ROAMING: return "ROAMING";
+        }
+        return "Unknown reg state " + regState;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder("NetworkRegistrationState{")
+                .append("transportType=").append(mTransportType)
+                .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS")
+                .append(" regState=").append(regStateToString(mRegState))
+                .append(" accessNetworkTechnology=")
+                .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
+                .append(" reasonForDenial=").append(mReasonForDenial)
+                .append(" emergencyEnabled=").append(mEmergencyOnly)
+                .append(" supportedServices=").append(mAvailableServices)
+                .append(" cellIdentity=").append(mCellIdentity)
+                .append(" voiceSpecificStates=").append(mVoiceSpecificStates)
+                .append(" dataSpecificStates=").append(mDataSpecificStates)
+                .append("}").toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTransportType, mDomain, mRegState, mAccessNetworkTechnology,
+                mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity,
+                mVoiceSpecificStates, mDataSpecificStates);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof NetworkRegistrationState)) {
+            return false;
+        }
+
+        NetworkRegistrationState other = (NetworkRegistrationState) o;
+        return mTransportType == other.mTransportType
+                && mDomain == other.mDomain
+                && mRegState == other.mRegState
+                && mAccessNetworkTechnology == other.mAccessNetworkTechnology
+                && mReasonForDenial == other.mReasonForDenial
+                && mEmergencyOnly == other.mEmergencyOnly
+                && (mAvailableServices == other.mAvailableServices
+                    || Arrays.equals(mAvailableServices, other.mAvailableServices))
+                && mCellIdentity == other.mCellIdentity
+                && mVoiceSpecificStates == other.mVoiceSpecificStates
+                && mDataSpecificStates == other.mDataSpecificStates;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mTransportType);
+        dest.writeInt(mDomain);
+        dest.writeInt(mRegState);
+        dest.writeInt(mAccessNetworkTechnology);
+        dest.writeInt(mReasonForDenial);
+        dest.writeBoolean(mEmergencyOnly);
+        dest.writeIntArray(mAvailableServices);
+        dest.writeParcelable(mCellIdentity, 0);
+        dest.writeParcelable(mVoiceSpecificStates, 0);
+        dest.writeParcelable(mDataSpecificStates, 0);
+    }
+
+    public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
+            new Parcelable.Creator<NetworkRegistrationState>() {
+        @Override
+        public NetworkRegistrationState createFromParcel(Parcel source) {
+            return new NetworkRegistrationState(source);
+        }
+
+        @Override
+        public NetworkRegistrationState[] newArray(int size) {
+            return new NetworkRegistrationState[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
new file mode 100644
index 0000000..94921de
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.CallSuper;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class of network service. Services that extend NetworkService must register the service in
+ * their AndroidManifest to be detected by the framework. They must be protected by the permission
+ * "android.permission.BIND_NETWORK_SERVICE". The network service definition in the manifest must
+ * follow the following format:
+ * ...
+ * <service android:name=".xxxNetworkService"
+ *     android:permission="android.permission.BIND_NETWORK_SERVICE" >
+ *     <intent-filter>
+ *         <action android:name="android.telephony.NetworkService" />
+ *     </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class NetworkService extends Service {
+
+    private final String TAG = NetworkService.class.getSimpleName();
+
+    public static final String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
+    public static final String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
+
+    private static final int NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER                 = 1;
+    private static final int NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER                 = 2;
+    private static final int NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS            = 3;
+    private static final int NETWORK_SERVICE_GET_REGISTRATION_STATE                          = 4;
+    private static final int NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE                       = 5;
+    private static final int NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE                     = 6;
+    private static final int NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED                = 7;
+
+
+    private final HandlerThread mHandlerThread;
+
+    private final NetworkServiceHandler mHandler;
+
+    private final SparseArray<NetworkServiceProvider> mServiceMap = new SparseArray<>();
+
+    private final INetworkServiceWrapper mBinder = new INetworkServiceWrapper();
+
+    /**
+     * The abstract class of the actual network service implementation. The network service provider
+     * must extend this class to support network connection. Note that each instance of network
+     * service is associated with one physical SIM slot.
+     */
+    public class NetworkServiceProvider {
+        private final int mSlotId;
+
+        private final List<INetworkServiceCallback>
+                mNetworkRegistrationStateChangedCallbacks = new ArrayList<>();
+
+        public NetworkServiceProvider(int slotId) {
+            mSlotId = slotId;
+        }
+
+        /**
+         * @return SIM slot id the network service associated with.
+         */
+        public final int getSlotId() {
+            return mSlotId;
+        }
+
+        /**
+         * API to get network registration state. The result will be passed to the callback.
+         * @param domain
+         * @param callback
+         * @return SIM slot id the network service associated with.
+         */
+        public void getNetworkRegistrationState(int domain, NetworkServiceCallback callback) {
+            callback.onGetNetworkRegistrationStateComplete(
+                    NetworkServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+        }
+
+        public final void notifyNetworkRegistrationStateChanged() {
+            mHandler.obtainMessage(NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED,
+                    mSlotId, 0, null).sendToTarget();
+        }
+
+        private void registerForStateChanged(INetworkServiceCallback callback) {
+            synchronized (mNetworkRegistrationStateChangedCallbacks) {
+                mNetworkRegistrationStateChangedCallbacks.add(callback);
+            }
+        }
+
+        private void unregisterForStateChanged(INetworkServiceCallback callback) {
+            synchronized (mNetworkRegistrationStateChangedCallbacks) {
+                mNetworkRegistrationStateChangedCallbacks.remove(callback);
+            }
+        }
+
+        private void notifyStateChangedToCallbacks() {
+            for (INetworkServiceCallback callback : mNetworkRegistrationStateChangedCallbacks) {
+                try {
+                    callback.onNetworkStateChanged();
+                } catch (RemoteException exception) {
+                    // Doing nothing.
+                }
+            }
+        }
+
+        /**
+         * Called when the instance of network service is destroyed (e.g. got unbind or binder died).
+         */
+        @CallSuper
+        protected void onDestroy() {
+            mNetworkRegistrationStateChangedCallbacks.clear();
+        }
+    }
+
+    private class NetworkServiceHandler extends Handler {
+
+        NetworkServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            final int slotId = message.arg1;
+            final INetworkServiceCallback callback = (INetworkServiceCallback) message.obj;
+
+            NetworkServiceProvider serviceProvider = mServiceMap.get(slotId);
+
+            switch (message.what) {
+                case NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER:
+                    // If the service provider doesn't exist yet, we try to create it.
+                    if (serviceProvider == null) {
+                        mServiceMap.put(slotId, createNetworkServiceProvider(slotId));
+                    }
+                    break;
+                case NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER:
+                    // If the service provider doesn't exist yet, we try to create it.
+                    if (serviceProvider != null) {
+                        serviceProvider.onDestroy();
+                        mServiceMap.remove(slotId);
+                    }
+                    break;
+                case NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS:
+                    for (int i = 0; i < mServiceMap.size(); i++) {
+                        serviceProvider = mServiceMap.get(i);
+                        if (serviceProvider != null) {
+                            serviceProvider.onDestroy();
+                        }
+                    }
+                    mServiceMap.clear();
+                    break;
+                case NETWORK_SERVICE_GET_REGISTRATION_STATE:
+                    if (serviceProvider == null) break;
+                    int domainId = message.arg2;
+                    serviceProvider.getNetworkRegistrationState(domainId,
+                            new NetworkServiceCallback(callback));
+
+                    break;
+                case NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE:
+                    if (serviceProvider == null) break;
+                    serviceProvider.registerForStateChanged(callback);
+                    break;
+                case NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE:
+                    if (serviceProvider == null) break;
+                    serviceProvider.unregisterForStateChanged(callback);
+                    break;
+                case NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED:
+                    if (serviceProvider == null) break;
+                    serviceProvider.notifyStateChangedToCallbacks();
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    /** @hide */
+    protected NetworkService() {
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+
+        mHandler = new NetworkServiceHandler(mHandlerThread.getLooper());
+        log("network service created");
+    }
+
+    /**
+     * Create the instance of {@link NetworkServiceProvider}. Network service provider must override
+     * this method to facilitate the creation of {@link NetworkServiceProvider} instances. The system
+     * will call this method after binding the network service for each active SIM slot id.
+     *
+     * @param slotId SIM slot id the network service associated with.
+     * @return Network service object
+     */
+    protected abstract NetworkServiceProvider createNetworkServiceProvider(int slotId);
+
+    /** @hide */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (intent == null || !NETWORK_SERVICE_INTERFACE.equals(intent.getAction())) {
+            loge("Unexpected intent " + intent);
+            return null;
+        }
+
+        return mBinder;
+    }
+
+    /** @hide */
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS, 0,
+                0, null).sendToTarget();
+
+        return false;
+    }
+
+    /** @hide */
+    @Override
+    public void onDestroy() {
+        mHandlerThread.quit();
+    }
+
+    /**
+     * A wrapper around INetworkService that forwards calls to implementations of
+     * {@link NetworkService}.
+     */
+    private class INetworkServiceWrapper extends INetworkService.Stub {
+
+        @Override
+        public void createNetworkServiceProvider(int slotId) {
+            mHandler.obtainMessage(NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER, slotId,
+                    0, null).sendToTarget();
+        }
+
+        @Override
+        public void removeNetworkServiceProvider(int slotId) {
+            mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER, slotId,
+                    0, null).sendToTarget();
+        }
+
+        @Override
+        public void getNetworkRegistrationState(
+                int slotId, int domain, INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_STATE, slotId,
+                    domain, callback).sendToTarget();
+        }
+
+        @Override
+        public void registerForNetworkRegistrationStateChanged(
+                int slotId, INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE, slotId,
+                    0, callback).sendToTarget();
+        }
+
+        @Override
+        public void unregisterForNetworkRegistrationStateChanged(
+                int slotId,INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE, slotId,
+                    0, callback).sendToTarget();
+        }
+    }
+
+    private final void log(String s) {
+        Rlog.d(TAG, s);
+    }
+
+    private final void loge(String s) {
+        Rlog.e(TAG, s);
+    }
+}
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
new file mode 100644
index 0000000..92ebf36
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.NetworkService.NetworkServiceProvider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+
+/**
+ * Network service callback. Object of this class is passed to NetworkServiceProvider upon
+ * calling getNetworkRegistrationState, to receive asynchronous feedback from NetworkServiceProvider
+ * upon onGetNetworkRegistrationStateComplete. It's like a wrapper of INetworkServiceCallback
+ * because INetworkServiceCallback can't be a parameter type in public APIs.
+ *
+ * @hide
+ */
+@SystemApi
+public class NetworkServiceCallback {
+
+    private static final String mTag = NetworkServiceCallback.class.getSimpleName();
+
+    /**
+     * Result of network requests
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
+            RESULT_ERROR_ILLEGAL_STATE, RESULT_ERROR_FAILED})
+    public @interface Result {}
+
+    /** Request is completed successfully */
+    public static final int RESULT_SUCCESS              = 0;
+    /** Request is not support */
+    public static final int RESULT_ERROR_UNSUPPORTED    = 1;
+    /** Request contains invalid arguments */
+    public static final int RESULT_ERROR_INVALID_ARG    = 2;
+    /** Service is busy */
+    public static final int RESULT_ERROR_BUSY           = 3;
+    /** Request sent in illegal state */
+    public static final int RESULT_ERROR_ILLEGAL_STATE  = 4;
+    /** Request failed */
+    public static final int RESULT_ERROR_FAILED         = 5;
+
+    private final WeakReference<INetworkServiceCallback> mCallback;
+
+    /** @hide */
+    public NetworkServiceCallback(INetworkServiceCallback callback) {
+        mCallback = new WeakReference<>(callback);
+    }
+
+    /**
+     * Called to indicate result of
+     * {@link NetworkServiceProvider#getNetworkRegistrationState(int, NetworkServiceCallback)}
+     *
+     * @param result Result status like {@link NetworkServiceCallback#RESULT_SUCCESS} or
+     *                {@link NetworkServiceCallback#RESULT_ERROR_UNSUPPORTED}
+     * @param state The state information to be returned to callback.
+     */
+    public void onGetNetworkRegistrationStateComplete(int result, NetworkRegistrationState state) {
+        INetworkServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onGetNetworkRegistrationStateComplete(result, state);
+            } catch (RemoteException e) {
+                Rlog.e(mTag, "Failed to onGetNetworkRegistrationStateComplete on the remote");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index c7e5131..0ee870a 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -244,7 +244,22 @@
      */
     public static final int LISTEN_DATA_ACTIVATION_STATE                   = 0x00040000;
 
-     /*
+    /**
+     *  Listen for changes to the user mobile data state
+     *
+     *  @see #onUserMobileDataStateChanged
+     */
+    public static final int LISTEN_USER_MOBILE_DATA_STATE                  = 0x00080000;
+
+    /**
+     *  Listen for changes to the physical channel configuration.
+     *
+     *  @see #onPhysicalChannelConfigurationChanged
+     *  @hide
+     */
+    public static final int LISTEN_PHYSICAL_CHANNEL_CONFIGURATION          = 0x00100000;
+
+    /*
      * Subscription used to listen to the phone state changes
      * @hide
      */
@@ -349,10 +364,16 @@
                     case LISTEN_DATA_ACTIVATION_STATE:
                         PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
                         break;
+                    case LISTEN_USER_MOBILE_DATA_STATE:
+                        PhoneStateListener.this.onUserMobileDataStateChanged((boolean)msg.obj);
+                        break;
                     case LISTEN_CARRIER_NETWORK_CHANGE:
                         PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
                         break;
-
+                    case LISTEN_PHYSICAL_CHANNEL_CONFIGURATION:
+                        PhoneStateListener.this.onPhysicalChannelConfigurationChanged(
+                            (List<PhysicalChannelConfig>)msg.obj);
+                        break;
                 }
             }
         };
@@ -543,6 +564,24 @@
     }
 
     /**
+     * Callback invoked when the user mobile data state has changed
+     * @param enabled indicates whether the current user mobile data state is enabled or disabled.
+     */
+    public void onUserMobileDataStateChanged(boolean enabled) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when the current physical channel configuration has changed
+     *
+     * @param configs List of the current {@link PhysicalChannelConfig}s
+     * @hide
+     */
+    public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+        // default implementation empty
+    }
+
+    /**
      * Callback invoked when telephony has received notice from a carrier
      * app that a network action that could result in connectivity loss
      * has been requested by an app using
@@ -654,6 +693,10 @@
             send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
         }
 
+        public void onUserMobileDataStateChanged(boolean enabled) {
+            send(LISTEN_USER_MOBILE_DATA_STATE, 0, 0, enabled);
+        }
+
         public void onCarrierNetworkChange(boolean active) {
             send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
         }
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.aidl b/telephony/java/android/telephony/PhysicalChannelConfig.aidl
new file mode 100644
index 0000000..651c103
--- /dev/null
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony;
+
+parcelable PhysicalChannelConfig;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
new file mode 100644
index 0000000..651d68d
--- /dev/null
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @hide
+ */
+public final class PhysicalChannelConfig implements Parcelable {
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING})
+    public @interface ConnectionStatus {}
+
+    /**
+     * UE has connection to cell for signalling and possibly data (3GPP 36.331, 25.331).
+     */
+    public static final int CONNECTION_PRIMARY_SERVING = 1;
+
+    /**
+     * UE has connection to cell for data (3GPP 36.331, 25.331).
+     */
+    public static final int CONNECTION_SECONDARY_SERVING = 2;
+
+    /**
+     * Connection status of the cell.
+     *
+     * <p>One of {@link #CONNECTION_PRIMARY_SERVING}, {@link #CONNECTION_SECONDARY_SERVING}.
+     */
+    private int mCellConnectionStatus;
+
+    /**
+     * Cell bandwidth, in kHz.
+     */
+    private int mCellBandwidthDownlinkKhz;
+
+    public PhysicalChannelConfig(int status, int bandwidth) {
+        mCellConnectionStatus = status;
+        mCellBandwidthDownlinkKhz = bandwidth;
+    }
+
+    public PhysicalChannelConfig(Parcel in) {
+        mCellConnectionStatus = in.readInt();
+        mCellBandwidthDownlinkKhz = in.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCellConnectionStatus);
+        dest.writeInt(mCellBandwidthDownlinkKhz);
+    }
+
+    /**
+     * @return Cell bandwidth, in kHz
+     */
+    public int getCellBandwidthDownlink() {
+        return mCellBandwidthDownlinkKhz;
+    }
+
+    /**
+     * Gets the connection status of the cell.
+     *
+     * @see #CONNECTION_PRIMARY_SERVING
+     * @see #CONNECTION_SECONDARY_SERVING
+     *
+     * @return Connection status of the cell
+     */
+    @ConnectionStatus
+    public int getConnectionStatus() {
+        return mCellConnectionStatus;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof PhysicalChannelConfig)) {
+            return false;
+        }
+
+        PhysicalChannelConfig config = (PhysicalChannelConfig) o;
+        return mCellConnectionStatus == config.mCellConnectionStatus
+                && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz;
+    }
+
+    @Override
+    public int hashCode() {
+        return (mCellBandwidthDownlinkKhz * 29) + (mCellConnectionStatus * 31);
+    }
+
+    public static final Parcelable.Creator<PhysicalChannelConfig> CREATOR =
+        new Parcelable.Creator<PhysicalChannelConfig>() {
+            public PhysicalChannelConfig createFromParcel(Parcel in) {
+                return new PhysicalChannelConfig(in);
+            }
+
+            public PhysicalChannelConfig[] newArray(int size) {
+                return new PhysicalChannelConfig[size];
+            }
+        };
+}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index d4b4b88..90a3677 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -17,6 +17,8 @@
 package android.telephony;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -24,6 +26,10 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Contains phone state and service related information.
@@ -32,6 +38,7 @@
  *
  * <ul>
  *   <li>Service state: IN_SERVICE, OUT_OF_SERVICE, EMERGENCY_ONLY, POWER_OFF
+ *   <li>Duplex mode: UNKNOWN, FDD, TDD
  *   <li>Roaming indicator
  *   <li>Operator name, short name and numeric id
  *   <li>Network selection mode
@@ -67,6 +74,26 @@
      */
     public static final int STATE_POWER_OFF = 3;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({DUPLEX_MODE_UNKNOWN, DUPLEX_MODE_FDD, DUPLEX_MODE_TDD})
+    public @interface DuplexMode {}
+
+    /**
+     * Duplex mode for the phone is unknown.
+     */
+    public static final int DUPLEX_MODE_UNKNOWN = 0;
+
+    /**
+     * Duplex mode for the phone is frequency-division duplexing.
+     */
+    public static final int DUPLEX_MODE_FDD = 1;
+
+    /**
+     * Duplex mode for the phone is time-division duplexing.
+     */
+    public static final int DUPLEX_MODE_TDD = 2;
+
     /**
      * RIL level registration state values from ril.h
      * ((const char **)response)[0] is registration state 0-6,
@@ -282,10 +309,15 @@
 
     private boolean mIsUsingCarrierAggregation;
 
+    private int mChannelNumber;
+    private int[] mCellBandwidths = new int[0];
+
     /* EARFCN stands for E-UTRA Absolute Radio Frequency Channel Number,
      * Reference: 3GPP TS 36.104 5.4.3 */
     private int mLteEarfcnRsrpBoost = 0;
 
+    private List<NetworkRegistrationState> mNetworkRegistrationStates = new ArrayList<>();
+
     /**
      * get String description of roaming type
      * @hide
@@ -366,6 +398,7 @@
         mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
         mIsUsingCarrierAggregation = s.mIsUsingCarrierAggregation;
         mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
+        mNetworkRegistrationStates = new ArrayList<>(s.mNetworkRegistrationStates);
     }
 
     /**
@@ -396,6 +429,10 @@
         mIsDataRoamingFromRegistration = in.readInt() != 0;
         mIsUsingCarrierAggregation = in.readInt() != 0;
         mLteEarfcnRsrpBoost = in.readInt();
+        mNetworkRegistrationStates = new ArrayList<>();
+        in.readList(mNetworkRegistrationStates, NetworkRegistrationState.class.getClassLoader());
+        mChannelNumber = in.readInt();
+        mCellBandwidths = in.createIntArray();
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -423,6 +460,9 @@
         out.writeInt(mIsDataRoamingFromRegistration ? 1 : 0);
         out.writeInt(mIsUsingCarrierAggregation ? 1 : 0);
         out.writeInt(mLteEarfcnRsrpBoost);
+        out.writeList(mNetworkRegistrationStates);
+        out.writeInt(mChannelNumber);
+        out.writeIntArray(mCellBandwidths);
     }
 
     public int describeContents() {
@@ -476,6 +516,43 @@
     }
 
     /**
+     * Get the current duplex mode
+     *
+     * @see #DUPLEX_MODE_UNKNOWN
+     * @see #DUPLEX_MODE_FDD
+     * @see #DUPLEX_MODE_TDD
+     *
+     * @return Current {@code DuplexMode} for the phone
+     */
+    @DuplexMode
+    public int getDuplexMode() {
+        // TODO(b/72117602) determine duplex mode from channel number, using 3GPP 36.101 sections
+        // 5.7.3-1 and 5.5-1
+        return DUPLEX_MODE_UNKNOWN;
+    }
+
+    /**
+     * Get the channel number of the current primary serving cell, or -1 if unknown
+     *
+     * <p>This is EARFCN for LTE, UARFCN for UMTS, and ARFCN for GSM.
+     *
+     * @return Channel number of primary serving cell
+     */
+    public int getChannelNumber() {
+        return mChannelNumber;
+    }
+
+    /**
+     * Get an array of cell bandwidths (kHz) for the current serving cells
+     *
+     * @return Current serving cell bandwidths
+     */
+    @Nullable
+    public int[] getCellBandwidths() {
+        return mCellBandwidths;
+    }
+
+    /**
      * Get current roaming indicator of phone
      * (note: not just decoding from TS 27.007 7.2)
      *
@@ -703,6 +780,8 @@
                 + (mDataRegState * 37)
                 + mVoiceRoamingType
                 + mDataRoamingType
+                + mChannelNumber
+                + Arrays.hashCode(mCellBandwidths)
                 + (mIsManualNetworkSelection ? 1 : 0)
                 + ((null == mVoiceOperatorAlphaLong) ? 0 : mVoiceOperatorAlphaLong.hashCode())
                 + ((null == mVoiceOperatorAlphaShort) ? 0 : mVoiceOperatorAlphaShort.hashCode())
@@ -735,6 +814,8 @@
                 && mIsManualNetworkSelection == s.mIsManualNetworkSelection
                 && mVoiceRoamingType == s.mVoiceRoamingType
                 && mDataRoamingType == s.mDataRoamingType
+                && mChannelNumber == s.mChannelNumber
+                && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
                 && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
                 && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
                 && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
@@ -751,13 +832,14 @@
                         s.mCdmaDefaultRoamingIndicator)
                 && mIsEmergencyOnly == s.mIsEmergencyOnly
                 && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration
-                && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation);
+                && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation)
+                && mNetworkRegistrationStates.containsAll(s.mNetworkRegistrationStates);
     }
 
     /**
      * Convert radio technology to String
      *
-     * @param radioTechnology
+     * @param rt radioTechnology
      * @return String representation of the RAT
      *
      * @hide
@@ -863,6 +945,8 @@
             .append("(" + rilServiceStateToString(mVoiceRegState) + ")")
             .append(", mDataRegState=").append(mDataRegState)
             .append("(" + rilServiceStateToString(mDataRegState) + ")")
+            .append(", mChannelNumber=").append(mChannelNumber)
+            .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
             .append(", mVoiceRoamingType=").append(getRoamingLogString(mVoiceRoamingType))
             .append(", mDataRoamingType=").append(getRoamingLogString(mDataRoamingType))
             .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
@@ -884,6 +968,7 @@
             .append(", mIsDataRoamingFromRegistration=").append(mIsDataRoamingFromRegistration)
             .append(", mIsUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
             .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
+            .append(", mNetworkRegistrationStates=").append(mNetworkRegistrationStates)
             .append("}").toString();
     }
 
@@ -893,6 +978,8 @@
         mDataRegState = state;
         mVoiceRoamingType = ROAMING_TYPE_NOT_ROAMING;
         mDataRoamingType = ROAMING_TYPE_NOT_ROAMING;
+        mChannelNumber = -1;
+        mCellBandwidths = new int[0];
         mVoiceOperatorAlphaLong = null;
         mVoiceOperatorAlphaShort = null;
         mVoiceOperatorNumeric = null;
@@ -913,6 +1000,7 @@
         mIsDataRoamingFromRegistration = false;
         mIsUsingCarrierAggregation = false;
         mLteEarfcnRsrpBoost = 0;
+        mNetworkRegistrationStates = new ArrayList<>();
     }
 
     public void setStateOutOfService() {
@@ -940,6 +1028,16 @@
         if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
     }
 
+    /** @hide */
+    public void setCellBandwidths(int[] bandwidths) {
+        mCellBandwidths = bandwidths;
+    }
+
+    /** @hide */
+    public void setChannelNumber(int channelNumber) {
+        mChannelNumber = channelNumber;
+    }
+
     public void setRoaming(boolean roaming) {
         mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
         mDataRoamingType = mVoiceRoamingType;
@@ -1088,6 +1186,8 @@
         mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration");
         mIsUsingCarrierAggregation = m.getBoolean("isUsingCarrierAggregation");
         mLteEarfcnRsrpBoost = m.getInt("LteEarfcnRsrpBoost");
+        mChannelNumber = m.getInt("ChannelNumber");
+        mCellBandwidths = m.getIntArray("CellBandwidths");
     }
 
     /**
@@ -1119,6 +1219,8 @@
         m.putBoolean("isDataRoamingFromRegistration", mIsDataRoamingFromRegistration);
         m.putBoolean("isUsingCarrierAggregation", mIsUsingCarrierAggregation);
         m.putInt("LteEarfcnRsrpBoost", mLteEarfcnRsrpBoost);
+        m.putInt("ChannelNumber", mChannelNumber);
+        m.putIntArray("CellBandwidths", mCellBandwidths);
     }
 
     /** @hide */
@@ -1394,4 +1496,52 @@
 
         return newSs;
     }
+
+    /**
+     * Get all of the available network registration states.
+     *
+     * @return List of registration states
+     * @hide
+     */
+    @SystemApi
+    public List<NetworkRegistrationState> getNetworkRegistrationStates() {
+        return mNetworkRegistrationStates;
+    }
+
+    /**
+     * Get the network registration states with given transport type.
+     *
+     * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
+     * @return List of registration states.
+     * @hide
+     */
+    @SystemApi
+    public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+        List<NetworkRegistrationState> list = new ArrayList<>();
+        for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+            if (networkRegistrationState.getTransportType() == transportType) {
+                list.add(networkRegistrationState);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Get the network registration states with given transport type and domain.
+     *
+     * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
+     * @param domain The network domain. Must be DOMAIN_CS or DOMAIN_PS.
+     * @return The matching NetworkRegistrationState.
+     * @hide
+     */
+    @SystemApi
+    public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) {
+        for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+            if (networkRegistrationState.getTransportType() == transportType
+                    && networkRegistrationState.getDomain() == domain) {
+                return networkRegistrationState;
+            }
+        }
+        return null;
+    }
 }
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index de02de7..fc2ef27 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -19,9 +19,13 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.CarrierConfigManager;
 import android.util.Log;
 import android.content.res.Resources;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
 /**
  * Contains phone signal strength related information.
  */
@@ -47,10 +51,15 @@
         "none", "poor", "moderate", "good", "great"
     };
 
-    /** @hide */
-    //Use int max, as -1 is a valid value in signal strength
-    public static final int INVALID = 0x7FFFFFFF;
+    /**
+     * Use Integer.MAX_VALUE because -1 is a valid value in signal strength.
+     * @hide
+     */
+    public static final int INVALID = Integer.MAX_VALUE;
 
+    private static final int LTE_RSRP_THRESHOLDS_NUM = 6;
+
+    /** Parameters reported by the Radio */
     private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
     private int mGsmBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
     private int mCdmaDbm;   // This value is the RSSI value
@@ -63,13 +72,18 @@
     private int mLteRsrq;
     private int mLteRssnr;
     private int mLteCqi;
-    private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
-                                // signal strength level
     private int mTdScdmaRscp;
 
-    private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
+    /** Parameters from the framework */
+    private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
+                                // signal strength level
+    private boolean mIsGsm; // This value is set by the ServiceStateTracker
+                            // onSignalStrengthResult.
     private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
 
+    // The threshold of LTE RSRP for determining the display level of LTE signal bar.
+    private int mLteRsrpThresholds[] = new int[LTE_RSRP_THRESHOLDS_NUM];
+
     /**
      * Create a new SignalStrength from a intent notifier Bundle
      *
@@ -94,27 +108,12 @@
      * @hide
      */
     public SignalStrength() {
-        mGsmSignalStrength = 99;
-        mGsmBitErrorRate = -1;
-        mCdmaDbm = -1;
-        mCdmaEcio = -1;
-        mEvdoDbm = -1;
-        mEvdoEcio = -1;
-        mEvdoSnr = -1;
-        mLteSignalStrength = 99;
-        mLteRsrp = INVALID;
-        mLteRsrq = INVALID;
-        mLteRssnr = INVALID;
-        mLteCqi = INVALID;
-        mLteRsrpBoost = 0;
-        mTdScdmaRscp = INVALID;
-        isGsm = true;
-        mUseOnlyRsrpForLteLevel = false;
+        this(true);
     }
 
     /**
      * This constructor is used to create SignalStrength with default
-     * values and set the isGsmFlag with the value passed in the input
+     * values and set the gsmFlag with the value passed in the input
      *
      * @param gsmFlag true if Gsm Phone,false if Cdma phone
      * @return newly created SignalStrength
@@ -133,133 +132,26 @@
         mLteRsrq = INVALID;
         mLteRssnr = INVALID;
         mLteCqi = INVALID;
-        mLteRsrpBoost = 0;
         mTdScdmaRscp = INVALID;
-        isGsm = gsmFlag;
+        mLteRsrpBoost = 0;
+        mIsGsm = gsmFlag;
         mUseOnlyRsrpForLteLevel = false;
+        setLteRsrpThresholds(getDefaultLteRsrpThresholds());
     }
 
     /**
-     * Constructor
+     * Constructor with all fields present
      *
      * @hide
      */
-    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+    public SignalStrength(
+            int gsmSignalStrength, int gsmBitErrorRate,
             int cdmaDbm, int cdmaEcio,
             int evdoDbm, int evdoEcio, int evdoSnr,
             int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
-        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag, lteLevelBaseOnRsrp);
-        mTdScdmaRscp = tdScdmaRscp;
-    }
-
-    /**
-     * Constructor
-     *
-     * @hide
-     */
-    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int tdScdmaRscp, boolean gsmFlag) {
-        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
-        mTdScdmaRscp = tdScdmaRscp;
-    }
-
-    /**
-     * Constructor
-     *
-     * @hide
-     */
-    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            boolean gsmFlag) {
-        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
-    }
-
-    /**
-     * Constructor
-     *
-     * @hide
-     */
-    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            boolean gsmFlag) {
-        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
-                INVALID, INVALID, INVALID, 0, gsmFlag, false);
-    }
-
-    /**
-     * Copy constructors
-     *
-     * @param s Source SignalStrength
-     *
-     * @hide
-     */
-    public SignalStrength(SignalStrength s) {
-        copyFrom(s);
-    }
-
-    /**
-     * Initialize gsm/cdma values, sets lte values to defaults.
-     *
-     * @param gsmSignalStrength
-     * @param gsmBitErrorRate
-     * @param cdmaDbm
-     * @param cdmaEcio
-     * @param evdoDbm
-     * @param evdoEcio
-     * @param evdoSnr
-     * @param gsm
-     *
-     * @hide
-     */
-    public void initialize(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            boolean gsm) {
-        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
-                INVALID, INVALID, INVALID, 0, gsm, false);
-    }
-
-    /**
-     * Initialize all the values
-     *
-     * @param gsmSignalStrength
-     * @param gsmBitErrorRate
-     * @param cdmaDbm
-     * @param cdmaEcio
-     * @param evdoDbm
-     * @param evdoEcio
-     * @param evdoSnr
-     * @param lteSignalStrength
-     * @param lteRsrp
-     * @param lteRsrq
-     * @param lteRssnr
-     * @param lteCqi
-     * @param lteRsrpBoost
-     * @param gsm
-     * @param useOnlyRsrpForLteLevel
-     *
-     * @hide
-     */
-    public void initialize(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int lteRsrpBoost, boolean gsm, boolean useOnlyRsrpForLteLevel) {
+            int tdScdmaRscp,
+            // values Added by config
+            int lteRsrpBoost, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
         mGsmSignalStrength = gsmSignalStrength;
         mGsmBitErrorRate = gsmBitErrorRate;
         mCdmaDbm = cdmaDbm;
@@ -272,14 +164,41 @@
         mLteRsrq = lteRsrq;
         mLteRssnr = lteRssnr;
         mLteCqi = lteCqi;
-        mLteRsrpBoost = lteRsrpBoost;
         mTdScdmaRscp = INVALID;
-        isGsm = gsm;
-        mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
+        mLteRsrpBoost = lteRsrpBoost;
+        mIsGsm = gsmFlag;
+        mUseOnlyRsrpForLteLevel = lteLevelBaseOnRsrp;
+        setLteRsrpThresholds(getDefaultLteRsrpThresholds());
         if (DBG) log("initialize: " + toString());
     }
 
     /**
+     * Constructor for only values provided by Radio HAL
+     *
+     * @hide
+     */
+    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+            int cdmaDbm, int cdmaEcio,
+            int evdoDbm, int evdoEcio, int evdoSnr,
+            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+            int tdScdmaRscp) {
+        this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
+                lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, 0, true, false);
+    }
+
+    /**
+     * Copy constructors
+     *
+     * @param s Source SignalStrength
+     *
+     * @hide
+     */
+    public SignalStrength(SignalStrength s) {
+        copyFrom(s);
+    }
+
+    /**
      * @hide
      */
     protected void copyFrom(SignalStrength s) {
@@ -295,10 +214,11 @@
         mLteRsrq = s.mLteRsrq;
         mLteRssnr = s.mLteRssnr;
         mLteCqi = s.mLteCqi;
-        mLteRsrpBoost = s.mLteRsrpBoost;
         mTdScdmaRscp = s.mTdScdmaRscp;
-        isGsm = s.isGsm;
+        mLteRsrpBoost = s.mLteRsrpBoost;
+        mIsGsm = s.mIsGsm;
         mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
+        setLteRsrpThresholds(s.mLteRsrpThresholds);
     }
 
     /**
@@ -321,37 +241,11 @@
         mLteRsrq = in.readInt();
         mLteRssnr = in.readInt();
         mLteCqi = in.readInt();
-        mLteRsrpBoost = in.readInt();
         mTdScdmaRscp = in.readInt();
-        isGsm = (in.readInt() != 0);
-        mUseOnlyRsrpForLteLevel = (in.readInt() != 0);
-    }
-
-    /**
-     * Make a SignalStrength object from the given parcel as passed up by
-     * the ril which does not have isGsm. isGsm will be changed by ServiceStateTracker
-     * so the default is a don't care.
-     *
-     * @hide
-     */
-    public static SignalStrength makeSignalStrengthFromRilParcel(Parcel in) {
-        if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
-
-        SignalStrength ss = new SignalStrength();
-        ss.mGsmSignalStrength = in.readInt();
-        ss.mGsmBitErrorRate = in.readInt();
-        ss.mCdmaDbm = in.readInt();
-        ss.mCdmaEcio = in.readInt();
-        ss.mEvdoDbm = in.readInt();
-        ss.mEvdoEcio = in.readInt();
-        ss.mEvdoSnr = in.readInt();
-        ss.mLteSignalStrength = in.readInt();
-        ss.mLteRsrp = in.readInt();
-        ss.mLteRsrq = in.readInt();
-        ss.mLteRssnr = in.readInt();
-        ss.mLteCqi = in.readInt();
-        ss.mTdScdmaRscp = in.readInt();
-        return ss;
+        mLteRsrpBoost = in.readInt();
+        mIsGsm = in.readBoolean();
+        mUseOnlyRsrpForLteLevel = in.readBoolean();
+        in.readIntArray(mLteRsrpThresholds);
     }
 
     /**
@@ -370,10 +264,11 @@
         out.writeInt(mLteRsrq);
         out.writeInt(mLteRssnr);
         out.writeInt(mLteCqi);
-        out.writeInt(mLteRsrpBoost);
         out.writeInt(mTdScdmaRscp);
-        out.writeInt(isGsm ? 1 : 0);
-        out.writeInt(mUseOnlyRsrpForLteLevel ? 1 : 0);
+        out.writeInt(mLteRsrpBoost);
+        out.writeBoolean(mIsGsm);
+        out.writeBoolean(mUseOnlyRsrpForLteLevel);
+        out.writeIntArray(mLteRsrpThresholds);
     }
 
     /**
@@ -436,24 +331,24 @@
     }
 
     /**
-     * Fix {@link #isGsm} based on the signal strength data.
+     * Fix {@link #mIsGsm} based on the signal strength data.
      *
      * @hide
      */
     public void fixType() {
-        isGsm = getCdmaRelatedSignalStrength() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        mIsGsm = getCdmaRelatedSignalStrength() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
     /**
      * @param true - Gsm, Lte phones
      *        false - Cdma phones
      *
-     * Used by voice phone to set the isGsm
+     * Used by voice phone to set the mIsGsm
      *        flag
      * @hide
      */
     public void setGsm(boolean gsmFlag) {
-        isGsm = gsmFlag;
+        mIsGsm = gsmFlag;
     }
 
     /**
@@ -480,6 +375,22 @@
     }
 
     /**
+     * Sets the threshold array for determining the display level of LTE signal bar.
+     *
+     * @param lteRsrpThresholds int array for determining the display level.
+     *
+     * @hide
+     */
+    public void setLteRsrpThresholds(int[] lteRsrpThresholds) {
+        if ((lteRsrpThresholds == null)
+                || (lteRsrpThresholds.length != LTE_RSRP_THRESHOLDS_NUM)) {
+            Log.wtf(LOG_TAG, "setLteRsrpThresholds - lteRsrpThresholds is invalid.");
+            return;
+        }
+        System.arraycopy(lteRsrpThresholds, 0, mLteRsrpThresholds, 0, LTE_RSRP_THRESHOLDS_NUM);
+    }
+
+    /**
      * Get the GSM Signal Strength, valid values are (0-31, 99) as defined in TS
      * 27.007 8.5
      */
@@ -568,7 +479,7 @@
      *     while 4 represents a very strong signal strength.
      */
     public int getLevel() {
-        int level = isGsm ? getGsmRelatedSignalStrength() : getCdmaRelatedSignalStrength();
+        int level = mIsGsm ? getGsmRelatedSignalStrength() : getCdmaRelatedSignalStrength();
         if (DBG) log("getLevel=" + level);
         return level;
     }
@@ -580,15 +491,13 @@
      */
     public int getAsuLevel() {
         int asuLevel = 0;
-        if (isGsm) {
-            if (getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                    asuLevel = getGsmAsuLevel();
-                } else {
-                    asuLevel = getTdScdmaAsuLevel();
-                }
-            } else {
+        if (mIsGsm) {
+            if (mLteRsrp != SignalStrength.INVALID) {
                 asuLevel = getLteAsuLevel();
+            } else if (mTdScdmaRscp != SignalStrength.INVALID) {
+                asuLevel = getTdScdmaAsuLevel();
+            } else {
+                asuLevel = getGsmAsuLevel();
             }
         } else {
             int cdmaAsuLevel = getCdmaAsuLevel();
@@ -833,25 +742,18 @@
          */
         int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
 
-        int[] threshRsrp = Resources.getSystem().getIntArray(
-                com.android.internal.R.array.config_lteDbmThresholds);
-        if (threshRsrp.length != 6) {
-            Log.wtf(LOG_TAG, "getLteLevel - config_lteDbmThresholds has invalid num of elements."
-                    + " Cannot evaluate RSRP signal.");
-        } else {
-            if (mLteRsrp > threshRsrp[5]) {
-                rsrpIconLevel = -1;
-            } else if (mLteRsrp >= (threshRsrp[4] - mLteRsrpBoost)) {
-                rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
-            } else if (mLteRsrp >= (threshRsrp[3] - mLteRsrpBoost)) {
-                rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
-            } else if (mLteRsrp >= (threshRsrp[2] - mLteRsrpBoost)) {
-                rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
-            } else if (mLteRsrp >= (threshRsrp[1] - mLteRsrpBoost)) {
-                rsrpIconLevel = SIGNAL_STRENGTH_POOR;
-            } else if (mLteRsrp >= threshRsrp[0]) {
-                rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-            }
+        if (mLteRsrp > mLteRsrpThresholds[5]) {
+            rsrpIconLevel = -1;
+        } else if (mLteRsrp >= (mLteRsrpThresholds[4] - mLteRsrpBoost)) {
+            rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+        } else if (mLteRsrp >= (mLteRsrpThresholds[3] - mLteRsrpBoost)) {
+            rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+        } else if (mLteRsrp >= (mLteRsrpThresholds[2] - mLteRsrpBoost)) {
+            rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+        } else if (mLteRsrp >= (mLteRsrpThresholds[1] - mLteRsrpBoost)) {
+            rsrpIconLevel = SIGNAL_STRENGTH_POOR;
+        } else if (mLteRsrp >= mLteRsrpThresholds[0]) {
+            rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         }
 
         if (useOnlyRsrpForLteLevel()) {
@@ -937,7 +839,7 @@
      * @return true if this is for GSM
      */
     public boolean isGsm() {
-        return this.isGsm;
+        return this.mIsGsm;
     }
 
     /**
@@ -1009,8 +911,8 @@
                 + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
                 + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
                 + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
-                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0)
-                + (mUseOnlyRsrpForLteLevel ? 1 : 0));
+                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (mIsGsm ? 1 : 0)
+                + (mUseOnlyRsrpForLteLevel ? 1 : 0) + (Arrays.hashCode(mLteRsrpThresholds)));
     }
 
     /**
@@ -1044,8 +946,9 @@
                 && mLteCqi == s.mLteCqi
                 && mLteRsrpBoost == s.mLteRsrpBoost
                 && mTdScdmaRscp == s.mTdScdmaRscp
-                && isGsm == s.isGsm
-                && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel);
+                && mIsGsm == s.mIsGsm
+                && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel
+                && Arrays.equals(mLteRsrpThresholds, s.mLteRsrpThresholds));
     }
 
     /**
@@ -1068,9 +971,10 @@
                 + " " + mLteCqi
                 + " " + mLteRsrpBoost
                 + " " + mTdScdmaRscp
-                + " " + (isGsm ? "gsm|lte" : "cdma")
+                + " " + (mIsGsm ? "gsm|lte" : "cdma")
                 + " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
-                         "use_rsrp_and_rssnr_for_lte_level"));
+                         "use_rsrp_and_rssnr_for_lte_level")
+                + " " + (Arrays.toString(mLteRsrpThresholds)));
     }
 
     /** Returns the signal strength related to GSM. */
@@ -1122,10 +1026,14 @@
         mLteRsrq = m.getInt("LteRsrq");
         mLteRssnr = m.getInt("LteRssnr");
         mLteCqi = m.getInt("LteCqi");
-        mLteRsrpBoost = m.getInt("lteRsrpBoost");
+        mLteRsrpBoost = m.getInt("LteRsrpBoost");
         mTdScdmaRscp = m.getInt("TdScdma");
-        isGsm = m.getBoolean("isGsm");
-        mUseOnlyRsrpForLteLevel = m.getBoolean("useOnlyRsrpForLteLevel");
+        mIsGsm = m.getBoolean("IsGsm");
+        mUseOnlyRsrpForLteLevel = m.getBoolean("UseOnlyRsrpForLteLevel");
+        ArrayList<Integer> lteRsrpThresholds = m.getIntegerArrayList("lteRsrpThresholds");
+        for (int i = 0; i < lteRsrpThresholds.size(); i++) {
+            mLteRsrpThresholds[i] = lteRsrpThresholds.get(i);
+        }
     }
 
     /**
@@ -1147,10 +1055,25 @@
         m.putInt("LteRsrq", mLteRsrq);
         m.putInt("LteRssnr", mLteRssnr);
         m.putInt("LteCqi", mLteCqi);
-        m.putInt("lteRsrpBoost", mLteRsrpBoost);
+        m.putInt("LteRsrpBoost", mLteRsrpBoost);
         m.putInt("TdScdma", mTdScdmaRscp);
-        m.putBoolean("isGsm", isGsm);
-        m.putBoolean("useOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
+        m.putBoolean("IsGsm", mIsGsm);
+        m.putBoolean("UseOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
+        ArrayList<Integer> lteRsrpThresholds = new ArrayList<Integer>();
+        for (int value : mLteRsrpThresholds) {
+            lteRsrpThresholds.add(value);
+        }
+        m.putIntegerArrayList("lteRsrpThresholds", lteRsrpThresholds);
+    }
+
+    /**
+     * Gets the default threshold array for determining the display level of LTE signal bar.
+     *
+     * @return int array for determining the display level.
+     */
+    private int[] getDefaultLteRsrpThresholds() {
+        return CarrierConfigManager.getDefaultConfig().getIntArray(
+                CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
     }
 
     /**
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 4e1c15f..38408fe 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -126,14 +126,31 @@
     private UiccAccessRule[] mAccessRules;
 
     /**
+     * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID
+     * for an eUICC card.
+     */
+    private String mCardId;
+
+    /**
+     * @hide
+     */
+    public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+        CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+        Bitmap icon, int mcc, int mnc, String countryIso) {
+        this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+            roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
+            null /* accessRules */, null /* accessRules */);
+    }
+
+    /**
      * @hide
      */
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
-            Bitmap icon, int mcc, int mnc, String countryIso) {
+            Bitmap icon, int mcc, int mnc, String countryIso,  boolean isEmbedded,
+            @Nullable UiccAccessRule[] accessRules) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
-                roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
-                null /* accessRules */);
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, null /* cardId */);
     }
 
     /**
@@ -142,7 +159,7 @@
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules) {
+            @Nullable UiccAccessRule[] accessRules, String cardId) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -158,6 +175,7 @@
         this.mCountryIso = countryIso;
         this.mIsEmbedded = isEmbedded;
         this.mAccessRules = accessRules;
+        this.mCardId = cardId;
     }
 
     /**
@@ -387,6 +405,14 @@
         return mAccessRules;
     }
 
+    /**
+     * @return the ID of the SIM card which contains the subscription.
+     * @hide
+     */
+    public String getCardId() {
+        return this.mCardId;
+    }
+
     public static final Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
         @Override
         public SubscriptionInfo createFromParcel(Parcel source) {
@@ -405,10 +431,11 @@
             Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
             boolean isEmbedded = source.readBoolean();
             UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
+            String cardId = source.readString();
 
             return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
                     nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
-                    isEmbedded, accessRules);
+                    isEmbedded, accessRules, cardId);
         }
 
         @Override
@@ -434,6 +461,7 @@
         mIconBitmap.writeToParcel(dest, flags);
         dest.writeBoolean(mIsEmbedded);
         dest.writeTypedArray(mAccessRules, flags);
+        dest.writeString(mCardId);
     }
 
     @Override
@@ -459,11 +487,13 @@
     @Override
     public String toString() {
         String iccIdToPrint = givePrintableIccid(mIccId);
+        String cardIdToPrint = givePrintableIccid(mCardId);
         return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
                 + " displayName=" + mDisplayName + " carrierName=" + mCarrierName
                 + " nameSource=" + mNameSource + " iconTint=" + mIconTint
                 + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
                 + " mnc " + mMnc + " isEmbedded " + mIsEmbedded
-                + " accessRules " + Arrays.toString(mAccessRules) + "}";
+                + " accessRules " + Arrays.toString(mAccessRules)
+                + " cardId=" + cardIdToPrint + "}";
     }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 64cc7c1..1fae04b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -271,6 +271,14 @@
     public static final String IS_EMBEDDED = "is_embedded";
 
     /**
+     * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the
+     * current enabled profile on the card, while for eUICC card it is the EID of the card.
+     * <P>Type: TEXT (String)</P>
+     * @hide
+     */
+     public static final String CARD_ID = "card_id";
+
+    /**
      * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
      * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1.
      * <p>TYPE: BLOB
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index e0b6f61..8c45724 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -1102,6 +1102,16 @@
                 "android.provider.Telephony.MMS_DOWNLOADED";
 
             /**
+             * Broadcast Action: A debug code has been entered in the dialer. These "secret codes"
+             * are used to activate developer menus by dialing certain codes. And they are of the
+             * form {@code *#*#&lt;code&gt;#*#*}. The intent will have the data URI:
+             * {@code android_secret_code://&lt;code&gt;}.
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String SECRET_CODE_ACTION =
+                    "android.provider.Telephony.SECRET_CODE";
+
+            /**
              * Broadcast action: When the default SMS package changes,
              * the previous default SMS package and the new default SMS
              * package are sent this broadcast to notify them of the change.
@@ -2564,6 +2574,35 @@
         public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers");
 
         /**
+         * The {@code content://} style URL to be called from DevicePolicyManagerService,
+         * can manage DPC-owned APNs.
+         * @hide
+         */
+        public static final Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc");
+
+        /**
+         * The {@code content://} style URL to be called from Telephony to query APNs.
+         * When DPC-owned APNs are enforced, only DPC-owned APNs are returned, otherwise only
+         * non-DPC-owned APNs are returned.
+         * @hide
+         */
+        public static final Uri FILTERED_URI = Uri.parse("content://telephony/carriers/filtered");
+
+        /**
+         * The {@code content://} style URL to be called from DevicePolicyManagerService
+         * or Telephony to manage whether DPC-owned APNs are enforced.
+         * @hide
+         */
+        public static final Uri ENFORCE_MANAGED_URI = Uri.parse(
+                "content://telephony/carriers/enforce_managed");
+
+        /**
+         * The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
+         * @hide
+         */
+        public static final String ENFORCE_KEY = "enforced";
+
+        /**
          * The default sort order for this table.
          */
         public static final String DEFAULT_SORT_ORDER = "name ASC";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f278d7c..ada697f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -22,14 +22,13 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
-import android.annotation.SuppressLint;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.WorkerThread;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
@@ -43,7 +42,6 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.carrier.CarrierIdentifier;
 import android.telecom.PhoneAccount;
@@ -54,6 +52,7 @@
 
 import com.android.ims.internal.IImsMMTelFeature;
 import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.ITelecomService;
@@ -61,7 +60,6 @@
 import com.android.internal.telephony.IPhoneSubInfo;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.ITelephonyRegistry;
-import com.android.internal.telephony.OperatorInfo;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.TelephonyProperties;
@@ -957,6 +955,64 @@
      */
     public static final int USSD_ERROR_SERVICE_UNAVAIL = -2;
 
+    /**
+     * An unknown carrier id. It could either be subscription unavailable or the subscription
+     * carrier cannot be recognized. Unrecognized carriers here means
+     * {@link #getSimOperator() MCC+MNC} cannot be identified.
+     */
+    public static final int UNKNOWN_CARRIER_ID = -1;
+
+    /**
+     * Broadcast Action: The subscription carrier identity has changed.
+     * This intent could be sent on the following events:
+     * <ul>
+     *   <li>Subscription absent. Carrier identity could change from a valid id to
+     *   {@link TelephonyManager#UNKNOWN_CARRIER_ID}.</li>
+     *   <li>Subscription loaded. Carrier identity could change from
+     *   {@link TelephonyManager#UNKNOWN_CARRIER_ID} to a valid id.</li>
+     *   <li>The subscription carrier is recognized after a remote update.</li>
+     * </ul>
+     * The intent will have the following extra values:
+     * <ul>
+     *   <li>{@link #EXTRA_CARRIER_ID} The up-to-date carrier id of the current subscription id.
+     *   </li>
+     *   <li>{@link #EXTRA_CARRIER_NAME} The up-to-date carrier name of the current subscription.
+     *   </li>
+     *   <li>{@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier
+     *   identity.
+     *   </li>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED =
+            "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
+
+    /**
+     * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
+     * the updated carrier id {@link TelephonyManager#getAndroidCarrierIdForSubscription()} of
+     * the current subscription.
+     * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
+     * the carrier cannot be identified.
+     */
+    public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
+
+    /**
+     * An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
+     * indicates the updated carrier name of the current subscription.
+     * {@see TelephonyManager#getSubscriptionCarrierName()}
+     * <p>Carrier name is a user-facing name of the carrier id {@link #EXTRA_CARRIER_ID},
+     * usually the brand name of the subsidiary (e.g. T-Mobile).
+     */
+    public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
+
+    /**
+     * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} to indicate the
+     * subscription which has changed.
+     */
+    public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
+
+
     //
     //
     // Device Info
@@ -2025,6 +2081,110 @@
      * carrier restrictions.
      */
     public static final int SIM_STATE_CARD_RESTRICTED = 9;
+    /**
+     * SIM card state: Loaded: SIM card applications have been loaded
+     * @hide
+     */
+    @SystemApi
+    public static final int SIM_STATE_LOADED = 10;
+    /**
+     * SIM card state: SIM Card is present
+     * @hide
+     */
+    @SystemApi
+    public static final int SIM_STATE_PRESENT = 11;
+
+    /**
+     * Extra included in {@link #ACTION_SIM_CARD_STATE_CHANGED} and
+     * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} to indicate the card/application state.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
+
+    /**
+     * Broadcast Action: The sim card state has changed.
+     * The intent will have the following extra values:</p>
+     * <dl>
+     *   <dt>{@link #EXTRA_SIM_STATE}</dt>
+     *   <dd>The sim card state. One of:
+     *     <dl>
+     *       <dt>{@link #SIM_STATE_ABSENT}</dt>
+     *       <dd>SIM card not found</dd>
+     *       <dt>{@link #SIM_STATE_CARD_IO_ERROR}</dt>
+     *       <dd>SIM card IO error</dd>
+     *       <dt>{@link #SIM_STATE_CARD_RESTRICTED}</dt>
+     *       <dd>SIM card is restricted</dd>
+     *       <dt>{@link #SIM_STATE_PRESENT}</dt>
+     *       <dd>SIM card is present</dd>
+     *     </dl>
+     *   </dd>
+     * </dl>
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The current state can also be queried using {@link #getSimCardState()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_CARD_STATE_CHANGED =
+            "android.telephony.action.SIM_CARD_STATE_CHANGED";
+
+    /**
+     * Broadcast Action: The sim application state has changed.
+     * The intent will have the following extra values:</p>
+     * <dl>
+     *   <dt>{@link #EXTRA_SIM_STATE}</dt>
+     *   <dd>The sim application state. One of:
+     *     <dl>
+     *       <dt>{@link #SIM_STATE_NOT_READY}</dt>
+     *       <dd>SIM card applications not ready</dd>
+     *       <dt>{@link #SIM_STATE_PIN_REQUIRED}</dt>
+     *       <dd>SIM card PIN locked</dd>
+     *       <dt>{@link #SIM_STATE_PUK_REQUIRED}</dt>
+     *       <dd>SIM card PUK locked</dd>
+     *       <dt>{@link #SIM_STATE_NETWORK_LOCKED}</dt>
+     *       <dd>SIM card network locked</dd>
+     *       <dt>{@link #SIM_STATE_PERM_DISABLED}</dt>
+     *       <dd>SIM card permanently disabled due to PUK failures</dd>
+     *       <dt>{@link #SIM_STATE_LOADED}</dt>
+     *       <dd>SIM card data loaded</dd>
+     *     </dl>
+     *   </dd>
+     * </dl>
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The current state can also be queried using
+     * {@link #getSimApplicationState()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_APPLICATION_STATE_CHANGED =
+            "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
+
+    /**
+     * Broadcast Action: Status of the SIM slots on the device has changed.
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The status can be queried using
+     * {@link #getUiccSlotsInfo()}
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_SLOT_STATUS_CHANGED =
+            "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
 
     /**
      * @return true if a ICC card is present
@@ -2071,6 +2231,14 @@
      * @see #SIM_STATE_CARD_RESTRICTED
      */
     public int getSimState() {
+        int simState = getSimStateIncludingLoaded();
+        if (simState == SIM_STATE_LOADED) {
+            simState = SIM_STATE_READY;
+        }
+        return simState;
+    }
+
+    private int getSimStateIncludingLoaded() {
         int slotIndex = getSlotIndex();
         // slotIndex may be invalid due to sim being absent. In that case query all slots to get
         // sim state
@@ -2089,7 +2257,63 @@
                     "state as absent");
             return SIM_STATE_ABSENT;
         }
-        return getSimState(slotIndex);
+        return SubscriptionManager.getSimStateForSlotIndex(slotIndex);
+    }
+
+    /**
+     * Returns a constant indicating the state of the default SIM card.
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_ABSENT
+     * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
+     * @see #SIM_STATE_PRESENT
+     *
+     * @hide
+     */
+    @SystemApi
+    public int getSimCardState() {
+        int simCardState = getSimState();
+        switch (simCardState) {
+            case SIM_STATE_UNKNOWN:
+            case SIM_STATE_ABSENT:
+            case SIM_STATE_CARD_IO_ERROR:
+            case SIM_STATE_CARD_RESTRICTED:
+                return simCardState;
+            default:
+                return SIM_STATE_PRESENT;
+        }
+    }
+
+    /**
+     * Returns a constant indicating the state of the card applications on the default SIM card.
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_PIN_REQUIRED
+     * @see #SIM_STATE_PUK_REQUIRED
+     * @see #SIM_STATE_NETWORK_LOCKED
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_PERM_DISABLED
+     * @see #SIM_STATE_LOADED
+     *
+     * @hide
+     */
+    @SystemApi
+    public int getSimApplicationState() {
+        int simApplicationState = getSimStateIncludingLoaded();
+        switch (simApplicationState) {
+            case SIM_STATE_UNKNOWN:
+            case SIM_STATE_ABSENT:
+            case SIM_STATE_CARD_IO_ERROR:
+            case SIM_STATE_CARD_RESTRICTED:
+                return SIM_STATE_UNKNOWN;
+            case SIM_STATE_READY:
+                // Ready is not a valid state anymore. The state that is broadcast goes from
+                // NOT_READY to either LOCKED or LOADED.
+                return SIM_STATE_NOT_READY;
+            default:
+                return simApplicationState;
+        }
     }
 
     /**
@@ -2110,6 +2334,9 @@
      */
     public int getSimState(int slotIndex) {
         int simState = SubscriptionManager.getSimStateForSlotIndex(slotIndex);
+        if (simState == SIM_STATE_LOADED) {
+            simState = SIM_STATE_READY;
+        }
         return simState;
     }
 
@@ -2331,6 +2558,53 @@
         }
     }
 
+    /**
+     * Gets all the UICC slots.
+     *
+     * @return UiccSlotInfo array.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public UiccSlotInfo[] getUiccSlotsInfo() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return null;
+            }
+            return telephony.getUiccSlotsInfo();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. For
+     * example, passing the physicalSlots array [1, 0] means mapping the first item 1, which is
+     * physical slot index 1, to the logical slot 0; and mapping the second item 0, which is
+     * physical slot index 0, to the logical slot 1. The index of the array means the index of the
+     * logical slots.
+     *
+     * @param physicalSlots Index i in the array representing physical slot for phone i. The array
+     *        size should be same as {@link #getPhoneCount()}.
+     * @return boolean Return true if the switch succeeds, false if the switch fails.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean switchSlots(int[] physicalSlots) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return false;
+            }
+            return telephony.switchSlots(physicalSlots);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     //
     //
     // Subscriber Info
@@ -4720,6 +4994,25 @@
     }
 
     /**
+     * @return the {@IImsRegistration} interface that corresponds with the slot index and feature.
+     * @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for.
+     * @param feature An integer indicating the feature that we wish to get the ImsRegistration for.
+     * Corresponds to features defined in ImsFeature.
+     * @hide
+     */
+    public @Nullable IImsRegistration getImsRegistration(int slotIndex, int feature) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getImsRegistration(slotIndex, feature);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage());
+        }
+        return null;
+    }
+
+    /**
      * Set IMS registration state
      *
      * @param Registration state
@@ -6548,6 +6841,61 @@
     }
 
     /**
+     * Returns carrier id of the current subscription.
+     * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
+     * carrier with a canonical integer a.k.a. android carrier id. The Android carrier ID is an
+     * Android platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
+     * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+     *
+     * <p>Apps which have carrier-specific configurations or business logic can use the carrier id
+     * as an Android platform-wide identifier for carriers.
+     *
+     * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
+     * subscription is unavailable or the carrier cannot be identified.
+     * @throws IllegalStateException if telephony service is unavailable.
+     */
+    public int getAndroidCarrierIdForSubscription() {
+        try {
+            ITelephony service = getITelephony();
+            return service.getSubscriptionCarrierId(getSubId());
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+            ex.rethrowAsRuntimeException();
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing.
+            throw new IllegalStateException("Telephony service unavailable");
+        }
+        return UNKNOWN_CARRIER_ID;
+    }
+
+    /**
+     * Returns carrier name of the current subscription.
+     * <p>Carrier name is a user-facing name of carrier id
+     * {@link #getAndroidCarrierIdForSubscription()}, usually the brand name of the subsidiary
+     * (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but
+     * should have a single carrier name. Carrier name is not a canonical identity,
+     * use {@link #getAndroidCarrierIdForSubscription()} instead.
+     * <p>The returned carrier name is unlocalized.
+     *
+     * @return Carrier name of the current subscription. Return {@code null} if the subscription is
+     * unavailable or the carrier cannot be identified.
+     * @throws IllegalStateException if telephony service is unavailable.
+     */
+    public CharSequence getAndroidCarrierNameForSubscription() {
+        try {
+            ITelephony service = getITelephony();
+            return service.getSubscriptionCarrierName(getSubId());
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+            ex.rethrowAsRuntimeException();
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing.
+            throw new IllegalStateException("Telephony service unavailable");
+        }
+        return null;
+    }
+
+    /**
      * Return the application ID for the app type like {@link APPTYPE_CSIM}.
      *
      * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/UiccSlotInfo.aidl
similarity index 81%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/UiccSlotInfo.aidl
index d750363..5571a6c 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/UiccSlotInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-/** @hide */
-package android.telephony.data;
+package android.telephony;
 
-parcelable InterfaceAddress;
+parcelable UiccSlotInfo;
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
new file mode 100644
index 0000000..0b3cbad
--- /dev/null
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+import android.annotation.IntDef;
+
+/**
+ * Class for the information of a UICC slot.
+ * @hide
+ */
+@SystemApi
+public class UiccSlotInfo implements Parcelable {
+    /**
+     * Card state.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "CARD_STATE_INFO_" }, value = {
+            CARD_STATE_INFO_ABSENT,
+            CARD_STATE_INFO_PRESENT,
+            CARD_STATE_INFO_ERROR,
+            CARD_STATE_INFO_RESTRICTED
+    })
+    public @interface CardStateInfo {}
+
+    /** Card state absent. */
+    public static final int CARD_STATE_INFO_ABSENT = 1;
+
+    /** Card state present. */
+    public static final int CARD_STATE_INFO_PRESENT = 2;
+
+    /** Card state error. */
+    public static final int CARD_STATE_INFO_ERROR = 3;
+
+    /** Card state restricted. */
+    public static final int CARD_STATE_INFO_RESTRICTED = 4;
+
+    public final boolean isActive;
+    public final boolean isEuicc;
+    public final String cardId;
+    public final @CardStateInfo int cardStateInfo;
+
+    public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
+        @Override
+        public UiccSlotInfo createFromParcel(Parcel in) {
+            return new UiccSlotInfo(in);
+        }
+
+        @Override
+        public UiccSlotInfo[] newArray(int size) {
+            return new UiccSlotInfo[size];
+        }
+    };
+
+    private UiccSlotInfo(Parcel in) {
+        isActive = in.readByte() != 0;
+        isEuicc = in.readByte() != 0;
+        cardId = in.readString();
+        cardStateInfo = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByte((byte) (isActive ? 1 : 0));
+        dest.writeByte((byte) (isEuicc ? 1 : 0));
+        dest.writeString(cardId);
+        dest.writeInt(cardStateInfo);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
+            @CardStateInfo int cardStateInfo) {
+        this.isActive = isActive;
+        this.isEuicc = isEuicc;
+        this.cardId = cardId;
+        this.cardStateInfo = cardStateInfo;
+    }
+
+    public boolean getIsActive() {
+        return isActive;
+    }
+
+    public boolean getIsEuicc() {
+        return isEuicc;
+    }
+
+    public String getCardId() {
+        return cardId;
+    }
+
+    @CardStateInfo
+    public int getCardStateInfo() {
+        return cardStateInfo;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccSlotInfo that = (UiccSlotInfo) obj;
+        return (isActive == that.isActive)
+                && (isEuicc == that.isEuicc)
+                && (cardId == that.cardId)
+                && (cardStateInfo == that.cardStateInfo);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        result = 31 * result + (isActive ? 1 : 0);
+        result = 31 * result + (isEuicc ? 1 : 0);
+        result = 31 * result + Objects.hashCode(cardId);
+        result = 31 * result + cardStateInfo;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "UiccSlotInfo (isActive="
+                + isActive
+                + ", isEuicc="
+                + isEuicc
+                + ", cardId="
+                + cardId
+                + ", cardState="
+                + cardStateInfo
+                + ")";
+    }
+}
diff --git a/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java b/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java
new file mode 100644
index 0000000..871ee4d
--- /dev/null
+++ b/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java
@@ -0,0 +1,114 @@
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to voice network registration.
+ * @hide
+ */
+public class VoiceSpecificRegistrationStates implements Parcelable{
+    /**
+     * oncurrent services support indicator. if
+     * registered on a CDMA system.
+     * false - Concurrent services not supported,
+     * true - Concurrent services supported
+     */
+     public final boolean cssSupported;
+
+    /**
+     * TSB-58 Roaming Indicator if registered
+     * on a CDMA or EVDO system or -1 if not.
+     * Valid values are 0-255.
+     */
+    public final int roamingIndicator;
+
+    /**
+     * indicates whether the current system is in the
+     * PRL if registered on a CDMA or EVDO system or -1 if
+     * not. 0=not in the PRL, 1=in the PRL
+     */
+    public final int systemIsInPrl;
+
+    /**
+     * default Roaming Indicator from the PRL,
+     * if registered on a CDMA or EVDO system or -1 if not.
+     * Valid values are 0-255.
+     */
+    public final int defaultRoamingIndicator;
+
+    VoiceSpecificRegistrationStates(boolean cssSupported, int roamingIndicator, int systemIsInPrl,
+            int defaultRoamingIndicator) {
+        this.cssSupported = cssSupported;
+        this.roamingIndicator = roamingIndicator;
+        this.systemIsInPrl = systemIsInPrl;
+        this.defaultRoamingIndicator = defaultRoamingIndicator;
+    }
+
+    private VoiceSpecificRegistrationStates(Parcel source) {
+        this.cssSupported = source.readBoolean();
+        this.roamingIndicator = source.readInt();
+        this.systemIsInPrl = source.readInt();
+        this.defaultRoamingIndicator = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(cssSupported);
+        dest.writeInt(roamingIndicator);
+        dest.writeInt(systemIsInPrl);
+        dest.writeInt(defaultRoamingIndicator);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "VoiceSpecificRegistrationStates {"
+                + " mCssSupported=" + cssSupported
+                + " mRoamingIndicator=" + roamingIndicator
+                + " mSystemIsInPrl=" + systemIsInPrl
+                + " mDefaultRoamingIndicator=" + defaultRoamingIndicator + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(cssSupported, roamingIndicator, systemIsInPrl,
+                defaultRoamingIndicator);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof VoiceSpecificRegistrationStates)) {
+            return false;
+        }
+
+        VoiceSpecificRegistrationStates other = (VoiceSpecificRegistrationStates) o;
+        return this.cssSupported == other.cssSupported
+                && this.roamingIndicator == other.roamingIndicator
+                && this.systemIsInPrl == other.systemIsInPrl
+                && this.defaultRoamingIndicator == other.defaultRoamingIndicator;
+    }
+
+
+    public static final Parcelable.Creator<VoiceSpecificRegistrationStates> CREATOR =
+            new Parcelable.Creator<VoiceSpecificRegistrationStates>() {
+                @Override
+                public VoiceSpecificRegistrationStates createFromParcel(Parcel source) {
+                    return new VoiceSpecificRegistrationStates(source);
+                }
+
+                @Override
+                public VoiceSpecificRegistrationStates[] newArray(int size) {
+                    return new VoiceSpecificRegistrationStates[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 2ab8d4f..73a05af 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -21,22 +21,23 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.hardware.radio.V1_0.ApnTypes;
-import android.net.NetworkUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Telephony;
 import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.net.MalformedURLException;
-import java.net.UnknownHostException;
-import java.net.URL;
 import java.net.InetAddress;
-import java.util.Arrays;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -67,8 +68,8 @@
     private final int mMtu;
 
     private final boolean mCarrierEnabled;
-    private final int mBearer;
-    private final int mBearerBitmask;
+
+    private final int mNetworkTypeBitmask;
 
     private final int mProfileId;
 
@@ -103,34 +104,6 @@
     }
 
     /**
-     * Radio Access Technology info.
-     * To check what values can hold, refer to ServiceState.java.
-     * This should be spread to other technologies,
-     * but currently only used for LTE(14) and EHRPD(13).
-     *
-     * @return the bearer info of the APN
-     * @hide
-     */
-    public int getBearer() {
-        return mBearer;
-    }
-
-    /**
-     * Returns the radio access technology bitmask for this APN.
-     *
-     * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
-     * technologies in ServiceState.
-     * This should be spread to other technologies,
-     * but currently only used for LTE(14) and EHRPD(13).
-     *
-     * @return the radio access technology bitmask
-     * @hide
-     */
-    public int getBearerBitmask() {
-        return mBearerBitmask;
-    }
-
-    /**
      * Returns the profile id to which the APN saved in modem.
      *
      * @return the profile id of the APN
@@ -411,6 +384,20 @@
         return mCarrierEnabled;
     }
 
+    /**
+     * Returns a bitmask describing the Radio Technologies(Network Types) which this APN may use.
+     *
+     * NetworkType bitmask is calculated from NETWORK_TYPE defined in {@link TelephonyManager}.
+     *
+     * Examples of Network Types include {@link TelephonyManager#NETWORK_TYPE_UNKNOWN},
+     * {@link TelephonyManager#NETWORK_TYPE_GPRS}, {@link TelephonyManager#NETWORK_TYPE_EDGE}.
+     *
+     * @return a bitmask describing the Radio Technologies(Network Types)
+     */
+    public int getNetworkTypeBitmask() {
+        return mNetworkTypeBitmask;
+    }
+
     /** @hide */
     @StringDef({
             MVNO_TYPE_SPN,
@@ -452,8 +439,7 @@
         this.mRoamingProtocol = builder.mRoamingProtocol;
         this.mMtu = builder.mMtu;
         this.mCarrierEnabled = builder.mCarrierEnabled;
-        this.mBearer = builder.mBearer;
-        this.mBearerBitmask = builder.mBearerBitmask;
+        this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask;
         this.mProfileId = builder.mProfileId;
         this.mModemCognitive = builder.mModemCognitive;
         this.mMaxConns = builder.mMaxConns;
@@ -467,8 +453,8 @@
     public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
             String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
             int mmsPort, String user, String password, int authType, List<String> types,
-            String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
-            int bearerBitmask, int profileId, boolean modemCognitive, int maxConns,
+            String protocol, String roamingProtocol, boolean carrierEnabled,
+            int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
             int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
         return new Builder()
                 .setId(id)
@@ -487,8 +473,7 @@
                 .setProtocol(protocol)
                 .setRoamingProtocol(roamingProtocol)
                 .setCarrierEnabled(carrierEnabled)
-                .setBearer(bearer)
-                .setBearerBitmask(bearerBitmask)
+                .setNetworkTypeBitmask(networkTypeBitmask)
                 .setProfileId(profileId)
                 .setModemCognitive(modemCognitive)
                 .setMaxConns(maxConns)
@@ -504,6 +489,14 @@
     public static ApnSetting makeApnSetting(Cursor cursor) {
         String[] types = parseTypes(
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
+        int networkTypeBitmask = cursor.getInt(
+                cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
+        if (networkTypeBitmask == 0) {
+            final int bearerBitmask = cursor.getInt(cursor.getColumnIndexOrThrow(
+                    Telephony.Carriers.BEARER_BITMASK));
+            networkTypeBitmask =
+                    ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
+        }
 
         return makeApnSetting(
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
@@ -529,9 +522,7 @@
                         Telephony.Carriers.ROAMING_PROTOCOL)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.BEARER_BITMASK)),
+                networkTypeBitmask,
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MODEM_COGNITIVE)) == 1,
@@ -551,7 +542,7 @@
         return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
                 apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
                 apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
-                apn.mCarrierEnabled, apn.mBearer, apn.mBearerBitmask, apn.mProfileId,
+                apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
                 apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
                 apn.mMvnoType, apn.mMvnoMatchData);
     }
@@ -559,7 +550,7 @@
     /** @hide */
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("[ApnSettingV3] ")
+        sb.append("[ApnSettingV4] ")
                 .append(mEntryName)
                 .append(", ").append(mId)
                 .append(", ").append(mOperatorNumeric)
@@ -579,8 +570,6 @@
         sb.append(", ").append(mProtocol);
         sb.append(", ").append(mRoamingProtocol);
         sb.append(", ").append(mCarrierEnabled);
-        sb.append(", ").append(mBearer);
-        sb.append(", ").append(mBearerBitmask);
         sb.append(", ").append(mProfileId);
         sb.append(", ").append(mModemCognitive);
         sb.append(", ").append(mMaxConns);
@@ -590,6 +579,7 @@
         sb.append(", ").append(mMvnoType);
         sb.append(", ").append(mMvnoMatchData);
         sb.append(", ").append(mPermanentFailed);
+        sb.append(", ").append(mNetworkTypeBitmask);
         return sb.toString();
     }
 
@@ -678,8 +668,6 @@
                 && Objects.equals(mProtocol, other.mProtocol)
                 && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
-                && Objects.equals(mBearer, other.mBearer)
-                && Objects.equals(mBearerBitmask, other.mBearerBitmask)
                 && Objects.equals(mProfileId, other.mProfileId)
                 && Objects.equals(mModemCognitive, other.mModemCognitive)
                 && Objects.equals(mMaxConns, other.mMaxConns)
@@ -687,13 +675,14 @@
                 && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
                 && Objects.equals(mMtu, other.mMtu)
                 && Objects.equals(mMvnoType, other.mMvnoType)
-                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData);
+                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
+                && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
     /**
      * Compare two APN settings
      *
-     * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. We only use this for
+     * Note: This method does not compare 'mId', 'mNetworkTypeBitmask'. We only use this for
      * determining if tearing a data call is needed when conditions change. See
      * cleanUpConnectionsOnUpdatedApns in DcTracker.
      *
@@ -752,13 +741,13 @@
                 && xorEquals(this.mProtocol, other.mProtocol)
                 && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
-                && Objects.equals(this.mBearerBitmask, other.mBearerBitmask)
                 && Objects.equals(this.mProfileId, other.mProfileId)
                 && Objects.equals(this.mMvnoType, other.mMvnoType)
                 && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
                 && xorEqualsURL(this.mMmsc, other.mMmsc)
                 && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
-                && xorEqualsPort(this.mMmsPort, other.mMmsPort));
+                && xorEqualsPort(this.mMmsPort, other.mMmsPort))
+                && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
     // Equal or one is not specified.
@@ -808,53 +797,33 @@
         return TextUtils.join(",", types);
     }
 
+    private String nullToEmpty(String stringValue) {
+        return stringValue == null ? "" : stringValue;
+    }
+
     /** @hide */
     // Called by DPM.
     public ContentValues toContentValues() {
         ContentValues apnValue = new ContentValues();
-        if (mOperatorNumeric != null) {
-            apnValue.put(Telephony.Carriers.NUMERIC, mOperatorNumeric);
-        }
-        if (mEntryName != null) {
-            apnValue.put(Telephony.Carriers.NAME, mEntryName);
-        }
-        if (mApnName != null) {
-            apnValue.put(Telephony.Carriers.APN, mApnName);
-        }
-        if (mProxy != null) {
-            apnValue.put(Telephony.Carriers.PROXY, inetAddressToString(mProxy));
-        }
+        apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
+        apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
+        apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
+        apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy));
         apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
-        if (mMmsc != null) {
-            apnValue.put(Telephony.Carriers.MMSC, URLToString(mMmsc));
-        }
+        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
         apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
-        if (mMmsProxy != null) {
-            apnValue.put(Telephony.Carriers.MMSPROXY, inetAddressToString(mMmsProxy));
-        }
-        if (mUser != null) {
-            apnValue.put(Telephony.Carriers.USER, mUser);
-        }
-        if (mPassword != null) {
-            apnValue.put(Telephony.Carriers.PASSWORD, mPassword);
-        }
+        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
+                ? "" : inetAddressToString(mMmsProxy));
+        apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
+        apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
         apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
         String apnType = deParseTypes(mTypes);
-        if (apnType != null) {
-            apnValue.put(Telephony.Carriers.TYPE, apnType);
-        }
-        if (mProtocol != null) {
-            apnValue.put(Telephony.Carriers.PROTOCOL, mProtocol);
-        }
-        if (mRoamingProtocol != null) {
-            apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, mRoamingProtocol);
-        }
+        apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
+        apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol));
+        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol));
         apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
-        // networkTypeBit.
-        apnValue.put(Telephony.Carriers.BEARER_BITMASK, mBearerBitmask);
-        if (mMvnoType != null) {
-            apnValue.put(Telephony.Carriers.MVNO_TYPE, mMvnoType);
-        }
+        apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+        apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
 
         return apnValue;
     }
@@ -905,8 +874,16 @@
         if (inetAddress == null) {
             return null;
         }
-        return TextUtils.isEmpty(inetAddress.getHostName())
-                ? inetAddress.getHostAddress() : inetAddress.getHostName();
+        final String inetAddressString = inetAddress.toString();
+        if (TextUtils.isEmpty(inetAddressString)) {
+            return null;
+        }
+        final String hostName = inetAddressString.substring(0, inetAddressString.indexOf("/"));
+        final String address = inetAddressString.substring(inetAddressString.indexOf("/") + 1);
+        if (TextUtils.isEmpty(hostName) && TextUtils.isEmpty(address)) {
+            return null;
+        }
+        return TextUtils.isEmpty(hostName) ? address : hostName;
     }
 
     private static int portFromString(String strPort) {
@@ -952,16 +929,33 @@
         dest.writeString(mRoamingProtocol);
         dest.writeInt(mCarrierEnabled ? 1: 0);
         dest.writeString(mMvnoType);
+        dest.writeInt(mNetworkTypeBitmask);
     }
 
     private static ApnSetting readFromParcel(Parcel in) {
-        return makeApnSetting(in.readInt(), in.readString(), in.readString(), in.readString(),
-                (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
-                in.readInt(), (URL)in.readValue(URL.class.getClassLoader()),
-                (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
-                in.readInt(), in.readString(), in.readString(), in.readInt(),
-                Arrays.asList(in.readStringArray()), in.readString(), in.readString(),
-                in.readInt() > 0, 0, 0, 0, false, 0, 0, 0, 0, in.readString(), null);
+        final int id = in.readInt();
+        final String operatorNumeric = in.readString();
+        final String entryName = in.readString();
+        final String apnName = in.readString();
+        final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+        final int port = in.readInt();
+        final URL mmsc = (URL)in.readValue(URL.class.getClassLoader());
+        final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+        final int mmsPort = in.readInt();
+        final String user = in.readString();
+        final String password = in.readString();
+        final int authType = in.readInt();
+        final List<String> types = Arrays.asList(in.readStringArray());
+        final String protocol = in.readString();
+        final String roamingProtocol = in.readString();
+        final boolean carrierEnabled = in.readInt() > 0;
+        final String mvnoType = in.readString();
+        final int networkTypeBitmask = in.readInt();
+
+        return makeApnSetting(id, operatorNumeric, entryName, apnName,
+                proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol,
+                roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+                0, 0, 0, 0, mvnoType, null);
     }
 
     public static final Parcelable.Creator<ApnSetting> CREATOR =
@@ -1061,9 +1055,8 @@
         private String mProtocol;
         private String mRoamingProtocol;
         private int mMtu;
+        private int mNetworkTypeBitmask;
         private boolean mCarrierEnabled;
-        private int mBearer;
-        private int mBearerBitmask;
         private int mProfileId;
         private boolean mModemCognitive;
         private int mMaxConns;
@@ -1078,6 +1071,16 @@
         public Builder() {}
 
         /**
+         * Sets the unique database id for this entry.
+         *
+         * @param id the unique database id to set for this entry
+         */
+        private Builder setId(int id) {
+            this.mId = id;
+            return this;
+        }
+
+        /**
          * Set the MTU size of the mobile interface to which the APN connected.
          *
          * @param mtu the MTU size to set for the APN
@@ -1089,28 +1092,6 @@
         }
 
         /**
-         * Sets bearer info.
-         *
-         * @param bearer the bearer info to set for the APN
-         * @hide
-         */
-        public Builder setBearer(int bearer) {
-            this.mBearer = bearer;
-            return this;
-        }
-
-        /**
-         * Sets the radio access technology bitmask for this APN.
-         *
-         * @param bearerBitmask the radio access technology bitmask to set for this APN
-         * @hide
-         */
-        public Builder setBearerBitmask(int bearerBitmask) {
-            this.mBearerBitmask = bearerBitmask;
-            return this;
-        }
-
-        /**
          * Sets the profile id to which the APN saved in modem.
          *
          * @param profileId the profile id to set for the APN
@@ -1298,16 +1279,6 @@
         }
 
         /**
-         * Sets the unique database id for this entry.
-         *
-         * @param id the unique database id to set for this entry
-         */
-        public Builder setId(int id) {
-            this.mId = id;
-            return this;
-        }
-
-        /**
          * Set the numeric operator ID for the APN.
          *
          * @param operatorNumeric the numeric operator ID to set for this entry
@@ -1341,7 +1312,7 @@
         }
 
         /**
-         * Sets the current status of APN.
+         * Sets the current status for this APN.
          *
          * @param carrierEnabled the current status to set for this APN
          */
@@ -1351,6 +1322,16 @@
         }
 
         /**
+         * Sets Radio Technology (Network Type) info for this APN.
+         *
+         * @param networkTypeBitmask the Radio Technology (Network Type) info
+         */
+        public Builder setNetworkTypeBitmask(int networkTypeBitmask) {
+            this.mNetworkTypeBitmask = networkTypeBitmask;
+            return this;
+        }
+
+        /**
          * Sets the MVNO match type for this APN.
          *
          * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index da51c86..ef3a183 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.net.LinkAddress;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -40,7 +41,7 @@
     private final int mActive;
     private final String mType;
     private final String mIfname;
-    private final List<InterfaceAddress> mAddresses;
+    private final List<LinkAddress> mAddresses;
     private final List<InetAddress> mDnses;
     private final List<InetAddress> mGateways;
     private final List<String> mPcscfs;
@@ -71,7 +72,7 @@
      */
     public DataCallResponse(int status, int suggestedRetryTime, int cid, int active,
                             @Nullable String type, @Nullable String ifname,
-                            @Nullable List<InterfaceAddress> addresses,
+                            @Nullable List<LinkAddress> addresses,
                             @Nullable List<InetAddress> dnses,
                             @Nullable List<InetAddress> gateways,
                             @Nullable List<String> pcscfs, int mtu) {
@@ -96,7 +97,7 @@
         mType = source.readString();
         mIfname = source.readString();
         mAddresses = new ArrayList<>();
-        source.readList(mAddresses, InterfaceAddress.class.getClassLoader());
+        source.readList(mAddresses, LinkAddress.class.getClassLoader());
         mDnses = new ArrayList<>();
         source.readList(mDnses, InetAddress.class.getClassLoader());
         mGateways = new ArrayList<>();
@@ -140,10 +141,10 @@
     public String getIfname() { return mIfname; }
 
     /**
-     * @return A list of {@link InterfaceAddress}
+     * @return A list of {@link LinkAddress}
      */
     @NonNull
-    public List<InterfaceAddress> getAddresses() { return mAddresses; }
+    public List<LinkAddress> getAddresses() { return mAddresses; }
 
     /**
      * @return A list of DNS server addresses, e.g., "192.0.1.3" or
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
new file mode 100644
index 0000000..63e8c3b
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.net.LinkProperties;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Rlog;
+import android.util.SparseArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class of data service. Services that extend DataService must register the service in
+ * their AndroidManifest to be detected by the framework. They must be protected by the permission
+ * "android.permission.BIND_DATA_SERVICE". The data service definition in the manifest must follow
+ * the following format:
+ * ...
+ * <service android:name=".xxxDataService"
+ *     android:permission="android.permission.BIND_DATA_SERVICE" >
+ *     <intent-filter>
+ *         <action android:name="android.telephony.data.DataService" />
+ *     </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class DataService extends Service {
+    private static final String TAG = DataService.class.getSimpleName();
+
+    public static final String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
+    public static final String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
+
+    /** {@hide} */
+    @IntDef(prefix = "REQUEST_REASON_", value = {
+            REQUEST_REASON_NORMAL,
+            REQUEST_REASON_HANDOVER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SetupDataReason {}
+
+    /** {@hide} */
+    @IntDef(prefix = "REQUEST_REASON_", value = {
+            REQUEST_REASON_NORMAL,
+            REQUEST_REASON_SHUTDOWN,
+            REQUEST_REASON_HANDOVER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeactivateDataReason {}
+
+
+    /** The reason of the data request is normal */
+    public static final int REQUEST_REASON_NORMAL = 1;
+
+    /** The reason of the data request is device shutdown */
+    public static final int REQUEST_REASON_SHUTDOWN = 2;
+
+    /** The reason of the data request is IWLAN handover */
+    public static final int REQUEST_REASON_HANDOVER = 3;
+
+    private static final int DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER                 = 1;
+    private static final int DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER                 = 2;
+    private static final int DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS            = 3;
+    private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL                      = 4;
+    private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL                 = 5;
+    private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN               = 6;
+    private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE                     = 7;
+    private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST                   = 8;
+    private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED      = 9;
+    private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED    = 10;
+    private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED            = 11;
+
+    private final HandlerThread mHandlerThread;
+
+    private final DataServiceHandler mHandler;
+
+    private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
+
+    private final IBinder mBinder = new IDataServiceWrapper();
+
+    /**
+     * The abstract class of the actual data service implementation. The data service provider
+     * must extend this class to support data connection. Note that each instance of data service
+     * provider is associated with one physical SIM slot.
+     */
+    public class DataServiceProvider {
+
+        private final int mSlotId;
+
+        private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>();
+
+        /**
+         * Constructor
+         * @param slotId SIM slot id the data service provider associated with.
+         */
+        public DataServiceProvider(int slotId) {
+            mSlotId = slotId;
+        }
+
+        /**
+         * @return SIM slot id the data service provider associated with.
+         */
+        public final int getSlotId() {
+            return mSlotId;
+        }
+
+        /**
+         * Setup a data connection. The data service provider must implement this method to support
+         * establishing a packet data connection. When completed or error, the service must invoke
+         * the provided callback to notify the platform.
+         *
+         * @param accessNetworkType Access network type that the data call will be established on.
+         *        Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
+         * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
+         * @param isRoaming True if the device is data roaming.
+         * @param allowRoaming True if data roaming is allowed by the user.
+         * @param reason The reason for data setup. Must be {@link #REQUEST_REASON_NORMAL} or
+         *        {@link #REQUEST_REASON_HANDOVER}.
+         * @param linkProperties If {@code reason} is {@link #REQUEST_REASON_HANDOVER}, this is the
+         *        link properties of the existing data connection, otherwise null.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
+         */
+        public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                                  boolean allowRoaming, @SetupDataReason int reason,
+                                  @Nullable LinkProperties linkProperties,
+                                  @Nullable DataServiceCallback callback) {
+            // The default implementation is to return unsupported.
+            callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+        }
+
+        /**
+         * Deactivate a data connection. The data service provider must implement this method to
+         * support data connection tear down. When completed or error, the service must invoke the
+         * provided callback to notify the platform.
+         *
+         * @param cid Call id returned in the callback of {@link DataServiceProvider#setupDataCall(
+         *        int, DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)}.
+         * @param reason The reason for data deactivation. Must be {@link #REQUEST_REASON_NORMAL},
+         *        {@link #REQUEST_REASON_SHUTDOWN} or {@link #REQUEST_REASON_HANDOVER}.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
+         *
+         */
+        public void deactivateDataCall(int cid, @DeactivateDataReason int reason,
+                                       @Nullable DataServiceCallback callback) {
+            // The default implementation is to return unsupported.
+            callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+        }
+
+        /**
+         * Set an APN to initial attach network.
+         *
+         * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
+         * @param isRoaming True if the device is data roaming.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
+         */
+        public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+                                        @Nullable DataServiceCallback callback) {
+            // The default implementation is to return unsupported.
+            callback.onSetInitialAttachApnComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+        }
+
+        /**
+         * Send current carrier's data profiles to the data service for data call setup. This is
+         * only for CDMA carrier that can change the profile through OTA. The data service should
+         * always uses the latest data profile sent by the framework.
+         *
+         * @param dps A list of data profiles.
+         * @param isRoaming True if the device is data roaming.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
+         */
+        public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+                                   @Nullable DataServiceCallback callback) {
+            // The default implementation is to return unsupported.
+            callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+        }
+
+        /**
+         * Get the active data call list.
+         *
+         * @param callback The result callback for this request.
+         */
+        public void getDataCallList(@NonNull DataServiceCallback callback) {
+            // The default implementation is to return unsupported.
+            callback.onGetDataCallListComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+        }
+
+        private void registerForDataCallListChanged(IDataServiceCallback callback) {
+            synchronized (mDataCallListChangedCallbacks) {
+                mDataCallListChangedCallbacks.add(callback);
+            }
+        }
+
+        private void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+            synchronized (mDataCallListChangedCallbacks) {
+                mDataCallListChangedCallbacks.remove(callback);
+            }
+        }
+
+        /**
+         * Notify the system that current data call list changed. Data service must invoke this
+         * method whenever there is any data call status changed.
+         *
+         * @param dataCallList List of the current active data call.
+         */
+        public final void notifyDataCallListChanged(List<DataCallResponse> dataCallList) {
+            synchronized (mDataCallListChangedCallbacks) {
+                for (IDataServiceCallback callback : mDataCallListChangedCallbacks) {
+                    mHandler.obtainMessage(DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED, mSlotId,
+                            0, new DataCallListChangedIndication(dataCallList, callback))
+                            .sendToTarget();
+                }
+            }
+        }
+
+        /**
+         * Called when the instance of data service is destroyed (e.g. got unbind or binder died).
+         */
+        @CallSuper
+        protected void onDestroy() {
+            mDataCallListChangedCallbacks.clear();
+        }
+    }
+
+    private static final class SetupDataCallRequest {
+        public final int accessNetworkType;
+        public final DataProfile dataProfile;
+        public final boolean isRoaming;
+        public final boolean allowRoaming;
+        public final int reason;
+        public final LinkProperties linkProperties;
+        public final IDataServiceCallback callback;
+        SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                             boolean allowRoaming, int reason, LinkProperties linkProperties,
+                             IDataServiceCallback callback) {
+            this.accessNetworkType = accessNetworkType;
+            this.dataProfile = dataProfile;
+            this.isRoaming = isRoaming;
+            this.allowRoaming = allowRoaming;
+            this.linkProperties = linkProperties;
+            this.reason = reason;
+            this.callback = callback;
+        }
+    }
+
+    private static final class DeactivateDataCallRequest {
+        public final int cid;
+        public final int reason;
+        public final IDataServiceCallback callback;
+        DeactivateDataCallRequest(int cid, int reason, IDataServiceCallback callback) {
+            this.cid = cid;
+            this.reason = reason;
+            this.callback = callback;
+        }
+    }
+
+    private static final class SetInitialAttachApnRequest {
+        public final DataProfile dataProfile;
+        public final boolean isRoaming;
+        public final IDataServiceCallback callback;
+        SetInitialAttachApnRequest(DataProfile dataProfile, boolean isRoaming,
+                                   IDataServiceCallback callback) {
+            this.dataProfile = dataProfile;
+            this.isRoaming = isRoaming;
+            this.callback = callback;
+        }
+    }
+
+    private static final class SetDataProfileRequest {
+        public final List<DataProfile> dps;
+        public final boolean isRoaming;
+        public final IDataServiceCallback callback;
+        SetDataProfileRequest(List<DataProfile> dps, boolean isRoaming,
+                              IDataServiceCallback callback) {
+            this.dps = dps;
+            this.isRoaming = isRoaming;
+            this.callback = callback;
+        }
+    }
+
+    private static final class DataCallListChangedIndication {
+        public final List<DataCallResponse> dataCallList;
+        public final IDataServiceCallback callback;
+        DataCallListChangedIndication(List<DataCallResponse> dataCallList,
+                                      IDataServiceCallback callback) {
+            this.dataCallList = dataCallList;
+            this.callback = callback;
+        }
+    }
+
+    private class DataServiceHandler extends Handler {
+
+        DataServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            IDataServiceCallback callback;
+            final int slotId = message.arg1;
+            DataServiceProvider serviceProvider = mServiceMap.get(slotId);
+
+            switch (message.what) {
+                case DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER:
+                    serviceProvider = createDataServiceProvider(message.arg1);
+                    if (serviceProvider != null) {
+                        mServiceMap.put(slotId, serviceProvider);
+                    }
+                    break;
+                case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER:
+                    if (serviceProvider != null) {
+                        serviceProvider.onDestroy();
+                        mServiceMap.remove(slotId);
+                    }
+                    break;
+                case DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS:
+                    for (int i = 0; i < mServiceMap.size(); i++) {
+                        serviceProvider = mServiceMap.get(i);
+                        if (serviceProvider != null) {
+                            serviceProvider.onDestroy();
+                        }
+                    }
+                    mServiceMap.clear();
+                    break;
+                case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
+                    if (serviceProvider == null) break;
+                    SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
+                    serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType,
+                            setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
+                            setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
+                            setupDataCallRequest.linkProperties,
+                            (setupDataCallRequest.callback != null)
+                                    ? new DataServiceCallback(setupDataCallRequest.callback)
+                                    : null);
+
+                    break;
+                case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
+                    if (serviceProvider == null) break;
+                    DeactivateDataCallRequest deactivateDataCallRequest =
+                            (DeactivateDataCallRequest) message.obj;
+                    serviceProvider.deactivateDataCall(deactivateDataCallRequest.cid,
+                            deactivateDataCallRequest.reason,
+                            (deactivateDataCallRequest.callback != null)
+                                    ? new DataServiceCallback(deactivateDataCallRequest.callback)
+                                    : null);
+                    break;
+                case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
+                    if (serviceProvider == null) break;
+                    SetInitialAttachApnRequest setInitialAttachApnRequest =
+                            (SetInitialAttachApnRequest) message.obj;
+                    serviceProvider.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
+                            setInitialAttachApnRequest.isRoaming,
+                            (setInitialAttachApnRequest.callback != null)
+                                    ? new DataServiceCallback(setInitialAttachApnRequest.callback)
+                                    : null);
+                    break;
+                case DATA_SERVICE_REQUEST_SET_DATA_PROFILE:
+                    if (serviceProvider == null) break;
+                    SetDataProfileRequest setDataProfileRequest =
+                            (SetDataProfileRequest) message.obj;
+                    serviceProvider.setDataProfile(setDataProfileRequest.dps,
+                            setDataProfileRequest.isRoaming,
+                            (setDataProfileRequest.callback != null)
+                                    ? new DataServiceCallback(setDataProfileRequest.callback)
+                                    : null);
+                    break;
+                case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST:
+                    if (serviceProvider == null) break;
+
+                    serviceProvider.getDataCallList(new DataServiceCallback(
+                            (IDataServiceCallback) message.obj));
+                    break;
+                case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED:
+                    if (serviceProvider == null) break;
+                    serviceProvider.registerForDataCallListChanged((IDataServiceCallback) message.obj);
+                    break;
+                case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED:
+                    if (serviceProvider == null) break;
+                    callback = (IDataServiceCallback) message.obj;
+                    serviceProvider.unregisterForDataCallListChanged(callback);
+                    break;
+                case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED:
+                    if (serviceProvider == null) break;
+                    DataCallListChangedIndication indication =
+                            (DataCallListChangedIndication) message.obj;
+                    try {
+                        indication.callback.onDataCallListChanged(indication.dataCallList);
+                    } catch (RemoteException e) {
+                        loge("Failed to call onDataCallListChanged. " + e);
+                    }
+                    break;
+            }
+        }
+    }
+
+    /** @hide */
+    protected DataService() {
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+
+        mHandler = new DataServiceHandler(mHandlerThread.getLooper());
+        log("Data service created");
+    }
+
+    /**
+     * Create the instance of {@link DataServiceProvider}. Data service provider must override
+     * this method to facilitate the creation of {@link DataServiceProvider} instances. The system
+     * will call this method after binding the data service for each active SIM slot id.
+     *
+     * @param slotId SIM slot id the data service associated with.
+     * @return Data service object
+     */
+    public abstract DataServiceProvider createDataServiceProvider(int slotId);
+
+    /** @hide */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (intent == null || !DATA_SERVICE_INTERFACE.equals(intent.getAction())) {
+            loge("Unexpected intent " + intent);
+            return null;
+        }
+        return mBinder;
+    }
+
+    /** @hide */
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mHandler.obtainMessage(DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS).sendToTarget();
+        return false;
+    }
+
+    /** @hide */
+    @Override
+    public void onDestroy() {
+        mHandlerThread.quit();
+    }
+
+    /**
+     * A wrapper around IDataService that forwards calls to implementations of {@link DataService}.
+     */
+    private class IDataServiceWrapper extends IDataService.Stub {
+        @Override
+        public void createDataServiceProvider(int slotId) {
+            mHandler.obtainMessage(DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER, slotId, 0)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void removeDataServiceProvider(int slotId) {
+            mHandler.obtainMessage(DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER, slotId, 0)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void setupDataCall(int slotId, int accessNetworkType, DataProfile dataProfile,
+                                  boolean isRoaming, boolean allowRoaming, int reason,
+                                  LinkProperties linkProperties, IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotId, 0,
+                    new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
+                            allowRoaming, reason, linkProperties, callback))
+                    .sendToTarget();
+        }
+
+        @Override
+        public void deactivateDataCall(int slotId, int cid, int reason,
+                                       IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotId, 0,
+                    new DeactivateDataCallRequest(cid, reason, callback))
+                    .sendToTarget();
+        }
+
+        @Override
+        public void setInitialAttachApn(int slotId, DataProfile dataProfile, boolean isRoaming,
+                                        IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, slotId, 0,
+                    new SetInitialAttachApnRequest(dataProfile, isRoaming, callback))
+                    .sendToTarget();
+        }
+
+        @Override
+        public void setDataProfile(int slotId, List<DataProfile> dps, boolean isRoaming,
+                                   IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, slotId, 0,
+                    new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget();
+        }
+
+        @Override
+        public void getDataCallList(int slotId, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("getDataCallList: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, slotId, 0,
+                    callback).sendToTarget();
+        }
+
+        @Override
+        public void registerForDataCallListChanged(int slotId, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("registerForDataCallListChanged: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, slotId,
+                    0, callback).sendToTarget();
+        }
+
+        @Override
+        public void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("unregisterForDataCallListChanged: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, slotId,
+                    0, callback).sendToTarget();
+        }
+    }
+
+    private void log(String s) {
+        Rlog.d(TAG, s);
+    }
+
+    private void loge(String s) {
+        Rlog.e(TAG, s);
+    }
+}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
new file mode 100644
index 0000000..4af31b5
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.net.LinkProperties;
+import android.os.RemoteException;
+import android.telephony.Rlog;
+import android.telephony.data.DataService.DataServiceProvider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * Data service callback, which is for bound data service to invoke for solicited and unsolicited
+ * response. The caller is responsible to create a callback object for each single asynchronous
+ * request.
+ *
+ * @hide
+ */
+@SystemApi
+public class DataServiceCallback {
+
+    private static final String TAG = DataServiceCallback.class.getSimpleName();
+
+    /**
+     * Result of data requests
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
+            RESULT_ERROR_ILLEGAL_STATE})
+    public @interface ResultCode {}
+
+    /** Request is completed successfully */
+    public static final int RESULT_SUCCESS              = 0;
+    /** Request is not support */
+    public static final int RESULT_ERROR_UNSUPPORTED    = 1;
+    /** Request contains invalid arguments */
+    public static final int RESULT_ERROR_INVALID_ARG    = 2;
+    /** Service is busy */
+    public static final int RESULT_ERROR_BUSY           = 3;
+    /** Request sent in illegal state */
+    public static final int RESULT_ERROR_ILLEGAL_STATE  = 4;
+
+    private final WeakReference<IDataServiceCallback> mCallback;
+
+    /** @hide */
+    public DataServiceCallback(IDataServiceCallback callback) {
+        mCallback = new WeakReference<>(callback);
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#setupDataCall(int,
+     * DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)} .
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}.
+     * @param response Setup data call response.
+     */
+    public void onSetupDataCallComplete(@ResultCode int result, DataCallResponse response) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onSetupDataCallComplete(result, response);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
+            }
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#deactivateDataCall(int,
+     * int, DataServiceCallback)}
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}.
+     */
+    public void onDeactivateDataCallComplete(@ResultCode int result) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onDeactivateDataCallComplete(result);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
+            }
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#setInitialAttachApn(
+     * DataProfile, boolean, DataServiceCallback)}.
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}.
+     */
+    public void onSetInitialAttachApnComplete(@ResultCode int result) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onSetInitialAttachApnComplete(result);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onSetInitialAttachApnComplete on the remote");
+            }
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#setDataProfile(List,
+     * boolean, DataServiceCallback)}.
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}.
+     */
+    @SystemApi
+    public void onSetDataProfileComplete(@ResultCode int result) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onSetDataProfileComplete(result);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onSetDataProfileComplete on the remote");
+            }
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#getDataCallList(
+     * DataServiceCallback)}.
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}.
+     * @param dataCallList List of the current active data connection.
+     */
+    public void onGetDataCallListComplete(@ResultCode int result,
+                                          List<DataCallResponse> dataCallList) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onGetDataCallListComplete(result, dataCallList);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onGetDataCallListComplete on the remote");
+            }
+        }
+    }
+
+    /**
+     * Called to indicate that data connection list changed.
+     *
+     * @param dataCallList List of the current active data connection.
+     */
+    public void onDataCallListChanged(List<DataCallResponse> dataCallList) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onDataCallListChanged(dataCallList);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
+            }
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
new file mode 100644
index 0000000..d4d9be8
--- /dev/null
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.net.LinkProperties;
+import android.telephony.data.DataProfile;
+import android.telephony.data.IDataServiceCallback;
+
+/**
+ * {@hide}
+ */
+oneway interface IDataService
+{
+    void createDataServiceProvider(int slotId);
+    void removeDataServiceProvider(int slotId);
+    void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
+                       boolean allowRoaming, int reason, in LinkProperties linkProperties,
+                       IDataServiceCallback callback);
+    void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback);
+    void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming,
+                             IDataServiceCallback callback);
+    void setDataProfile(int slotId, in List<DataProfile> dps, boolean isRoaming,
+                        IDataServiceCallback callback);
+    void getDataCallList(int slotId, IDataServiceCallback callback);
+    void registerForDataCallListChanged(int slotId, IDataServiceCallback callback);
+    void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback);
+}
diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
new file mode 100644
index 0000000..856185b
--- /dev/null
+++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.telephony.data.DataCallResponse;
+
+/**
+ * The call back interface
+ * @hide
+ */
+oneway interface IDataServiceCallback
+{
+    void onSetupDataCallComplete(int result, in DataCallResponse dataCallResponse);
+    void onDeactivateDataCallComplete(int result);
+    void onSetInitialAttachApnComplete(int result);
+    void onSetDataProfileComplete(int result);
+    void onGetDataCallListComplete(int result, in List<DataCallResponse> dataCallList);
+    void onDataCallListChanged(in List<DataCallResponse> dataCallList);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.java b/telephony/java/android/telephony/data/InterfaceAddress.java
deleted file mode 100644
index 00d212a..0000000
--- a/telephony/java/android/telephony/data/InterfaceAddress.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.data;
-
-import android.annotation.SystemApi;
-import android.net.NetworkUtils;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * This class represents a Network Interface address. In short it's an IP address, a subnet mask
- * when the address is an IPv4 one. An IP address and a network prefix length in the case of IPv6
- * address.
- *
- * @hide
- */
-@SystemApi
-public final class InterfaceAddress implements Parcelable {
-
-    private final InetAddress mInetAddress;
-
-    private final int mPrefixLength;
-
-    /**
-     * @param inetAddress A {@link InetAddress} of the address
-     * @param prefixLength The network prefix length for this address.
-     */
-    public InterfaceAddress(InetAddress inetAddress, int prefixLength) {
-        mInetAddress = inetAddress;
-        mPrefixLength = prefixLength;
-    }
-
-    /**
-     * @param address The address in string format
-     * @param prefixLength The network prefix length for this address.
-     * @throws UnknownHostException
-     */
-    public InterfaceAddress(String address, int prefixLength) throws UnknownHostException {
-        InetAddress ia;
-        try {
-            ia = NetworkUtils.numericToInetAddress(address);
-        } catch (IllegalArgumentException e) {
-            throw new UnknownHostException("Non-numeric ip addr=" + address);
-        }
-        mInetAddress = ia;
-        mPrefixLength = prefixLength;
-    }
-
-    public InterfaceAddress(Parcel source) {
-        mInetAddress = (InetAddress) source.readSerializable();
-        mPrefixLength = source.readInt();
-    }
-
-    /**
-     * @return an InetAddress for this address.
-     */
-    public InetAddress getAddress() { return mInetAddress; }
-
-    /**
-     * @return The network prefix length for this address.
-     */
-    public int getNetworkPrefixLength() { return mPrefixLength; }
-
-    @Override
-    public boolean equals (Object o) {
-        if (this == o) return true;
-
-        if (o == null || !(o instanceof InterfaceAddress)) {
-            return false;
-        }
-
-        InterfaceAddress other = (InterfaceAddress) o;
-        return this.mInetAddress.equals(other.mInetAddress)
-                && this.mPrefixLength == other.mPrefixLength;
-    }
-
-    @Override
-    public int hashCode() {
-        return mInetAddress.hashCode() * 31 + mPrefixLength * 37;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public String toString() {
-        return mInetAddress + "/" + mPrefixLength;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeSerializable(mInetAddress);
-        dest.writeInt(mPrefixLength);
-    }
-
-    public static final Parcelable.Creator<InterfaceAddress> CREATOR =
-            new Parcelable.Creator<InterfaceAddress>() {
-        @Override
-        public InterfaceAddress createFromParcel(Parcel source) {
-            return new InterfaceAddress(source);
-        }
-
-        @Override
-        public InterfaceAddress[] newArray(int size) {
-            return new InterfaceAddress[size];
-        }
-    };
-}
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 29849c1..a1a6a5a 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -15,14 +15,40 @@
  */
 package android.telephony.euicc;
 
+import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.service.euicc.EuiccProfileInfo;
 import android.util.Log;
 
+import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
+import com.android.internal.telephony.euicc.ICancelSessionCallback;
+import com.android.internal.telephony.euicc.IDeleteProfileCallback;
+import com.android.internal.telephony.euicc.IDisableProfileCallback;
 import com.android.internal.telephony.euicc.IEuiccCardController;
 import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+import com.android.internal.telephony.euicc.IGetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback;
+import com.android.internal.telephony.euicc.IGetProfileCallback;
+import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback;
+import com.android.internal.telephony.euicc.IGetSmdsAddressCallback;
+import com.android.internal.telephony.euicc.IListNotificationsCallback;
+import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback;
+import com.android.internal.telephony.euicc.IPrepareDownloadCallback;
+import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback;
+import com.android.internal.telephony.euicc.IResetMemoryCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback;
+import com.android.internal.telephony.euicc.ISetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.ISetNicknameCallback;
+import com.android.internal.telephony.euicc.ISwitchToProfileCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * EuiccCardManager is the application interface to an eSIM card.
@@ -34,9 +60,59 @@
 public class EuiccCardManager {
     private static final String TAG = "EuiccCardManager";
 
+    /** Reason for canceling a profile download session */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "CANCEL_REASON_" }, value = {
+            CANCEL_REASON_END_USER_REJECTED,
+            CANCEL_REASON_POSTPONED,
+            CANCEL_REASON_TIMEOUT,
+            CANCEL_REASON_PPR_NOT_ALLOWED
+    })
+    public @interface CancelReason {}
+
+    /**
+     * The end user has rejected the download. The profile will be put into the error state and
+     * cannot be downloaded again without the operator's change.
+     */
+    public static final int CANCEL_REASON_END_USER_REJECTED = 0;
+
+    /** The download has been postponed and can be restarted later. */
+    public static final int CANCEL_REASON_POSTPONED = 1;
+
+    /** The download has been timed out and can be restarted later. */
+    public static final int CANCEL_REASON_TIMEOUT = 2;
+
+    /**
+     * The profile to be downloaded cannot be installed due to its policy rule is not allowed by
+     * the RAT (Rules Authorisation Table) on the eUICC or by other installed profiles. The
+     * download can be restarted later.
+     */
+    public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3;
+
+    /** Options for resetting eUICC memory */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "RESET_OPTION_" }, value = {
+            RESET_OPTION_DELETE_OPERATIONAL_PROFILES,
+            RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
+            RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS
+    })
+    public @interface ResetOption {}
+
+    /** Deletes all operational profiles. */
+    public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1;
+
+    /** Deletes all field-loaded testing profiles. */
+    public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 1 << 1;
+
+    /** Resets the default SM-DP+ address. */
+    public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 1 << 2;
+
     /** Result code of execution with no error. */
     public static final int RESULT_OK = 0;
 
+    /** Result code of an unknown error. */
+    public static final int RESULT_UNKNOWN_ERROR = -1;
+
     /**
      * Callback to receive the result of an eUICC card API.
      *
@@ -69,11 +145,12 @@
     /**
      * Gets all the profiles on eUicc.
      *
-     * @param callback the callback to get the result code and all the profiles.
+     * @param cardId The Id of the eUICC.
+     * @param callback The callback to get the result code and all the profiles.
      */
-    public void getAllProfiles(ResultCallback<EuiccProfileInfo[]> callback) {
+    public void getAllProfiles(String cardId, ResultCallback<EuiccProfileInfo[]> callback) {
         try {
-            getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(),
+            getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(), cardId,
                     new IGetAllProfilesCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
@@ -85,4 +162,522 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Gets the profile of the given iccid.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile.
+     * @param callback The callback to get the result code and profile.
+     */
+    public void getProfile(String cardId, String iccid, ResultCallback<EuiccProfileInfo> callback) {
+        try {
+            getIEuiccCardController().getProfile(mContext.getOpPackageName(), cardId, iccid,
+                    new IGetProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo profile) {
+                            callback.onComplete(resultCode, profile);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Disables the profile of the given iccid.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile.
+     * @param refresh Whether sending the REFRESH command to modem.
+     * @param callback The callback to get the result code.
+     */
+    public void disableProfile(String cardId, String iccid, boolean refresh,
+            ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid,
+                    refresh, new IDisableProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            callback.onComplete(resultCode, null);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling disableProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Switches from the current profile to another profile. The current profile will be disabled
+     * and the specified profile will be enabled.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile to switch to.
+     * @param refresh Whether sending the REFRESH command to modem.
+     * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+     */
+    public void switchToProfile(String cardId, String iccid, boolean refresh,
+            ResultCallback<EuiccProfileInfo> callback) {
+        try {
+            getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
+                    refresh, new ISwitchToProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo profile) {
+                            callback.onComplete(resultCode, profile);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling switchToProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the nickname of the profile of the given iccid.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile.
+     * @param nickname The nickname of the profile.
+     * @param callback The callback to get the result code.
+     */
+    public void setNickname(String cardId, String iccid, String nickname,
+            ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().setNickname(mContext.getOpPackageName(), cardId, iccid,
+                    nickname, new ISetNicknameCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            callback.onComplete(resultCode, null);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling setNickname", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Deletes the profile of the given iccid from eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile.
+     * @param callback The callback to get the result code.
+     */
+    public void deleteProfile(String cardId, String iccid, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().deleteProfile(mContext.getOpPackageName(), cardId, iccid,
+                    new IDeleteProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            callback.onComplete(resultCode, null);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling deleteProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Resets the eUICC memory.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param options Bits of the options of resetting which parts of the eUICC memory. See
+     *     EuiccCard for details.
+     * @param callback The callback to get the result code.
+     */
+    public void resetMemory(String cardId, @ResetOption int options, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().resetMemory(mContext.getOpPackageName(), cardId, options,
+                    new IResetMemoryCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            callback.onComplete(resultCode, null);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling resetMemory", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the default SM-DP+ address from eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param callback The callback to get the result code and the default SM-DP+ address.
+     */
+    public void getDefaultSmdpAddress(String cardId, ResultCallback<String> callback) {
+        try {
+            getIEuiccCardController().getDefaultSmdpAddress(mContext.getOpPackageName(), cardId,
+                    new IGetDefaultSmdpAddressCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, String address) {
+                            callback.onComplete(resultCode, address);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getDefaultSmdpAddress", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the SM-DS address from eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param callback The callback to get the result code and the SM-DS address.
+     */
+    public void getSmdsAddress(String cardId, ResultCallback<String> callback) {
+        try {
+            getIEuiccCardController().getSmdsAddress(mContext.getOpPackageName(), cardId,
+                    new IGetSmdsAddressCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, String address) {
+                            callback.onComplete(resultCode, address);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getSmdsAddress", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the default SM-DP+ address of eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param defaultSmdpAddress The default SM-DP+ address to set.
+     * @param callback The callback to get the result code.
+     */
+    public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().setDefaultSmdpAddress(mContext.getOpPackageName(), cardId,
+                    defaultSmdpAddress,
+                    new ISetDefaultSmdpAddressCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            callback.onComplete(resultCode, null);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling setDefaultSmdpAddress", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets Rules Authorisation Table.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param callback the callback to get the result code and the rule authorisation table.
+     */
+    public void getRulesAuthTable(String cardId, ResultCallback<EuiccRulesAuthTable> callback) {
+        try {
+            getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(), cardId,
+                    new IGetRulesAuthTableCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
+                            callback.onComplete(resultCode, rat);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getRulesAuthTable", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the eUICC challenge for new profile downloading.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param callback the callback to get the result code and the challenge.
+     */
+    public void getEuiccChallenge(String cardId, ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(), cardId,
+                    new IGetEuiccChallengeCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] challenge) {
+                            callback.onComplete(resultCode, challenge);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getEuiccChallenge", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param callback the callback to get the result code and the info1.
+     */
+    public void getEuiccInfo1(String cardId, ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(), cardId,
+                    new IGetEuiccInfo1Callback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] info) {
+                            callback.onComplete(resultCode, info);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getEuiccInfo1", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param callback the callback to get the result code and the info2.
+     */
+    public void getEuiccInfo2(String cardId, ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(), cardId,
+                    new IGetEuiccInfo2Callback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] info) {
+                            callback.onComplete(resultCode, info);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getEuiccInfo2", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Authenticates the SM-DP+ server by the eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param matchingId the activation code token defined in GSMA RSP v2.0+ or empty when it is not
+     *     required.
+     * @param serverSigned1 ASN.1 data in byte array signed and returned by the SM-DP+ server.
+     * @param serverSignature1 ASN.1 data in byte array indicating a SM-DP+ signature which is
+     *     returned by SM-DP+ server.
+     * @param euiccCiPkIdToBeUsed ASN.1 data in byte array indicating CI Public Key Identifier to be
+     *     used by the eUICC for signature which is returned by SM-DP+ server. This is defined in
+     *     GSMA RSP v2.0+.
+     * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by
+     *     SM-DP+ server.
+     * @param callback the callback to get the result code and a byte array which represents a
+     *     {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
+     */
+    public void authenticateServer(String cardId, String matchingId, byte[] serverSigned1,
+            byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
+            ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().authenticateServer(
+                    mContext.getOpPackageName(),
+                    cardId,
+                    matchingId,
+                    serverSigned1,
+                    serverSignature1,
+                    euiccCiPkIdToBeUsed,
+                    serverCertificate,
+                    new IAuthenticateServerCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            callback.onComplete(resultCode, response);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling authenticateServer", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Prepares the profile download request sent to SM-DP+.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param hashCc the hash of confirmation code. It can be null if there is no confirmation code
+     *     required.
+     * @param smdpSigned2 ASN.1 data in byte array indicating the data to be signed by the SM-DP+
+     *     returned by SM-DP+ server.
+     * @param smdpSignature2 ASN.1 data in byte array indicating the SM-DP+ signature returned by
+     *     SM-DP+ server.
+     * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned
+     *     by SM-DP+ server.
+     * @param callback the callback to get the result code and a byte array which represents a
+     *     {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
+     */
+    public void prepareDownload(String cardId, @Nullable byte[] hashCc, byte[] smdpSigned2,
+            byte[] smdpSignature2, byte[] smdpCertificate, ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().prepareDownload(
+                    mContext.getOpPackageName(),
+                    cardId,
+                    hashCc,
+                    smdpSigned2,
+                    smdpSignature2,
+                    smdpCertificate,
+                    new IPrepareDownloadCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            callback.onComplete(resultCode, response);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling prepareDownload", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Loads a downloaded bound profile package onto the eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
+     * @param callback the callback to get the result code and a byte array which represents a
+     *     {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
+     */
+    public void loadBoundProfilePackage(String cardId, byte[] boundProfilePackage,
+            ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().loadBoundProfilePackage(
+                    mContext.getOpPackageName(),
+                    cardId,
+                    boundProfilePackage,
+                    new ILoadBoundProfilePackageCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            callback.onComplete(resultCode, response);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling loadBoundProfilePackage", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Cancels the current profile download session.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param transactionId the transaction ID returned by SM-DP+ server.
+     * @param reason the cancel reason.
+     * @param callback the callback to get the result code and an byte[] which represents a
+     *     {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
+     */
+    public void cancelSession(String cardId, byte[] transactionId, @CancelReason int reason,
+            ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().cancelSession(
+                    mContext.getOpPackageName(),
+                    cardId,
+                    transactionId,
+                    reason,
+                    new ICancelSessionCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            callback.onComplete(resultCode, response);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling cancelSession", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Lists all notifications of the given {@code notificationEvents}.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+     * @param callback the callback to get the result code and the list of notifications.
+     */
+    public void listNotifications(String cardId, @EuiccNotification.Event int events,
+            ResultCallback<EuiccNotification[]> callback) {
+        try {
+            getIEuiccCardController().listNotifications(mContext.getOpPackageName(), cardId, events,
+                    new IListNotificationsCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccNotification[] notifications) {
+                            callback.onComplete(resultCode, notifications);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling listNotifications", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieves contents of all notification of the given {@code events}.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+     * @param callback the callback to get the result code and the list of notifications.
+     */
+    public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events,
+            ResultCallback<EuiccNotification[]> callback) {
+        try {
+            getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), cardId,
+                    events, new IRetrieveNotificationListCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccNotification[] notifications) {
+                            callback.onComplete(resultCode, notifications);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling retrieveNotificationList", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieves the content of a notification of the given {@code seqNumber}.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param seqNumber the sequence number of the notification.
+     * @param callback the callback to get the result code and the notification.
+     */
+    public void retrieveNotification(String cardId, int seqNumber,
+            ResultCallback<EuiccNotification> callback) {
+        try {
+            getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), cardId,
+                    seqNumber, new IRetrieveNotificationCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccNotification notification) {
+                            callback.onComplete(resultCode, notification);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling retrieveNotification", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes a notification from eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param seqNumber the sequence number of the notification.
+     * @param callback the callback to get the result code.
+     */
+    public void removeNotificationFromList(String cardId, int seqNumber,
+            ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().removeNotificationFromList(
+                    mContext.getOpPackageName(),
+                    cardId,
+                    seqNumber,
+                    new IRemoveNotificationFromListCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            callback.onComplete(resultCode, null);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling removeNotificationFromList", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 3dfadf5..662056e 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -71,8 +72,18 @@
      * TODO(b/35851809): Make this a SystemApi.
      */
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_OTA_STATUS_CHANGED
-            = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+    public static final String ACTION_OTA_STATUS_CHANGED =
+            "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+
+    /**
+     * Broadcast Action: The action sent to carrier app so it knows the carrier setup is not
+     * completed.
+     *
+     * TODO(b/35851809): Make this a public API.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_NOTIFY_CARRIER_SETUP =
+            "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP";
 
     /**
      * Intent action to provision an embedded subscription.
@@ -265,8 +276,8 @@
      *
      * @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready,
      *     {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
+     * TODO(b/35851809): Make this a SystemApi.
      */
-    @SystemApi
     public int getOtaStatus() {
         if (!isEnabled()) {
             return EUICC_OTA_STATUS_UNAVAILABLE;
@@ -588,7 +599,11 @@
         }
     }
 
-    private static IEuiccController getIEuiccController() {
+    /**
+     * @hide
+     */
+    @TestApi
+    protected IEuiccController getIEuiccController() {
         return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
     }
 }
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/euicc/EuiccNotification.aidl
similarity index 81%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/euicc/EuiccNotification.aidl
index d750363..dad770d 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/euicc/EuiccNotification.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-/** @hide */
-package android.telephony.data;
+package android.telephony.euicc;
 
-parcelable InterfaceAddress;
+parcelable EuiccNotification;
diff --git a/telephony/java/android/telephony/euicc/EuiccNotification.java b/telephony/java/android/telephony/euicc/EuiccNotification.java
new file mode 100644
index 0000000..ef3c1ce
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccNotification.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.euicc;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * This represents a signed notification which is defined in SGP.22. It can be either a profile
+ * installation result or a notification generated for profile operations (e.g., enabling,
+ * disabling, or deleting).
+ *
+ * @hide
+ *
+ * TODO(b/35851809): Make this a @SystemApi.
+ */
+public class EuiccNotification implements Parcelable {
+    /** Event */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "EVENT_" }, value = {
+            EVENT_INSTALL,
+            EVENT_ENABLE,
+            EVENT_DISABLE,
+            EVENT_DELETE
+    })
+    public @interface Event {}
+
+    /** A profile is downloaded and installed. */
+    public static final int EVENT_INSTALL = 1;
+
+    /** A profile is enabled. */
+    public static final int EVENT_ENABLE = 1 << 1;
+
+    /** A profile is disabled. */
+    public static final int EVENT_DISABLE = 1 << 2;
+
+    /** A profile is deleted. */
+    public static final int EVENT_DELETE = 1 << 3;
+
+    /** Value of the bits of all above events */
+    @Event
+    public static final int ALL_EVENTS =
+            EVENT_INSTALL | EVENT_ENABLE | EVENT_DISABLE | EVENT_DELETE;
+
+    private final int mSeq;
+    private final String mTargetAddr;
+    @Event private final int mEvent;
+    @Nullable private final byte[] mData;
+
+    /**
+     * Creates an instance.
+     *
+     * @param seq The sequence number of this notification.
+     * @param targetAddr The target server where to send this notification.
+     * @param event The event which causes this notification.
+     * @param data The data which needs to be sent to the target server. This can be null for
+     *     building a list of notification metadata without data.
+     */
+    public EuiccNotification(int seq, String targetAddr, @Event int event, @Nullable byte[] data) {
+        mSeq = seq;
+        mTargetAddr = targetAddr;
+        mEvent = event;
+        mData = data;
+    }
+
+    /** @return The sequence number of this notification. */
+    public int getSeq() {
+        return mSeq;
+    }
+
+    /** @return The target server address where this notification should be sent to. */
+    public String getTargetAddr() {
+        return mTargetAddr;
+    }
+
+    /** @return The event of this notification. */
+    @Event
+    public int getEvent() {
+        return mEvent;
+    }
+
+    /** @return The notification data which needs to be sent to the target server. */
+    @Nullable
+    public byte[] getData() {
+        return mData;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        EuiccNotification that = (EuiccNotification) obj;
+        return mSeq == that.mSeq
+                && Objects.equals(mTargetAddr, that.mTargetAddr)
+                && mEvent == that.mEvent
+                && Arrays.equals(mData, that.mData);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        result = 31 * result + mSeq;
+        result = 31 * result + Objects.hashCode(mTargetAddr);
+        result = 31 * result + mEvent;
+        result = 31 * result + Arrays.hashCode(mData);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "EuiccNotification (seq="
+                + mSeq
+                + ", targetAddr="
+                + mTargetAddr
+                + ", event="
+                + mEvent
+                + ", data="
+                + (mData == null ? "null" : "byte[" + mData.length + "]")
+                + ")";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSeq);
+        dest.writeString(mTargetAddr);
+        dest.writeInt(mEvent);
+        dest.writeByteArray(mData);
+    }
+
+    private EuiccNotification(Parcel source) {
+        mSeq = source.readInt();
+        mTargetAddr = source.readString();
+        mEvent = source.readInt();
+        mData = source.createByteArray();
+    }
+
+    public static final Creator<EuiccNotification> CREATOR =
+            new Creator<EuiccNotification>() {
+                @Override
+                public EuiccNotification createFromParcel(Parcel source) {
+                    return new EuiccNotification(source);
+                }
+
+                @Override
+                public EuiccNotification[] newArray(int size) {
+                    return new EuiccNotification[size];
+                }
+            };
+}
diff --git a/core/java/android/os/Seccomp.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
similarity index 83%
rename from core/java/android/os/Seccomp.java
rename to telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
index f14e93f..9785a45 100644
--- a/core/java/android/os/Seccomp.java
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.telephony.euicc;
 
-/**
- * @hide
- */
-public final class Seccomp {
-    public static final native void setPolicy();
-}
+parcelable EuiccRulesAuthTable;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
new file mode 100644
index 0000000..7efe043
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.euicc;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.carrier.CarrierIdentifier;
+import android.service.euicc.EuiccProfileInfo;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * This represents the RAT (Rules Authorisation Table) stored on eUICC.
+ *
+ * @hide
+ *
+ * TODO(b/35851809): Make this a @SystemApi.
+ */
+public final class EuiccRulesAuthTable implements Parcelable {
+    /** Profile policy rule flags */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = {
+            POLICY_RULE_FLAG_CONSENT_REQUIRED
+    })
+    public @interface PolicyRuleFlag {}
+
+    /** User consent is required to install the profile. */
+    public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1;
+
+    private final int[] mPolicyRules;
+    private final CarrierIdentifier[][] mCarrierIds;
+    private final int[] mPolicyRuleFlags;
+
+    /** This is used to build new {@link EuiccRulesAuthTable} instance. */
+    public static final class Builder {
+        private int[] mPolicyRules;
+        private CarrierIdentifier[][] mCarrierIds;
+        private int[] mPolicyRuleFlags;
+        private int mPosition;
+
+        /**
+         * Creates a new builder.
+         *
+         * @param ruleNum The number of authorisation rules in the table.
+         */
+        public Builder(int ruleNum) {
+            mPolicyRules = new int[ruleNum];
+            mCarrierIds = new CarrierIdentifier[ruleNum][];
+            mPolicyRuleFlags = new int[ruleNum];
+        }
+
+        /**
+         * Builds the RAT instance. This builder should not be used anymore after this method is
+         * called, otherwise {@link NullPointerException} will be thrown.
+         */
+        public EuiccRulesAuthTable build() {
+            if (mPosition != mPolicyRules.length) {
+                throw new IllegalStateException(
+                        "Not enough rules are added, expected: "
+                                + mPolicyRules.length
+                                + ", added: "
+                                + mPosition);
+            }
+            return new EuiccRulesAuthTable(mPolicyRules, mCarrierIds, mPolicyRuleFlags);
+        }
+
+        /**
+         * Adds an authorisation rule.
+         *
+         * @throws ArrayIndexOutOfBoundsException If the {@code mPosition} is larger than the size
+         *     this table.
+         */
+        public Builder add(int policyRules, CarrierIdentifier[] carrierId, int policyRuleFlags) {
+            if (mPosition >= mPolicyRules.length) {
+                throw new ArrayIndexOutOfBoundsException(mPosition);
+            }
+            mPolicyRules[mPosition] = policyRules;
+            mCarrierIds[mPosition] = carrierId;
+            mPolicyRuleFlags[mPosition] = policyRuleFlags;
+            mPosition++;
+            return this;
+        }
+    }
+
+    /**
+     * @param mccRule A 2-character or 3-character string which can be either MCC or MNC. The
+     *     character 'E' is used as a wild char to match any digit.
+     * @param mcc A 2-character or 3-character string which can be either MCC or MNC.
+     * @return Whether the {@code mccRule} matches {@code mcc}.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static boolean match(String mccRule, String mcc) {
+        if (mccRule.length() < mcc.length()) {
+            return false;
+        }
+        for (int i = 0; i < mccRule.length(); i++) {
+            // 'E' is the wild char to match any digit.
+            if (mccRule.charAt(i) == 'E'
+                    || (i < mcc.length() && mccRule.charAt(i) == mcc.charAt(i))) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    private EuiccRulesAuthTable(int[] policyRules, CarrierIdentifier[][] carrierIds,
+            int[] policyRuleFlags) {
+        mPolicyRules = policyRules;
+        mCarrierIds = carrierIds;
+        mPolicyRuleFlags = policyRuleFlags;
+    }
+
+    /**
+     * Finds the index of the first authorisation rule matching the given policy and carrier id. If
+     * the returned index is not negative, the carrier is allowed to apply this policy to its
+     * profile.
+     *
+     * @param policy The policy rule.
+     * @param carrierId The carrier id.
+     * @return The index of authorization rule. If no rule is found, -1 will be returned.
+     */
+    public int findIndex(@EuiccProfileInfo.PolicyRule int policy, CarrierIdentifier carrierId) {
+        for (int i = 0; i < mPolicyRules.length; i++) {
+            if ((mPolicyRules[i] & policy) == 0) {
+                continue;
+            }
+            CarrierIdentifier[] carrierIds = mCarrierIds[i];
+            if (carrierIds == null || carrierIds.length == 0) {
+                continue;
+            }
+            for (int j = 0; j < carrierIds.length; j++) {
+                CarrierIdentifier ruleCarrierId = carrierIds[j];
+                if (!match(ruleCarrierId.getMcc(), carrierId.getMcc())
+                        || !match(ruleCarrierId.getMnc(), carrierId.getMnc())) {
+                    continue;
+                }
+                String gid = ruleCarrierId.getGid1();
+                if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid1())) {
+                    continue;
+                }
+                gid = ruleCarrierId.getGid2();
+                if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid2())) {
+                    continue;
+                }
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Tests if the entry in the table has the given policy rule flag.
+     *
+     * @param index The index of the entry.
+     * @param flag The policy rule flag to be tested.
+     * @throws ArrayIndexOutOfBoundsException If the {@code index} is negative or larger than the
+     *     size of this table.
+     */
+    public boolean hasPolicyRuleFlag(int index, @PolicyRuleFlag int flag) {
+        if (index < 0 || index >= mPolicyRules.length) {
+            throw new ArrayIndexOutOfBoundsException(index);
+        }
+        return (mPolicyRuleFlags[index] & flag) != 0;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeIntArray(mPolicyRules);
+        for (CarrierIdentifier[] ids : mCarrierIds) {
+            dest.writeTypedArray(ids, flags);
+        }
+        dest.writeIntArray(mPolicyRuleFlags);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        EuiccRulesAuthTable that = (EuiccRulesAuthTable) obj;
+        if (mCarrierIds.length != that.mCarrierIds.length) {
+            return false;
+        }
+        for (int i = 0; i < mCarrierIds.length; i++) {
+            CarrierIdentifier[] carrierIds = mCarrierIds[i];
+            CarrierIdentifier[] thatCarrierIds = that.mCarrierIds[i];
+            if (carrierIds != null && thatCarrierIds != null) {
+                if (carrierIds.length != thatCarrierIds.length) {
+                    return false;
+                }
+                for (int j = 0; j < carrierIds.length; j++) {
+                    if (!carrierIds[j].equals(thatCarrierIds[j])) {
+                        return false;
+                    }
+                }
+                continue;
+            } else if (carrierIds == null && thatCarrierIds == null) {
+                continue;
+            }
+            return false;
+        }
+
+        return Arrays.equals(mPolicyRules, that.mPolicyRules)
+                && Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags);
+    }
+
+    private EuiccRulesAuthTable(Parcel source) {
+        mPolicyRules = source.createIntArray();
+        int len = mPolicyRules.length;
+        mCarrierIds = new CarrierIdentifier[len][];
+        for (int i = 0; i < len; i++) {
+            mCarrierIds[i] = source.createTypedArray(CarrierIdentifier.CREATOR);
+        }
+        mPolicyRuleFlags = source.createIntArray();
+    }
+
+    public static final Creator<EuiccRulesAuthTable> CREATOR =
+            new Creator<EuiccRulesAuthTable>() {
+                @Override
+                public EuiccRulesAuthTable createFromParcel(Parcel source) {
+                    return new EuiccRulesAuthTable(source);
+                }
+
+                @Override
+                public EuiccRulesAuthTable[] newArray(int size) {
+                    return new EuiccRulesAuthTable[size];
+                }
+            };
+}
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 8230eaf..aaa0f08 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -26,12 +26,14 @@
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MMTelFeature;
 import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
 import com.android.ims.internal.IImsMMTelFeature;
 import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsServiceController;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -113,6 +115,12 @@
                 throws RemoteException {
             ImsService.this.removeImsFeature(slotId, featureType, c);
         }
+
+        @Override
+        public IImsRegistration getRegistration(int slotId) throws RemoteException {
+            ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+            return r != null ? r.getBinder() : null;
+        }
     };
 
     /**
@@ -174,6 +182,8 @@
         f.setSlotId(slotId);
         f.addImsFeatureStatusCallback(c);
         addImsFeature(slotId, featureType, f);
+        // TODO: Remove once new onFeatureReady AIDL is merged in.
+        f.onFeatureReady();
     }
 
     private void addImsFeature(int slotId, int featureType, ImsFeature f) {
@@ -236,4 +246,13 @@
     public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
         return null;
     }
+
+    /**
+     * @param slotId The slot that is associated with the IMS Registration.
+     * @return the ImsRegistration implementation associated with the slot.
+     * @hide
+     */
+    public ImsRegistrationImplBase getRegistration(int slotId) {
+        return new ImsRegistrationImplBase();
+    }
 }
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index ca4a210..d47cea30 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -96,7 +96,7 @@
             new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
     private @ImsState int mState = STATE_NOT_AVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-    private Context mContext;
+    protected Context mContext;
 
     public void setContext(Context context) {
         mContext = context;
diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java
index b7c8ca0..afaf332 100644
--- a/telephony/java/android/telephony/ims/internal/ImsService.java
+++ b/telephony/java/android/telephony/ims/internal/ImsService.java
@@ -24,7 +24,6 @@
 import android.telephony.ims.internal.aidl.IImsConfig;
 import android.telephony.ims.internal.aidl.IImsMmTelFeature;
 import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsRegistration;
 import android.telephony.ims.internal.aidl.IImsServiceController;
 import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
 import android.telephony.ims.internal.feature.ImsFeature;
@@ -32,11 +31,12 @@
 import android.telephony.ims.internal.feature.RcsFeature;
 import android.telephony.ims.internal.stub.ImsConfigImplBase;
 import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsRegistration;
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
index 8332bc0..43f5098 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
@@ -24,4 +24,5 @@
  */
 oneway interface IImsMmTelListener {
     void onIncomingCall(IImsCallSession c);
+    void onVoiceMessageCountUpdate(int count);
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
index 8afb955..82a8525 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
@@ -18,12 +18,12 @@
 
 import android.telephony.ims.internal.aidl.IImsMmTelFeature;
 import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsRegistration;
 import android.telephony.ims.internal.aidl.IImsConfig;
 import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
 import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsRegistration;
 
 /**
  * See ImsService and MmTelFeature for more information.
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
index 4d18873..5dbf077 100644
--- a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
@@ -18,7 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.ArraySet;
 
 import java.util.ArrayList;
diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
index 2f350c8..057c9a86 100644
--- a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
@@ -28,8 +28,8 @@
 import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
 import android.telephony.ims.internal.aidl.IImsMmTelFeature;
 import android.telephony.ims.internal.aidl.IImsMmTelListener;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
 import android.telephony.ims.internal.aidl.IImsSmsListener;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.ImsEcbmImplBase;
 import android.telephony.ims.stub.ImsMultiEndpointImplBase;
 import android.telephony.ims.stub.ImsUtImplBase;
@@ -261,6 +261,15 @@
         }
 
         /**
+         * Updates the Listener when the voice message count for IMS has changed.
+         * @param count an integer representing the new message count.
+         */
+        @Override
+        public void onVoiceMessageCountUpdate(int count) {
+
+        }
+
+        /**
          * Called when the IMS provider receives an incoming call.
          * @param c The {@link ImsCallSession} associated with the new call.
          */
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
similarity index 83%
rename from telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
rename to telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 558b009..42af083 100644
--- a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -14,16 +14,19 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.net.Uri;
+import android.os.IBinder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsRegistration;
-import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
 import android.util.Log;
 
 import com.android.ims.ImsReasonInfo;
+import com.android.ims.internal.IImsRegistration;
+import com.android.ims.internal.IImsRegistrationCallback;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -62,23 +65,25 @@
 
     // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
     // state.
+    // The unknown state is set as the initialization state. This is so that we do not call back
+    // with NOT_REGISTERED in the case where the ImsService has not updated the registration state
+    // yet.
+    private static final int REGISTRATION_STATE_UNKNOWN = -1;
     private static final int REGISTRATION_STATE_NOT_REGISTERED = 0;
     private static final int REGISTRATION_STATE_REGISTERING = 1;
     private static final int REGISTRATION_STATE_REGISTERED = 2;
 
-
     /**
      * Callback class for receiving Registration callback events.
+     * @hide
      */
-    public static class Callback extends IImsRegistrationCallback.Stub {
-
+    public static class Callback {
         /**
          * Notifies the framework when the IMS Provider is connected to the IMS network.
          *
          * @param imsRadioTech the radio access technology. Valid values are defined in
          * {@link ImsRegistrationTech}.
          */
-        @Override
         public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
         }
 
@@ -88,7 +93,6 @@
          * @param imsRadioTech the radio access technology. Valid values are defined in
          * {@link ImsRegistrationTech}.
          */
-        @Override
         public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
         }
 
@@ -97,7 +101,6 @@
          *
          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
          */
-        @Override
         public void onDeregistered(ImsReasonInfo info) {
         }
 
@@ -108,10 +111,19 @@
          * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
          * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
          */
-        @Override
         public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
                 ImsReasonInfo info) {
         }
+
+        /**
+         * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+         * it changes.
+         * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+         *         subscription.
+         */
+        public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+
+        }
     }
 
     private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@@ -139,9 +151,9 @@
     private @ImsRegistrationTech
     int mConnectionType = REGISTRATION_TECH_NONE;
     // Locked on mLock
-    private int mRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
-    // Locked on mLock
-    private ImsReasonInfo mLastDisconnectCause;
+    private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
+    // Locked on mLock, create unspecified disconnect cause.
+    private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
 
     public final IImsRegistration getBinder() {
         return mBinder;
@@ -221,6 +233,17 @@
         });
     }
 
+    public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
+        mCallbacks.broadcast((c) -> {
+            try {
+                c.onSubscriberAssociatedUriChanged(uris);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " +
+                        "callback.");
+            }
+        });
+    }
+
     private void updateToState(@ImsRegistrationTech int connType, int newState) {
         synchronized (mLock) {
             mConnectionType = connType;
@@ -241,7 +264,8 @@
         }
     }
 
-    private @ImsRegistrationTech int getConnectionType() {
+    @VisibleForTesting
+    public final @ImsRegistrationTech int getConnectionType() {
         synchronized (mLock) {
             return mConnectionType;
         }
@@ -271,6 +295,10 @@
                 c.onRegistered(getConnectionType());
                 break;
             }
+            case REGISTRATION_STATE_UNKNOWN: {
+                // Do not callback if the state has not been updated yet by the ImsService.
+                break;
+            }
         }
     }
 }
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 489c208..693aaff 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -351,7 +351,7 @@
         mServiceType = in.readInt();
         mCallType = in.readInt();
         mCallExtras = in.readBundle();
-        mMediaProfile = in.readParcelable(null);
+        mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
     }
 
     public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 4f6f68c..83d9bd9 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -384,6 +384,13 @@
     /** Call/IMS registration is failed/dropped because of a network detach */
     public static final int CODE_NETWORK_DETACH = 1513;
 
+    /**
+     * Call failed due to SIP code 380 (Alternative Service response) while dialing an "undetected
+     * emergency number".  This scenario is important in some regions where the carrier network will
+     * identify other non-emergency help numbers (e.g. mountain rescue) when attempting to dial.
+     */
+    public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514;
+
     /* OEM specific error codes. To be used by OEMs when they don't want to
    reveal error code which would be replaced by ERROR_UNSPECIFIED */
     public static final int CODE_OEM_CAUSE_1 = 0xf001;
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl b/telephony/java/com/android/ims/internal/IImsRegistration.aidl
similarity index 82%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
rename to telephony/java/com/android/ims/internal/IImsRegistration.aidl
index 687b7ca..6de264e 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistration.aidl
@@ -15,10 +15,9 @@
  */
 
 
-package android.telephony.ims.internal.aidl;
+package com.android.ims.internal;
 
-import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import com.android.ims.internal.IImsRegistrationCallback;
 
 /**
  * See ImsRegistration for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
similarity index 89%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
rename to telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
index a50575b..5f21167 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
@@ -15,8 +15,9 @@
  */
 
 
-package android.telephony.ims.internal.aidl;
+package com.android.ims.internal;
 
+import android.net.Uri;
 import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
 
 import com.android.ims.ImsReasonInfo;
@@ -31,4 +32,5 @@
    void onRegistering(int imsRadioTech);
    void onDeregistered(in ImsReasonInfo info);
    void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
+   void onSubscriberAssociatedUriChanged(in Uri[] uris);
 }
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index 857089f..7ac25ac 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -18,6 +18,7 @@
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
 import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsRcsFeature;
 
 /**
@@ -29,4 +30,5 @@
     IImsMMTelFeature createMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
     IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
     void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
+    IImsRegistration getRegistration(int slotId);
 }
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index ac16139..8e3f4c0 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -46,5 +46,6 @@
     void onVoiceActivationStateChanged(int activationState);
     void onDataActivationStateChanged(int activationState);
     void onCarrierNetworkChange(in boolean active);
+    void onUserMobileDataStateChanged(in boolean enabled);
 }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b0af9a8..dfb3c34 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -40,12 +40,14 @@
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import com.android.ims.internal.IImsMMTelFeature;
 import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.OperatorInfo;
 
 import java.util.List;
 
+import android.telephony.UiccSlotInfo;
 
 /**
  * Interface used to interact with the phone.  Mostly this is used by the
@@ -808,6 +810,11 @@
     IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
 
     /**
+    * Returns the IImsRegistration associated with the slot and feature specified.
+    */
+    IImsRegistration getImsRegistration(int slotId, int feature);
+
+    /**
      * Set the network selection mode to automatic.
      *
      * @param subId the id of the subscription to update.
@@ -1317,6 +1324,34 @@
      */
     List<CarrierIdentifier> getAllowedCarriers(int slotIndex);
 
+   /**
+     * Returns carrier id of the given subscription.
+     * <p>To recognize carrier as a first class identity, assign each carrier with a canonical
+     * integer a.k.a carrier id.
+     *
+     * @param subId The subscription id
+     * @return Carrier id of given subscription id. return {@link #UNKNOWN_CARRIER_ID} if
+     * subscription is unavailable or carrier cannot be identified.
+     * @throws IllegalStateException if telephony service is unavailable.
+     * @hide
+     */
+    int getSubscriptionCarrierId(int subId);
+
+    /**
+     * Returns carrier name of the given subscription.
+     * <p>Carrier name is a user-facing name of carrier id {@link #getSubscriptionCarrierId(int)},
+     * usually the brand name of the subsidiary (e.g. T-Mobile). Each carrier could configure
+     * multiple {@link #getSimOperatorName() SPN} but should have a single carrier name.
+     * Carrier name is not canonical identity, use {@link #getSubscriptionCarrierId(int)} instead.
+     * <p>Returned carrier name is unlocalized.
+     *
+     * @return Carrier name of given subscription id. return {@code null} if subscription is
+     * unavailable or carrier cannot be identified.
+     * @throws IllegalStateException if telephony service is unavailable.
+     * @hide
+     */
+    String getSubscriptionCarrierName(int subId);
+
     /**
      * Action set from carrier signalling broadcast receivers to enable/disable metered apns
      * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
@@ -1410,4 +1445,19 @@
      * @hide
      */
     SignalStrength getSignalStrength(int subId);
+
+    /**
+     * Get slot info for all the UICC slots.
+     * @return UiccSlotInfo array.
+     * @hide
+     */
+    UiccSlotInfo[] getUiccSlotsInfo();
+
+    /**
+     * Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive.
+     * @param physicalSlots Index i in the array representing physical slot for phone i. The array
+     *        size should be same as getPhoneCount().
+     * @return boolean Return true if the switch succeeds, false if the switch fails.
+     */
+    boolean switchSlots(in int[] physicalSlots);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 75d8f3f..188167c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -69,4 +69,5 @@
             int activationState, int activationType);
     void notifySubscriptionInfoChanged();
     void notifyCarrierNetworkChange(in boolean active);
+    void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
 }
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index f3d9335..d57f9af 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -30,16 +30,14 @@
     public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
     /* ABSENT means ICC is missing */
     public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
+    /* PRESENT means ICC is present */
+    public static final String INTENT_VALUE_ICC_PRESENT = "PRESENT";
     /* CARD_IO_ERROR means for three consecutive times there was SIM IO error */
     static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR";
     /* CARD_RESTRICTED means card is present but not usable due to carrier restrictions */
     static public final String INTENT_VALUE_ICC_CARD_RESTRICTED = "CARD_RESTRICTED";
     /* LOCKED means ICC is locked by pin or by network */
     public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
-    //TODO: we can remove this state in the future if Bug 18489776 analysis
-    //#42's first race condition is resolved
-    /* INTERNAL LOCKED means ICC is locked by pin or by network */
-    public static final String INTENT_VALUE_ICC_INTERNAL_LOCKED = "INTERNAL_LOCKED";
     /* READY means ICC is ready to access */
     public static final String INTENT_VALUE_ICC_READY = "READY";
     /* IMSI means ICC IMSI is ready in property */
@@ -77,7 +75,8 @@
         NOT_READY,      /** ordinal(6) == {@See TelephonyManager#SIM_STATE_NOT_READY} */
         PERM_DISABLED,  /** ordinal(7) == {@See TelephonyManager#SIM_STATE_PERM_DISABLED} */
         CARD_IO_ERROR,  /** ordinal(8) == {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} */
-        CARD_RESTRICTED;/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */
+        CARD_RESTRICTED,/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */
+        LOADED;         /** ordinal(9) == {@See TelephonyManager#SIM_STATE_LOADED} */
 
         public boolean isPinLocked() {
             return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
@@ -85,9 +84,9 @@
 
         public boolean iccCardExist() {
             return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)
-                    || (this == NETWORK_LOCKED) || (this == READY)
+                    || (this == NETWORK_LOCKED) || (this == READY) || (this == NOT_READY)
                     || (this == PERM_DISABLED) || (this == CARD_IO_ERROR)
-                    || (this == CARD_RESTRICTED));
+                    || (this == CARD_RESTRICTED) || (this == LOADED));
         }
 
         public static State intToState(int state) throws IllegalArgumentException {
@@ -102,6 +101,7 @@
                 case 7: return PERM_DISABLED;
                 case 8: return CARD_IO_ERROR;
                 case 9: return CARD_RESTRICTED;
+                case 10: return LOADED;
                 default:
                     throw new IllegalArgumentException();
             }
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
index d750363..8a77bf1 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IAuthenticateServerCallback {
+    void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
index d750363..f6b99e2 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ICancelSessionCallback {
+    void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl
similarity index 75%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl
index d750363..23a642e 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IDeleteProfileCallback {
+    void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl
similarity index 71%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl
index d750363..3ee0b3a 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IDisableProfileCallback {
+    void onComplete(int resultCode);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
index 2846a1a..e33f44c 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -17,8 +17,73 @@
 package com.android.internal.telephony.euicc;
 
 import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+import com.android.internal.telephony.euicc.IGetProfileCallback;
+import com.android.internal.telephony.euicc.IDisableProfileCallback;
+import com.android.internal.telephony.euicc.ISwitchToProfileCallback;
+import com.android.internal.telephony.euicc.ISetNicknameCallback;
+import com.android.internal.telephony.euicc.IDeleteProfileCallback;
+import com.android.internal.telephony.euicc.IResetMemoryCallback;
+import com.android.internal.telephony.euicc.IGetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.IGetSmdsAddressCallback;
+import com.android.internal.telephony.euicc.ISetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
+import com.android.internal.telephony.euicc.ICancelSessionCallback;
+import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback;
+import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback;
+import com.android.internal.telephony.euicc.IListNotificationsCallback;
+import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback;
+import com.android.internal.telephony.euicc.IPrepareDownloadCallback;
+import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback;
 
 /** @hide */
 interface IEuiccCardController {
-    oneway void getAllProfiles(String callingPackage, in IGetAllProfilesCallback callback);
+    oneway void getAllProfiles(String callingPackage, String cardId,
+        in IGetAllProfilesCallback callback);
+    oneway void getProfile(String callingPackage, String cardId, String iccid,
+        in IGetProfileCallback callback);
+    oneway void disableProfile(String callingPackage, String cardId, String iccid, boolean refresh,
+        in IDisableProfileCallback callback);
+    oneway void switchToProfile(String callingPackage, String cardId, String iccid, boolean refresh,
+        in ISwitchToProfileCallback callback);
+    oneway void setNickname(String callingPackage, String cardId, String iccid, String nickname,
+        in ISetNicknameCallback callback);
+    oneway void deleteProfile(String callingPackage, String cardId, String iccid,
+        in IDeleteProfileCallback callback);
+    oneway void resetMemory(String callingPackage, String cardId, int options, in IResetMemoryCallback callback);
+    oneway void getDefaultSmdpAddress(String callingPackage, String cardId,
+        in IGetDefaultSmdpAddressCallback callback);
+    oneway void getSmdsAddress(String callingPackage, String cardId,
+        in IGetSmdsAddressCallback callback);
+    oneway void setDefaultSmdpAddress(String callingPackage, String cardId, String address,
+        in ISetDefaultSmdpAddressCallback callback);
+    oneway void getRulesAuthTable(String callingPackage, String cardId,
+        in IGetRulesAuthTableCallback callback);
+    oneway void getEuiccChallenge(String callingPackage, String cardId,
+        in IGetEuiccChallengeCallback callback);
+    oneway void getEuiccInfo1(String callingPackage, String cardId,
+        in IGetEuiccInfo1Callback callback);
+    oneway void getEuiccInfo2(String callingPackage, String cardId,
+        in IGetEuiccInfo2Callback callback);
+    oneway void authenticateServer(String callingPackage, String cardId, String matchingId,
+        in byte[] serverSigned1, in byte[] serverSignature1, in byte[] euiccCiPkIdToBeUsed,
+        in byte[] serverCertificatein, in IAuthenticateServerCallback callback);
+    oneway void prepareDownload(String callingPackage, String cardId, in byte[] hashCc,
+        in byte[] smdpSigned2, in byte[] smdpSignature2, in byte[] smdpCertificate,
+        in IPrepareDownloadCallback callback);
+    oneway void loadBoundProfilePackage(String callingPackage, String cardId,
+        in byte[] boundProfilePackage, in ILoadBoundProfilePackageCallback callback);
+    oneway void cancelSession(String callingPackage, String cardId, in byte[] transactionId,
+        int reason, in ICancelSessionCallback callback);
+    oneway void listNotifications(String callingPackage, String cardId, int events,
+        in IListNotificationsCallback callback);
+    oneway void retrieveNotificationList(String callingPackage, String cardId, int events,
+        in IRetrieveNotificationListCallback callback);
+    oneway void retrieveNotification(String callingPackage, String cardId, int seqNumber,
+        in IRetrieveNotificationCallback callback);
+    oneway void removeNotificationFromList(String callingPackage, String cardId, int seqNumber,
+            in IRemoveNotificationFromListCallback callback);
 }
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl
index d750363..79b659d 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetDefaultSmdpAddressCallback {
+    void onComplete(int resultCode, String address);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
index d750363..5ffb340 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetEuiccChallengeCallback {
+    void onComplete(int resultCode, in byte[] challenge);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
similarity index 74%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
index d750363..9592acb 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetEuiccInfo1Callback {
+    void onComplete(int resultCode, in byte[] info);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
similarity index 74%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
index d750363..5256b35 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetEuiccInfo2Callback {
+    void onComplete(int resultCode, in byte[] info);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl
similarity index 69%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl
index d750363..e9fc9e9 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetProfileCallback {
+    void onComplete(int resultCode, in EuiccProfileInfo profile);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
similarity index 68%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
index d750363..58f0bde 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccRulesAuthTable;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetRulesAuthTableCallback {
+    void onComplete(int resultCode, in EuiccRulesAuthTable rat);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl
index d750363..09a83aa 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetSmdsAddressCallback {
+    void onComplete(int resultCode, String address);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
similarity index 67%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
index d750363..65aa302 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IListNotificationsCallback {
+    void onComplete(int resultCode, in EuiccNotification[] notifications);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
similarity index 72%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
index d750363..4ad7081 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ILoadBoundProfilePackageCallback {
+    void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
index d750363..c035184 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IPrepareDownloadCallback {
+    void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
similarity index 69%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
index d750363..b22d0da 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IRemoveNotificationFromListCallback {
+    void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl
similarity index 75%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl
index d750363..860c158 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IResetMemoryCallback {
+    void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
similarity index 67%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
index d750363..dd8889a9 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IRetrieveNotificationCallback {
+    void onComplete(int resultCode, in EuiccNotification notification);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
similarity index 66%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
index d750363..bc4e451 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IRetrieveNotificationListCallback {
+    void onComplete(int resultCode, in EuiccNotification[] notifications);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl
similarity index 74%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl
index d750363..1e47125 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ISetDefaultSmdpAddressCallback {
+    void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl
similarity index 75%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl
index d750363..5899980 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ISetNicknameCallback {
+    void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl
similarity index 68%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl
index d750363..21ff084 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
 
 /** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ISwitchToProfileCallback {
+    void onComplete(int resultCode, in EuiccProfileInfo profile);
+}
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 9f8b3a8..c095438 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -837,6 +837,13 @@
     }
 
     /**
+     * Strip all the trailing 'F' characters of a string, e.g., an ICCID.
+     */
+    public static String stripTrailingFs(String s) {
+        return s == null ? null : s.replaceAll("(?i)f*$", "");
+    }
+
+    /**
      * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a
      * hex number, 0 will be returned.
      */
diff --git a/legacy-test/Android.bp b/test-base/Android.bp
similarity index 66%
rename from legacy-test/Android.bp
rename to test-base/Android.bp
index 1173bc6..a42dc5a 100644
--- a/legacy-test/Android.bp
+++ b/test-base/Android.bp
@@ -14,6 +14,24 @@
 // limitations under the License.
 //
 
+// Build the android.test.base library
+// ===================================
+// This contains the junit.framework and android.test classes that were in
+// Android API level 25 excluding those from android.test.runner.
+// Also contains the com.android.internal.util.Predicate[s] classes.
+java_library {
+    name: "android.test.base",
+
+    srcs: ["src/**/*.java"],
+
+    no_framework_libs: true,
+    hostdex: true,
+    libs: [
+        "framework",
+    ],
+
+}
+
 // Build the legacy-test library
 // =============================
 // This contains the junit.framework and android.test classes that were in
@@ -21,23 +39,27 @@
 // Also contains the com.android.internal.util.Predicate[s] classes.
 java_library {
     name: "legacy-test",
-
-    srcs: ["src/**/*.java"],
+    static_libs: ["android.test.base"],
 
     no_framework_libs: true,
     libs: [
         "framework",
     ],
-
 }
 
-// Build the repackaged-legacy-test library
-// ========================================
-// This contains repackaged versions of the classes from legacy-test.
+// Build the repackaged.android.test.base library
+// ==============================================
+// This contains repackaged versions of the classes from
+// android.test.base.
 java_library_static {
-    name: "repackaged-legacy-test",
+    name: "repackaged.android.test.base",
 
-    static_libs: ["legacy-test"],
+    static_libs: ["android.test.base"],
+
+    no_framework_libs: true,
+    libs: [
+        "framework",
+    ],
 
     jarjar_rules: "jarjar-rules.txt",
 }
@@ -56,7 +78,7 @@
     ],
 
     static_libs: [
-        "android.test.runner",
+        "android.test.runner-minus-junit",
         "android.test.mock",
     ],
 
diff --git a/test-base/Android.mk b/test-base/Android.mk
new file mode 100644
index 0000000..25c3d76
--- /dev/null
+++ b/test-base/Android.mk
@@ -0,0 +1,120 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# Generate the stub source files for legacy.test.stubs
+# ====================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := \
+    core-oj \
+    core-libart \
+    framework \
+
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src
+
+ANDROID_TEST_BASE_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.base.stubs_intermediates/api.txt
+ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.base.stubs_intermediates/removed.txt
+
+ANDROID_TEST_BASE_API_FILE := $(LOCAL_PATH)/api/android-test-base-current.txt
+ANDROID_TEST_BASE_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-base-removed.txt
+
+LOCAL_DROIDDOC_OPTIONS:= \
+    -stubpackages android.test:android.test.suitebuilder.annotation:com.android.internal.util:junit.framework \
+    -stubsourceonly \
+    -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.base.stubs_intermediates/src \
+    -nodocs \
+    -api $(ANDROID_TEST_BASE_OUTPUT_API_FILE) \
+    -removedApi $(ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE) \
+
+LOCAL_UNINSTALLABLE_MODULE := true
+LOCAL_MODULE := android-test-base-api-stubs-gen
+
+include $(BUILD_DROIDDOC)
+
+# Remember the target that will trigger the code generation.
+android_test_base_gen_stamp := $(full_target)
+
+# Add some additional dependencies
+$(ANDROID_TEST_BASE_OUTPUT_API_FILE): $(full_target)
+$(ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE): $(full_target)
+
+# Build the android.test.base.stubs library
+# =========================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.test.base.stubs
+
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_SDK_VERSION := current
+
+# Make sure to run droiddoc first to generate the stub source files.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_base_gen_stamp)
+android_test_base_gen_stamp :=
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Archive a copy of the classes.jar in SDK build.
+$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.base.stubs.jar)
+
+# Check that the android.test.base.stubs library has not changed
+# ==============================================================
+
+# Check that the API we're building hasn't changed from the not-yet-released
+# SDK version.
+$(eval $(call check-api, \
+    check-android-test-base-api-current, \
+    $(ANDROID_TEST_BASE_API_FILE), \
+    $(ANDROID_TEST_BASE_OUTPUT_API_FILE), \
+    $(ANDROID_TEST_BASE_REMOVED_API_FILE), \
+    $(ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE), \
+    -error 2 -error 3 -error 4 -error 5 -error 6 \
+    -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
+    -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
+    -error 25 -error 26 -error 27, \
+    cat $(LOCAL_PATH)/api/apicheck_msg_android_test_base.txt, \
+    check-android-test-base-api, \
+    $(call doc-timestamp-for,android-test-base-api-stubs-gen) \
+    ))
+
+.PHONY: check-android-test-base-api
+checkapi: check-android-test-base-api
+
+.PHONY: update-android-test-base-api
+update-api: update-android-test-base-api
+
+update-android-test-base-api: $(ANDROID_TEST_BASE_OUTPUT_API_FILE) | $(ACP)
+	@echo Copying current.txt
+	$(hide) $(ACP) $(ANDROID_TEST_BASE_OUTPUT_API_FILE) $(ANDROID_TEST_BASE_API_FILE)
+	@echo Copying removed.txt
+	$(hide) $(ACP) $(ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_BASE_REMOVED_API_FILE)
+
+ifeq ($(HOST_OS),linux)
+# Build the legacy-performance-test-hostdex library
+# =================================================
+# This contains the android.test.PerformanceTestCase class only
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java
+LOCAL_MODULE := legacy-performance-test-hostdex
+
+include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY)
+endif  # HOST_OS == linux
diff --git a/legacy-test/api/legacy-test-current.txt b/test-base/api/android-test-base-current.txt
similarity index 100%
rename from legacy-test/api/legacy-test-current.txt
rename to test-base/api/android-test-base-current.txt
diff --git a/legacy-test/api/legacy-test-removed.txt b/test-base/api/android-test-base-removed.txt
similarity index 100%
rename from legacy-test/api/legacy-test-removed.txt
rename to test-base/api/android-test-base-removed.txt
diff --git a/test-base/api/apicheck_msg_android_test_base.txt b/test-base/api/apicheck_msg_android_test_base.txt
new file mode 100644
index 0000000..144aecc
--- /dev/null
+++ b/test-base/api/apicheck_msg_android_test_base.txt
@@ -0,0 +1,17 @@
+
+******************************
+You have tried to change the API from what has been previously approved.
+
+To make these errors go away, you have two choices:
+   1) You can add "@hide" javadoc comments to the methods, etc. listed in the
+      errors above.
+
+   2) You can update android-test-base-current.txt by executing the following command:
+         make update-android-test-base-api
+
+      To submit the revised android-test-base-current.txt to the main Android repository,
+      you will need approval.
+******************************
+
+
+
diff --git a/legacy-test/jarjar-rules.txt b/test-base/jarjar-rules.txt
similarity index 100%
rename from legacy-test/jarjar-rules.txt
rename to test-base/jarjar-rules.txt
diff --git a/legacy-test/src/android/test/AndroidTestCase.java b/test-base/src/android/test/AndroidTestCase.java
similarity index 100%
rename from legacy-test/src/android/test/AndroidTestCase.java
rename to test-base/src/android/test/AndroidTestCase.java
diff --git a/legacy-test/src/android/test/FlakyTest.java b/test-base/src/android/test/FlakyTest.java
similarity index 100%
rename from legacy-test/src/android/test/FlakyTest.java
rename to test-base/src/android/test/FlakyTest.java
diff --git a/legacy-test/src/android/test/InstrumentationTestCase.java b/test-base/src/android/test/InstrumentationTestCase.java
similarity index 100%
rename from legacy-test/src/android/test/InstrumentationTestCase.java
rename to test-base/src/android/test/InstrumentationTestCase.java
diff --git a/legacy-test/src/android/test/InstrumentationTestSuite.java b/test-base/src/android/test/InstrumentationTestSuite.java
similarity index 100%
rename from legacy-test/src/android/test/InstrumentationTestSuite.java
rename to test-base/src/android/test/InstrumentationTestSuite.java
diff --git a/legacy-test/src/android/test/PerformanceTestCase.java b/test-base/src/android/test/PerformanceTestCase.java
similarity index 100%
rename from legacy-test/src/android/test/PerformanceTestCase.java
rename to test-base/src/android/test/PerformanceTestCase.java
diff --git a/legacy-test/src/android/test/RepetitiveTest.java b/test-base/src/android/test/RepetitiveTest.java
similarity index 100%
rename from legacy-test/src/android/test/RepetitiveTest.java
rename to test-base/src/android/test/RepetitiveTest.java
diff --git a/legacy-test/src/android/test/UiThreadTest.java b/test-base/src/android/test/UiThreadTest.java
similarity index 100%
rename from legacy-test/src/android/test/UiThreadTest.java
rename to test-base/src/android/test/UiThreadTest.java
diff --git a/legacy-test/src/android/test/package.html b/test-base/src/android/test/package.html
similarity index 100%
rename from legacy-test/src/android/test/package.html
rename to test-base/src/android/test/package.html
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java b/test-base/src/android/test/suitebuilder/annotation/LargeTest.java
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java
rename to test-base/src/android/test/suitebuilder/annotation/LargeTest.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java b/test-base/src/android/test/suitebuilder/annotation/MediumTest.java
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java
rename to test-base/src/android/test/suitebuilder/annotation/MediumTest.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java b/test-base/src/android/test/suitebuilder/annotation/SmallTest.java
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java
rename to test-base/src/android/test/suitebuilder/annotation/SmallTest.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/Smoke.java b/test-base/src/android/test/suitebuilder/annotation/Smoke.java
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/Smoke.java
rename to test-base/src/android/test/suitebuilder/annotation/Smoke.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/Suppress.java b/test-base/src/android/test/suitebuilder/annotation/Suppress.java
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/Suppress.java
rename to test-base/src/android/test/suitebuilder/annotation/Suppress.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/package.html b/test-base/src/android/test/suitebuilder/annotation/package.html
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/package.html
rename to test-base/src/android/test/suitebuilder/annotation/package.html
diff --git a/legacy-test/src/com/android/internal/util/Predicate.java b/test-base/src/com/android/internal/util/Predicate.java
similarity index 100%
rename from legacy-test/src/com/android/internal/util/Predicate.java
rename to test-base/src/com/android/internal/util/Predicate.java
diff --git a/legacy-test/src/junit/MODULE_LICENSE_CPL b/test-base/src/junit/MODULE_LICENSE_CPL
similarity index 100%
rename from legacy-test/src/junit/MODULE_LICENSE_CPL
rename to test-base/src/junit/MODULE_LICENSE_CPL
diff --git a/legacy-test/src/junit/README.android b/test-base/src/junit/README.android
similarity index 100%
rename from legacy-test/src/junit/README.android
rename to test-base/src/junit/README.android
diff --git a/legacy-test/src/junit/cpl-v10.html b/test-base/src/junit/cpl-v10.html
similarity index 100%
rename from legacy-test/src/junit/cpl-v10.html
rename to test-base/src/junit/cpl-v10.html
diff --git a/legacy-test/src/junit/framework/Assert.java b/test-base/src/junit/framework/Assert.java
similarity index 100%
rename from legacy-test/src/junit/framework/Assert.java
rename to test-base/src/junit/framework/Assert.java
diff --git a/legacy-test/src/junit/framework/AssertionFailedError.java b/test-base/src/junit/framework/AssertionFailedError.java
similarity index 100%
rename from legacy-test/src/junit/framework/AssertionFailedError.java
rename to test-base/src/junit/framework/AssertionFailedError.java
diff --git a/legacy-test/src/junit/framework/ComparisonCompactor.java b/test-base/src/junit/framework/ComparisonCompactor.java
similarity index 100%
rename from legacy-test/src/junit/framework/ComparisonCompactor.java
rename to test-base/src/junit/framework/ComparisonCompactor.java
diff --git a/legacy-test/src/junit/framework/ComparisonFailure.java b/test-base/src/junit/framework/ComparisonFailure.java
similarity index 100%
rename from legacy-test/src/junit/framework/ComparisonFailure.java
rename to test-base/src/junit/framework/ComparisonFailure.java
diff --git a/legacy-test/src/junit/framework/Protectable.java b/test-base/src/junit/framework/Protectable.java
similarity index 100%
rename from legacy-test/src/junit/framework/Protectable.java
rename to test-base/src/junit/framework/Protectable.java
diff --git a/legacy-test/src/junit/framework/Test.java b/test-base/src/junit/framework/Test.java
similarity index 100%
rename from legacy-test/src/junit/framework/Test.java
rename to test-base/src/junit/framework/Test.java
diff --git a/legacy-test/src/junit/framework/TestCase.java b/test-base/src/junit/framework/TestCase.java
similarity index 100%
rename from legacy-test/src/junit/framework/TestCase.java
rename to test-base/src/junit/framework/TestCase.java
diff --git a/legacy-test/src/junit/framework/TestFailure.java b/test-base/src/junit/framework/TestFailure.java
similarity index 100%
rename from legacy-test/src/junit/framework/TestFailure.java
rename to test-base/src/junit/framework/TestFailure.java
diff --git a/legacy-test/src/junit/framework/TestListener.java b/test-base/src/junit/framework/TestListener.java
similarity index 100%
rename from legacy-test/src/junit/framework/TestListener.java
rename to test-base/src/junit/framework/TestListener.java
diff --git a/legacy-test/src/junit/framework/TestResult.java b/test-base/src/junit/framework/TestResult.java
similarity index 100%
rename from legacy-test/src/junit/framework/TestResult.java
rename to test-base/src/junit/framework/TestResult.java
diff --git a/legacy-test/src/junit/framework/TestSuite.java b/test-base/src/junit/framework/TestSuite.java
similarity index 100%
rename from legacy-test/src/junit/framework/TestSuite.java
rename to test-base/src/junit/framework/TestSuite.java
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 8eddec4..b1ae40e 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -24,7 +24,6 @@
     no_framework_libs: true,
     libs: [
         "framework",
-        "legacy-test",
     ],
 }
 
diff --git a/test-mock/jarjar-rules.txt b/test-mock/jarjar-rules.txt
index b0e4bea..f6f7913 120000
--- a/test-mock/jarjar-rules.txt
+++ b/test-mock/jarjar-rules.txt
@@ -1 +1 @@
-../legacy-test/jarjar-rules.txt
\ No newline at end of file
+../test-base/jarjar-rules.txt
\ No newline at end of file
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index 104ae82..dfaeed5 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -24,11 +24,28 @@
     no_framework_libs: true,
     libs: [
         "framework",
-        "legacy-test",
+        "android.test.base",
         "android.test.mock",
     ],
 }
 
+// Build the android.test.runner-minus-junit library
+// =================================================
+// This is provided solely for use by the legacy-android-test module.
+java_library {
+    name: "android.test.runner-minus-junit",
+
+    srcs: ["src/android/**/*.java"],
+
+    no_framework_libs: true,
+    libs: [
+        "framework",
+        "android.test.base",
+        "android.test.mock",
+        "junit",
+    ],
+}
+
 // Build the repackaged.android.test.runner library
 // ================================================
 java_library_static {
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 6cf2d56..cdc7756 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -26,7 +26,7 @@
     core-oj \
     core-libart \
     framework \
-    legacy-test \
+    android.test.base \
     android.test.mock \
 
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
@@ -65,7 +65,7 @@
 LOCAL_MODULE := android.test.runner.stubs
 
 LOCAL_JAVA_LIBRARIES := \
-    legacy.test.stubs \
+    android.test.base.stubs \
     android.test.mock.stubs \
 
 LOCAL_SOURCE_FILES_ALL_GENERATED := true
diff --git a/test-runner/jarjar-rules.txt b/test-runner/jarjar-rules.txt
index b0e4bea..f6f7913 120000
--- a/test-runner/jarjar-rules.txt
+++ b/test-runner/jarjar-rules.txt
@@ -1 +1 @@
-../legacy-test/jarjar-rules.txt
\ No newline at end of file
+../test-base/jarjar-rules.txt
\ No newline at end of file
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 677585c..4907894 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -29,32 +29,35 @@
 LOCAL_CERTIFICATE := platform
 
 # These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \
+LOCAL_JNI_SHARED_LIBRARIES := \
+    android.hidl.token@1.0 \
     libbacktrace \
     libbase \
     libbinder \
     libc++ \
+    libcrypto \
     libcutils \
+    libdexfile \
+    libframeworksnettestsjni \
+    libhidl-gen-utils \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
     liblog \
     liblzma \
     libnativehelper \
     libnetdaidl \
-    libui \
-    libunwind \
-    libutils \
-    libvndksupport \
-    libcrypto \
-    libhidl-gen-utils \
-    libhidlbase \
-    libhidltransport \
     libpackagelistparser \
     libpcre2 \
     libselinux \
-    libtinyxml2 \
+    libui \
+    libunwind \
+    libutils \
     libvintf \
-    libhwbinder \
+    libvndksupport \
+    libtinyxml2 \
     libunwindstack \
-    android.hidl.token@1.0
+    libutilscallstack
 
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index efc01f2a..f6c5532 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -36,19 +36,16 @@
     public void testDefaults() throws Exception {
         IpSecConfig c = new IpSecConfig();
         assertEquals(IpSecTransform.MODE_TRANSPORT, c.getMode());
-        assertEquals("", c.getLocalAddress());
-        assertEquals("", c.getRemoteAddress());
+        assertEquals("", c.getSourceAddress());
+        assertEquals("", c.getDestinationAddress());
         assertNull(c.getNetwork());
         assertEquals(IpSecTransform.ENCAP_NONE, c.getEncapType());
         assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getEncapSocketResourceId());
         assertEquals(0, c.getEncapRemotePort());
         assertEquals(0, c.getNattKeepaliveInterval());
-        for (int direction :
-                new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}) {
-            assertNull(c.getEncryption(direction));
-            assertNull(c.getAuthentication(direction));
-            assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId(direction));
-        }
+        assertNull(c.getEncryption());
+        assertNull(c.getAuthentication());
+        assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
     }
 
     @Test
@@ -57,34 +54,21 @@
 
         IpSecConfig c = new IpSecConfig();
         c.setMode(IpSecTransform.MODE_TUNNEL);
-        c.setLocalAddress("0.0.0.0");
-        c.setRemoteAddress("1.2.3.4");
+        c.setSourceAddress("0.0.0.0");
+        c.setDestinationAddress("1.2.3.4");
         c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
         c.setEncapSocketResourceId(7);
         c.setEncapRemotePort(22);
         c.setNattKeepaliveInterval(42);
         c.setEncryption(
-                IpSecTransform.DIRECTION_OUT,
                 new IpSecAlgorithm(
                         IpSecAlgorithm.CRYPT_AES_CBC,
                         new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
         c.setAuthentication(
-                IpSecTransform.DIRECTION_OUT,
                 new IpSecAlgorithm(
                         IpSecAlgorithm.AUTH_HMAC_MD5,
                         new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
-        c.setSpiResourceId(IpSecTransform.DIRECTION_OUT, 1984);
-        c.setEncryption(
-                IpSecTransform.DIRECTION_IN,
-                new IpSecAlgorithm(
-                        IpSecAlgorithm.CRYPT_AES_CBC,
-                        new byte[] {2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
-        c.setAuthentication(
-                IpSecTransform.DIRECTION_IN,
-                new IpSecAlgorithm(
-                        IpSecAlgorithm.AUTH_HMAC_MD5,
-                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 1}));
-        c.setSpiResourceId(IpSecTransform.DIRECTION_IN, 99);
+        c.setSpiResourceId(1984);
         assertParcelingIsLossless(c);
     }
 
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index 0f40b45..cc3366f 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -81,15 +81,13 @@
         IpSecSpiResponse spiResp =
                 new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
         when(mMockIpSecService.allocateSecurityParameterIndex(
-                        eq(IpSecTransform.DIRECTION_IN),
                         eq(GOOGLE_DNS_4.getHostAddress()),
                         eq(DROID_SPI),
                         anyObject()))
                 .thenReturn(spiResp);
 
         IpSecManager.SecurityParameterIndex droidSpi =
-                mIpSecManager.allocateSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_IN, GOOGLE_DNS_4, DROID_SPI);
+                mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, DROID_SPI);
         assertEquals(DROID_SPI, droidSpi.getSpi());
 
         droidSpi.close();
@@ -103,15 +101,13 @@
         IpSecSpiResponse spiResp =
                 new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
         when(mMockIpSecService.allocateSecurityParameterIndex(
-                        eq(IpSecTransform.DIRECTION_OUT),
                         eq(GOOGLE_DNS_4.getHostAddress()),
                         eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
                         anyObject()))
                 .thenReturn(spiResp);
 
         IpSecManager.SecurityParameterIndex randomSpi =
-                mIpSecManager.allocateSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+                mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
 
         assertEquals(DROID_SPI, randomSpi.getSpi());
 
@@ -124,16 +120,15 @@
      * Throws resource unavailable exception
      */
     @Test
-    public void testAllocSpiResUnavaiableExeption() throws Exception {
+    public void testAllocSpiResUnavailableException() throws Exception {
         IpSecSpiResponse spiResp =
                 new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0);
         when(mMockIpSecService.allocateSecurityParameterIndex(
-                        anyInt(), anyString(), anyInt(), anyObject()))
+                        anyString(), anyInt(), anyObject()))
                 .thenReturn(spiResp);
 
         try {
-            mIpSecManager.allocateSecurityParameterIndex(
-                    IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+            mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
             fail("ResourceUnavailableException was not thrown");
         } catch (IpSecManager.ResourceUnavailableException e) {
         }
@@ -143,15 +138,14 @@
      * Throws spi unavailable exception
      */
     @Test
-    public void testAllocSpiSpiUnavaiableExeption() throws Exception {
+    public void testAllocSpiSpiUnavailableException() throws Exception {
         IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0);
         when(mMockIpSecService.allocateSecurityParameterIndex(
-                        anyInt(), anyString(), anyInt(), anyObject()))
+                        anyString(), anyInt(), anyObject()))
                 .thenReturn(spiResp);
 
         try {
-            mIpSecManager.allocateSecurityParameterIndex(
-                    IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+            mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
             fail("ResourceUnavailableException was not thrown");
         } catch (IpSecManager.ResourceUnavailableException e) {
         }
@@ -163,8 +157,7 @@
     @Test
     public void testRequestAllocInvalidSpi() throws Exception {
         try {
-            mIpSecManager.allocateSecurityParameterIndex(
-                    IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4, 0);
+            mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, 0);
             fail("Able to allocate invalid spi");
         } catch (IllegalArgumentException e) {
         }
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index 52da79a..f3c22a5 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -79,6 +79,9 @@
         assertTrue(source.isIdenticalDnses(target));
         assertTrue(target.isIdenticalDnses(source));
 
+        assertTrue(source.isIdenticalPrivateDns(target));
+        assertTrue(target.isIdenticalPrivateDns(source));
+
         assertTrue(source.isIdenticalRoutes(target));
         assertTrue(target.isIdenticalRoutes(source));
 
@@ -91,6 +94,9 @@
         assertTrue(source.isIdenticalMtu(target));
         assertTrue(target.isIdenticalMtu(source));
 
+        assertTrue(source.isIdenticalTcpBufferSizes(target));
+        assertTrue(target.isIdenticalTcpBufferSizes(source));
+
         // Check result of equals().
         assertTrue(source.equals(target));
         assertTrue(target.equals(source));
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 473dc538..9aad413 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -67,7 +67,7 @@
             assertEquals(msg, t.expectedType, got);
 
             if (got != MacAddress.TYPE_UNKNOWN) {
-                assertEquals(got, MacAddress.fromBytes(t.addr).addressType());
+                assertEquals(got, MacAddress.fromBytes(t.addr).getAddressType());
             }
         }
     }
@@ -191,7 +191,7 @@
 
             assertTrue(stringRepr + " expected to be a locally assigned address",
                     mac.isLocallyAssigned());
-            assertEquals(MacAddress.TYPE_UNICAST, mac.addressType());
+            assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType());
             assertTrue(stringRepr + " expected to begin with " + expectedLocalOui,
                     stringRepr.startsWith(expectedLocalOui));
         }
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index e6170cb..4c6a644 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -34,12 +34,15 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.os.Parcel;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Set;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -189,4 +192,84 @@
         assertEquals(20, NetworkCapabilities
                 .maxBandwidth(10, 20));
     }
+
+    @Test
+    public void testSetUids() {
+        final NetworkCapabilities netCap = new NetworkCapabilities();
+        final Set<UidRange> uids = new ArraySet<>();
+        uids.add(new UidRange(50, 100));
+        uids.add(new UidRange(3000, 4000));
+        netCap.setUids(uids);
+        assertTrue(netCap.appliesToUid(50));
+        assertTrue(netCap.appliesToUid(80));
+        assertTrue(netCap.appliesToUid(100));
+        assertTrue(netCap.appliesToUid(3000));
+        assertTrue(netCap.appliesToUid(3001));
+        assertFalse(netCap.appliesToUid(10));
+        assertFalse(netCap.appliesToUid(25));
+        assertFalse(netCap.appliesToUid(49));
+        assertFalse(netCap.appliesToUid(101));
+        assertFalse(netCap.appliesToUid(2000));
+        assertFalse(netCap.appliesToUid(100000));
+
+        assertTrue(netCap.appliesToUidRange(new UidRange(50, 100)));
+        assertTrue(netCap.appliesToUidRange(new UidRange(70, 72)));
+        assertTrue(netCap.appliesToUidRange(new UidRange(3500, 3912)));
+        assertFalse(netCap.appliesToUidRange(new UidRange(1, 100)));
+        assertFalse(netCap.appliesToUidRange(new UidRange(49, 100)));
+        assertFalse(netCap.appliesToUidRange(new UidRange(1, 10)));
+        assertFalse(netCap.appliesToUidRange(new UidRange(60, 101)));
+        assertFalse(netCap.appliesToUidRange(new UidRange(60, 3400)));
+
+        NetworkCapabilities netCap2 = new NetworkCapabilities();
+        assertFalse(netCap2.satisfiedByUids(netCap));
+        assertFalse(netCap2.equalsUids(netCap));
+        netCap2.setUids(uids);
+        assertTrue(netCap2.satisfiedByUids(netCap));
+        assertTrue(netCap.equalsUids(netCap2));
+        assertTrue(netCap2.equalsUids(netCap));
+
+        uids.add(new UidRange(600, 700));
+        netCap2.setUids(uids);
+        assertFalse(netCap2.satisfiedByUids(netCap));
+        assertFalse(netCap.appliesToUid(650));
+        assertTrue(netCap2.appliesToUid(650));
+        netCap.combineCapabilities(netCap2);
+        assertTrue(netCap2.satisfiedByUids(netCap));
+        assertTrue(netCap.appliesToUid(650));
+        assertFalse(netCap.appliesToUid(500));
+
+        assertFalse(new NetworkCapabilities().satisfiedByUids(netCap));
+        netCap.combineCapabilities(new NetworkCapabilities());
+        assertTrue(netCap.appliesToUid(500));
+        assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000)));
+        assertFalse(netCap2.appliesToUid(500));
+        assertFalse(netCap2.appliesToUidRange(new UidRange(1, 100000)));
+        assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
+    }
+
+    @Test
+    public void testParcelNetworkCapabilities() {
+        final Set<UidRange> uids = new ArraySet<>();
+        uids.add(new UidRange(50, 100));
+        uids.add(new UidRange(3000, 4000));
+        final NetworkCapabilities netCap = new NetworkCapabilities()
+            .addCapability(NET_CAPABILITY_INTERNET)
+            .setUids(uids)
+            .addCapability(NET_CAPABILITY_EIMS)
+            .addCapability(NET_CAPABILITY_NOT_METERED);
+        assertEqualsThroughMarshalling(netCap);
+    }
+
+    private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) {
+        Parcel p = Parcel.obtain();
+        netCap.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        byte[] marshalledData = p.marshall();
+
+        p = Parcel.obtain();
+        p.unmarshall(marshalledData, 0, marshalledData.length);
+        p.setDataPosition(0);
+        assertEquals(NetworkCapabilities.CREATOR.createFromParcel(p), netCap);
+    }
 }
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 25289ba..035a4cd7 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -16,6 +16,9 @@
 
 package android.net;
 
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
 import static android.net.NetworkStats.METERED_ALL;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
@@ -56,71 +59,75 @@
     @Test
     public void testFindIndex() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 5)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
-                        8L, 0L, 0L, 10)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
-                        1024L, 8L, 11)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 0L, 0L,
-                        1024L, 8L, 11)
-                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
-                        8L, 1024L, 8L, 12)
-                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 1024L,
-                        8L, 1024L, 8L, 12);
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
+                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12)
+                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+                        DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12);
 
         assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES,
-                ROAMING_YES));
+                ROAMING_YES, DEFAULT_NETWORK_YES));
         assertEquals(3, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO));
+                ROAMING_NO, DEFAULT_NETWORK_NO));
         assertEquals(2, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES,
-                ROAMING_NO));
+                ROAMING_NO, DEFAULT_NETWORK_YES));
         assertEquals(1, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO));
+                ROAMING_NO, DEFAULT_NETWORK_NO));
         assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO));
+                ROAMING_NO, DEFAULT_NETWORK_YES));
         assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO));
+                ROAMING_NO, DEFAULT_NETWORK_NO));
+        assertEquals(-1, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO));
     }
 
     @Test
     public void testFindIndexHinted() {
         final NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
-                        8L, 0L, 0L, 10)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
-                        1024L, 8L, 11)
-                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
-                        8L, 1024L, 8L, 12)
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
+                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
                 .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                        1024L, 8L, 0L, 0L, 10)
-                .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 0L, 0L,
-                        1024L, 8L, 11)
-                .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 0L, 0L,
-                        1024L, 8L, 11)
-                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
-                        8L, 1024L, 8L, 12)
-                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 1024L,
-                        8L, 1024L, 8L, 12);
+                        DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10)
+                .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
+                .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
+                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
+                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+                        DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12);
 
         // verify that we correctly find across regardless of hinting
         for (int hint = 0; hint < stats.size(); hint++) {
             assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint));
             assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, hint));
             assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint));
             assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, hint));
             assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D,
-                    METERED_NO, ROAMING_NO, hint));
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint));
             assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D,
-                    METERED_YES, ROAMING_NO, hint));
+                    METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, hint));
             assertEquals(6, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint));
             assertEquals(7, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
-                    METERED_YES, ROAMING_YES, hint));
+                    METERED_YES, ROAMING_YES, DEFAULT_NETWORK_NO, hint));
             assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint));
+            assertEquals(-1, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
+                    METERED_YES, ROAMING_YES, DEFAULT_NETWORK_YES, hint));
         }
     }
 
@@ -131,50 +138,50 @@
         assertEquals(0, stats.size());
         assertEquals(4, stats.internalSize());
 
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1L, 1L,
-                2L, 2L, 3);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 2L, 2L,
-                2L, 2L, 4);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 3L,
-                3L, 2L, 2L, 5);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 3L,
-                3L, 2L, 2L, 5);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+                DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5);
 
         assertEquals(4, stats.size());
         assertEquals(4, stats.internalSize());
 
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 4L,
-                40L, 4L, 40L, 7);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 5L,
-                50L, 4L, 40L, 8);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 6L,
-                60L, 5L, 50L, 10);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 7L,
-                70L, 5L, 50L, 11);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 7L,
-                70L, 5L, 50L, 11);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+                DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11);
 
         assertEquals(9, stats.size());
         assertTrue(stats.internalSize() >= 9);
 
         assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                1L, 1L, 2L, 2L, 3);
+                DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3);
         assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                2L, 2L, 2L, 2L, 4);
+                DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4);
         assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
-                3L, 3L, 2L, 2L, 5);
+                DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5);
         assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES,
-                ROAMING_YES, 3L, 3L, 2L, 2L, 5);
+                ROAMING_YES, DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5);
         assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                4L, 40L, 4L, 40L, 7);
+                DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7);
         assertValues(stats, 5, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                5L, 50L, 4L, 40L, 8);
+                DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8);
         assertValues(stats, 6, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                6L, 60L, 5L, 50L, 10);
+                DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10);
         assertValues(stats, 7, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
-                7L, 70L, 5L, 50L, 11);
+                DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11);
         assertValues(stats, 8, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES,
-                ROAMING_YES, 7L, 70L, 5L, 50L, 11);
+                ROAMING_YES, DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11);
     }
 
     @Test
@@ -187,17 +194,17 @@
                 -128L, -1L, -1);
 
         assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                384L, 3L, 128L, 1L, 9);
-        assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, METERED_NO, ROAMING_NO, 128L,
-                1L, 128L, 1L, 2);
+                DEFAULT_NETWORK_NO, 384L, 3L, 128L, 1L, 9);
+        assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 128L, 1L, 128L, 1L, 2);
 
         // now try combining that should create row
         stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3);
         assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                128L, 1L, 128L, 1L, 3);
+                DEFAULT_NETWORK_NO, 128L, 1L, 128L, 1L, 3);
         stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3);
         assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                256L, 2L, 256L, 2L, 6);
+                DEFAULT_NETWORK_NO, 256L, 2L, 256L, 2L, 6);
     }
 
     @Test
@@ -213,10 +220,10 @@
         final NetworkStats result = after.subtract(before);
 
         // identical data should result in zero delta
-        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
-                0L, 0L, 0L, 0);
-        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
-                0L, 0L, 0L, 0);
+        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0);
+        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0);
     }
 
     @Test
@@ -232,10 +239,10 @@
         final NetworkStats result = after.subtract(before);
 
         // expect delta between measurements
-        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1L,
-                1L, 2L, 1L, 4);
-        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 3L,
-                1L, 4L, 1L, 8);
+        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 1L, 1L, 2L, 1L, 4);
+        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 3L, 1L, 4L, 1L, 8);
     }
 
     @Test
@@ -252,12 +259,12 @@
         final NetworkStats result = after.subtract(before);
 
         // its okay to have new rows
-        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
-                0L, 0L, 0L, 0);
-        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
-                0L, 0L, 0L, 0);
+        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0);
+        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0);
         assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                1024L, 8L, 1024L, 8L, 20);
+                DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 20);
     }
 
     @Test
@@ -274,7 +281,7 @@
         // should silently drop omitted rows
         assertEquals(1, result.size());
         assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 1L, 2L, 3L, 4L, 0);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 2L, 3L, 4L, 0);
         assertEquals(4L, result.getTotalBytes());
     }
 
@@ -301,21 +308,21 @@
         assertEquals(64L, uidTag.getTotalBytes());
 
         final NetworkStats uidMetered = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L);
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidMetered.getTotalBytes());
 
         final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 32L, 0L,
-                        0L, 0L, 0L);
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                        DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidRoaming.getTotalBytes());
     }
 
@@ -331,38 +338,38 @@
     @Test
     public void testGroupedByIfaceAll() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
-                .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L, 0L,
-                        2L, 20L)
-                .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO, 128L,
-                        8L, 0L, 2L, 20L)
-                .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES, 128L, 8L, 0L,
-                        2L, 20L);
+                .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
+                .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L)
+                .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
+                        DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L);
         final NetworkStats grouped = uidStats.groupedByIface();
 
         assertEquals(3, uidStats.size());
         assertEquals(1, grouped.size());
 
         assertValues(grouped, 0, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
-                384L, 24L, 0L, 6L, 0L);
+                DEFAULT_NETWORK_ALL, 384L, 24L, 0L, 6L, 0L);
     }
 
     @Test
     public void testGroupedByIface() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
-                        0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
-                        32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L, 4L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
-                        32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 128L, 8L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 128L,
-                        8L, 0L, 0L, 0L);
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                        DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L);
 
         final NetworkStats grouped = uidStats.groupedByIface();
 
@@ -370,59 +377,59 @@
 
         assertEquals(2, grouped.size());
         assertValues(grouped, 0, TEST_IFACE, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
-                384L, 24L, 0L, 2L, 0L);
+                DEFAULT_NETWORK_ALL, 384L, 24L, 0L, 2L, 0L);
         assertValues(grouped, 1, TEST_IFACE2, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
-                1024L, 64L, 0L, 0L, 0L);
+                DEFAULT_NETWORK_ALL, 1024L, 64L, 0L, 0L, 0L);
     }
 
     @Test
     public void testAddAllValues() {
         final NetworkStats first = new NetworkStats(TEST_START, 5)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 32L,
-                        0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, 32L,
-                        0L, 0L, 0L, 0L);
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+                        DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
 
         final NetworkStats second = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L,
-                        0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, 32L,
-                        0L, 0L, 0L, 0L);
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+                        DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
 
         first.combineAllValues(second);
 
         assertEquals(4, first.size());
-        assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 64L,
-                0L, 0L, 0L, 0L);
+        assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 64L, 0L, 0L, 0L, 0L);
         assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                32L, 0L, 0L, 0L, 0L);
+                DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L);
         assertValues(first, 2, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
-                64L, 0L, 0L, 0L, 0L);
+                DEFAULT_NETWORK_YES, 64L, 0L, 0L, 0L, 0L);
         assertValues(first, 3, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                32L, 0L, 0L, 0L, 0L);
+                DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L);
     }
 
     @Test
     public void testGetTotal() {
         final NetworkStats stats = new NetworkStats(TEST_START, 7)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
-                        0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
-                        32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L, 4L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
-                        32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 128L,
-                        8L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 128L, 8L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 128L,
-                        8L, 0L, 0L, 0L);
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                        DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
 
         assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
         assertValues(stats.getTotal(null, 100), 1280L, 80L, 0L, 2L, 20L);
@@ -449,9 +456,9 @@
         assertEquals(6, before.size());
         assertEquals(2, after.size());
         assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                128L, 8L, 0L, 0L, 0L);
-        assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 128L,
-                8L, 0L, 0L, 0L);
+                DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
+        assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
     }
 
     @Test
@@ -489,90 +496,90 @@
         final String underlyingIface = "wlan0";
         final int testTag1 = 8888;
         NetworkStats delta = new NetworkStats(TEST_START, 17)
-            .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 39605L, 46L,
-                    12259L, 55L, 0L)
-            .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
-                    0L, 0L, 0L)
-            .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 72667L, 197L,
-                    43909L, 241L, 0L)
-            .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 9297L,
-                    17L, 4128L, 21L, 0L)
+            .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L)
+            .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+            .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L)
+            .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L)
             // VPN package also uses some traffic through unprotected network.
-            .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 4983L, 10L,
-                    1801L, 12L, 0L)
-            .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
-                    0L, 0L, 0L)
+            .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L)
+            .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
             // Tag entries
-            .addValues(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, 21691L, 41L,
-                    13820L, 51L, 0L)
-            .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, 1281L, 2L,
-                    665L, 2L, 0L)
+            .addValues(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L)
+            .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L)
             // Irrelevant entries
-            .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1685L, 5L,
-                    2070L, 6L, 0L)
+            .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L)
             // Underlying Iface entries
-            .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 5178L,
-                    8L, 2139L, 11L, 0L)
-            .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
-                    0L, 0L, 0L, 0L)
+            .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L)
+            .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
             .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    149873L, 287L, 59217L /* smaller than sum(tun0) */,
+                    DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */,
                     299L /* smaller than sum(tun0) */, 0L)
             .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                    0L, 0L, 0L, 0L, 0L);
+                    DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
 
-        assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
+        assertTrue(delta.toString(), delta.migrateTun(tunUid, tunIface, underlyingIface));
         assertEquals(20, delta.size());
 
         // tunIface and TEST_IFACE entries are not changed.
         assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                39605L, 46L, 12259L, 55L, 0L);
+                DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L);
         assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                0L, 0L, 0L, 0L, 0L);
+                DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
         assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                72667L, 197L, 43909L, 241L, 0L);
+                DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L);
         assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                9297L, 17L, 4128L, 21L, 0L);
+                DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L);
         assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                4983L, 10L, 1801L, 12L, 0L);
+                DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L);
         assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                0L, 0L, 0L, 0L, 0L);
+                DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
         assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
-                21691L, 41L, 13820L, 51L, 0L);
+                DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L);
         assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
-                1281L, 2L, 665L, 2L, 0L);
+                DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L);
         assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                1685L, 5L, 2070L, 6L, 0L);
+                DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L);
 
         // Existing underlying Iface entries are updated
         assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 44783L, 54L, 14178L, 62L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 44783L, 54L, 14178L, 62L, 0L);
         assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO,
-                ROAMING_NO, 0L, 0L, 0L, 0L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
 
         // VPN underlying Iface entries are updated
         assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 28304L, 27L, 1L, 2L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 28304L, 27L, 1L, 2L, 0L);
         assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO,
-                ROAMING_NO, 0L, 0L, 0L, 0L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
 
         // New entries are added for new application's underlying Iface traffic
         assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 72667L, 197L, 43123L, 227L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 72667L, 197L, 43123L, 227L, 0L);
         assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO,
-                ROAMING_NO, 9297L, 17L, 4054, 19L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 9297L, 17L, 4054, 19L, 0L);
         assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, METERED_NO,
-                ROAMING_NO, 21691L, 41L, 13572L, 48L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 21691L, 41L, 13572L, 48L, 0L);
         assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, METERED_NO,
-                ROAMING_NO, 1281L, 2L, 653L, 1L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1281L, 2L, 653L, 1L, 0L);
 
         // New entries are added for debug purpose
         assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
-                ROAMING_NO, 39605L, 46L, 12039, 51, 0);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 39605L, 46L, 12039, 51, 0);
         assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
-                ROAMING_NO, 81964, 214, 47177, 246, 0);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 81964, 214, 47177, 246, 0);
         assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL,
-                ROAMING_ALL, 121569, 260, 59216, 297, 0);
+                ROAMING_ALL, DEFAULT_NETWORK_ALL, 121569, 260, 59216, 297, 0);
 
     }
 
@@ -587,79 +594,80 @@
         final String underlyingIface = "wlan0";
         NetworkStats delta = new NetworkStats(TEST_START, 9)
             // 2 different apps sent/receive data via tun0.
-            .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 50000L, 25L,
-                    100000L, 50L, 0L)
-            .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 500L, 2L,
-                    200L, 5L, 0L)
+            .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
+            .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
             // VPN package resends data through the tunnel (with exaggerated overhead)
-            .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 240000,
-                    100L, 120000L, 60L, 0L)
+            .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L)
             // 1 app already has some traffic on the underlying interface, the other doesn't yet
-            .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1000L,
-                    10L, 2000L, 20L, 0L)
+            .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L)
             // Traffic through the underlying interface via the vpn app.
             // This test should redistribute this data correctly.
             .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    75500L, 37L, 130000L, 70L, 0L);
+                    DEFAULT_NETWORK_NO,  75500L, 37L, 130000L, 70L, 0L);
 
         assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
         assertEquals(9, delta.size());
 
         // tunIface entries should not be changed.
         assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                50000L, 25L, 100000L, 50L, 0L);
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
         assertValues(delta, 1, tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                500L, 2L, 200L, 5L, 0L);
+                DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L);
         assertValues(delta, 2, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                240000L, 100L, 120000L, 60L, 0L);
+                DEFAULT_NETWORK_NO, 240000L, 100L, 120000L, 60L, 0L);
 
         // Existing underlying Iface entries are updated
         assertValues(delta, 3, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 51000L, 35L, 102000L, 70L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 51000L, 35L, 102000L, 70L, 0L);
 
         // VPN underlying Iface entries are updated
         assertValues(delta, 4, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 25000L, 10L, 29800L, 15L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 25000L, 10L, 29800L, 15L, 0L);
 
         // New entries are added for new application's underlying Iface traffic
         assertContains(delta, underlyingIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 500L, 2L, 200L, 5L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L);
 
         // New entries are added for debug purpose
         assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
-                ROAMING_NO, 50000L, 25L, 100000L, 50L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
         assertContains(delta, underlyingIface, 20100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
-                ROAMING_NO, 500, 2L, 200L, 5L, 0L);
+                ROAMING_NO, DEFAULT_NETWORK_NO, 500, 2L, 200L, 5L, 0L);
         assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL,
-                ROAMING_ALL, 50500L, 27L, 100200L, 55, 0);
+                ROAMING_ALL, DEFAULT_NETWORK_ALL, 50500L, 27L, 100200L, 55, 0);
     }
 
     private static void assertContains(NetworkStats stats,  String iface, int uid, int set,
-            int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes,
-            long txPackets, long operations) {
-        int index = stats.findIndex(iface, uid, set, tag, metered, roaming);
+            int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
+            long txBytes, long txPackets, long operations) {
+        int index = stats.findIndex(iface, uid, set, tag, metered, roaming, defaultNetwork);
         assertTrue(index != -1);
-        assertValues(stats, index, iface, uid, set, tag, metered, roaming,
+        assertValues(stats, index, iface, uid, set, tag, metered, roaming, defaultNetwork,
                 rxBytes, rxPackets, txBytes, txPackets, operations);
     }
 
     private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
-            int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes,
-            long txPackets, long operations) {
+            int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
+            long txBytes, long txPackets, long operations) {
         final NetworkStats.Entry entry = stats.getValues(index, null);
-        assertValues(entry, iface, uid, set, tag, metered, roaming);
+        assertValues(entry, iface, uid, set, tag, metered, roaming, defaultNetwork);
         assertValues(entry, rxBytes, rxPackets, txBytes, txPackets, operations);
     }
 
     private static void assertValues(
             NetworkStats.Entry entry, String iface, int uid, int set, int tag, int metered,
-            int roaming) {
+            int roaming, int defaultNetwork) {
         assertEquals(iface, entry.iface);
         assertEquals(uid, entry.uid);
         assertEquals(set, entry.set);
         assertEquals(tag, entry.tag);
         assertEquals(metered, entry.metered);
         assertEquals(roaming, entry.roaming);
+        assertEquals(defaultNetwork, entry.defaultNetwork);
     }
 
     private static void assertValues(NetworkStats.Entry entry, long rxBytes, long rxPackets,
diff --git a/tests/net/java/android/net/NetworkTest.java b/tests/net/java/android/net/NetworkTest.java
index bacf986..94d01e9 100644
--- a/tests/net/java/android/net/NetworkTest.java
+++ b/tests/net/java/android/net/NetworkTest.java
@@ -147,9 +147,9 @@
 
         // Adjust as necessary to test an implementation's specific constants.
         // When running with runtest, "adb logcat -s TestRunner" can be useful.
-        assertEquals(4311403230L, one.getNetworkHandle());
-        assertEquals(8606370526L, two.getNetworkHandle());
-        assertEquals(12901337822L, three.getNetworkHandle());
+        assertEquals(7700664333L, one.getNetworkHandle());
+        assertEquals(11995631629L, two.getNetworkHandle());
+        assertEquals(16290598925L, three.getNetworkHandle());
     }
 
     private static <T> void assertNotEqual(T t1, T t2) {
diff --git a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
index fb2bd79..b14f550 100644
--- a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.net;
 
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.SET_ALL;
@@ -66,7 +67,7 @@
             IoUtils.deleteContents(mTestProc);
         }
 
-        mFactory = new NetworkStatsFactory(mTestProc);
+        mFactory = new NetworkStatsFactory(mTestProc, false);
     }
 
     @After
@@ -115,6 +116,20 @@
     }
 
     @Test
+    public void testNetworkStatsSummary() throws Exception {
+        stageFile(R.raw.net_dev_typical, file("net/dev"));
+
+        final NetworkStats stats = mFactory.readNetworkStatsIfaceDev();
+        assertEquals(6, stats.size());
+        assertStatsEntry(stats, "lo", UID_ALL, SET_ALL, TAG_NONE, 8308L, 8308L);
+        assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 1507570L, 489339L);
+        assertStatsEntry(stats, "ifb0", UID_ALL, SET_ALL, TAG_NONE, 52454L, 0L);
+        assertStatsEntry(stats, "ifb1", UID_ALL, SET_ALL, TAG_NONE, 52454L, 0L);
+        assertStatsEntry(stats, "sit0", UID_ALL, SET_ALL, TAG_NONE, 0L, 0L);
+        assertStatsEntry(stats, "ip6tnl0", UID_ALL, SET_ALL, TAG_NONE, 0L, 0L);
+    }
+
+    @Test
     public void testNetworkStatsSingle() throws Exception {
         stageFile(R.raw.xt_qtaguid_iface_typical, file("net/xt_qtaguid/iface_stat_all"));
 
@@ -240,7 +255,8 @@
 
     private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
             int tag, long rxBytes, long txBytes) {
-        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO);
+        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO);
         if (i < 0) {
             fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)",
                     iface, uid, set, tag));
@@ -252,7 +268,8 @@
 
     private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
             int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
-        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO);
+        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO);
         if (i < 0) {
             fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)",
                     iface, uid, set, tag));
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2b0349c..6e643a3 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -44,6 +44,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
 
@@ -101,6 +102,7 @@
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.net.StringNetworkSpecifier;
+import android.net.UidRange;
 import android.net.metrics.IpConnectivityLog;
 import android.net.util.MultinetworkPolicyTracker;
 import android.os.ConditionVariable;
@@ -126,11 +128,13 @@
 import com.android.internal.util.WakeupMessage;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.ConnectivityConstants;
 import com.android.server.connectivity.DefaultNetworkMetrics;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.Vpn;
 import com.android.server.net.NetworkPinner;
 import com.android.server.net.NetworkPolicyManagerInternal;
 
@@ -360,7 +364,7 @@
 
         MockNetworkAgent(int transport, LinkProperties linkProperties) {
             final int type = transportToLegacyType(transport);
-            final String typeName = ConnectivityManager.getNetworkTypeName(type);
+            final String typeName = ConnectivityManager.getNetworkTypeName(transport);
             mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
             mNetworkCapabilities = new NetworkCapabilities();
             mNetworkCapabilities.addTransportType(transport);
@@ -377,6 +381,9 @@
                 case TRANSPORT_WIFI_AWARE:
                     mScore = 20;
                     break;
+                case TRANSPORT_VPN:
+                    mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
+                    break;
                 default:
                     throw new UnsupportedOperationException("unimplemented network type");
             }
@@ -438,6 +445,11 @@
             mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
         }
 
+        public void setUids(Set<UidRange> uids) {
+            mNetworkCapabilities.setUids(uids);
+            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+        }
+
         public void setSignalStrength(int signalStrength) {
             mNetworkCapabilities.setSignalStrength(signalStrength);
             mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
@@ -1392,39 +1404,80 @@
             return null;
         }
 
-        void expectAvailableCallbacks(
-                MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
+        // Expects onAvailable and the callbacks that follow it. These are:
+        // - onSuspended, iff the network was suspended when the callbacks fire.
+        // - onCapabilitiesChanged.
+        // - onLinkPropertiesChanged.
+        //
+        // @param agent the network to expect the callbacks on.
+        // @param expectSuspended whether to expect a SUSPENDED callback.
+        // @param expectValidated the expected value of the VALIDATED capability in the
+        //        onCapabilitiesChanged callback.
+        // @param timeoutMs how long to wait for the callbacks.
+        void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
+                boolean expectValidated, int timeoutMs) {
             expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
             if (expectSuspended) {
                 expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
             }
-            expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
+            if (expectValidated) {
+                expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+            } else {
+                expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent);
+            }
             expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
         }
 
-        void expectAvailableCallbacks(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+        // Expects the available callbacks (validated), plus onSuspended.
+        void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
+            expectAvailableCallbacks(agent, true, expectValidated, TIMEOUT_MS);
         }
 
-        void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, true, TIMEOUT_MS);
+        void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, false, true, TIMEOUT_MS);
         }
 
-        void expectAvailableAndValidatedCallbacks(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+        void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, false, false, TIMEOUT_MS);
+        }
+
+        // Expects the available callbacks (where the onCapabilitiesChanged must contain the
+        // VALIDATED capability), plus another onCapabilitiesChanged which is identical to the
+        // one we just sent.
+        // TODO: this is likely a bug. Fix it and remove this method.
+        void expectAvailableDoubleValidatedCallbacks(MockNetworkAgent agent) {
+            expectCallback(CallbackState.AVAILABLE, agent, TIMEOUT_MS);
+            NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+            expectCallback(CallbackState.LINK_PROPERTIES, agent, TIMEOUT_MS);
+            NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+            assertEquals(nc1, nc2);
+        }
+
+        // Expects the available callbacks where the onCapabilitiesChanged must not have validated,
+        // then expects another onCapabilitiesChanged that has the validated bit set. This is used
+        // when a network connects and satisfies a callback, and then immediately validates.
+        void expectAvailableThenValidatedCallbacks(MockNetworkAgent agent) {
+            expectAvailableCallbacksUnvalidated(agent);
             expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
         }
 
-        void expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
+        NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
             CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
             NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
             assertTrue(nc.hasCapability(capability));
+            return nc;
         }
 
-        void expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
+        NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
             CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
             NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
             assertFalse(nc.hasCapability(capability));
+            return nc;
+        }
+
+        void expectCapabilitiesLike(Predicate<NetworkCapabilities> fn, MockNetworkAgent agent) {
+            CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
+            assertTrue(fn.test((NetworkCapabilities) cbi.arg));
         }
 
         void assertNoCallback() {
@@ -1461,8 +1514,8 @@
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false);
-        genericNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
-        cellNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         waitFor(cv);
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1476,8 +1529,8 @@
         cv = waitForConnectivityBroadcasts(2);
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
-        wifiNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         waitFor(cv);
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1500,8 +1553,8 @@
         // Test validated networks
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        genericNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
-        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        genericNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
 
@@ -1513,10 +1566,10 @@
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         genericNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
         genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
-        wifiNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+        wifiNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         cellNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1552,32 +1605,32 @@
         mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
 
         mCellNetworkAgent.connect(true);
-        callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         mWiFiNetworkAgent.connect(true);
         // We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request.
         // We then get LOSING when wifi validates and cell is outscored.
-        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         // TODO: Investigate sending validated before losing.
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         mEthernetNetworkAgent.connect(true);
-        callback.expectAvailableCallbacks(mEthernetNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
         // TODO: Investigate sending validated before losing.
         callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
         assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         mEthernetNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
-        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
 
         for (int i = 0; i < 4; i++) {
             MockNetworkAgent oldNetwork, newNetwork;
@@ -1594,7 +1647,7 @@
             callback.expectCallback(CallbackState.LOSING, oldNetwork);
             // TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
             // longer lingering?
-            defaultCallback.expectAvailableCallbacks(newNetwork);
+            defaultCallback.expectAvailableCallbacksValidated(newNetwork);
             assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork());
         }
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1614,7 +1667,7 @@
         // Disconnect our test networks.
         mWiFiNetworkAgent.disconnect();
         defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         mCellNetworkAgent.disconnect();
         defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
 
@@ -1630,22 +1683,22 @@
 
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false);   // Score: 10
-        callback.expectAvailableCallbacks(mCellNetworkAgent);
-        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         // Bring up wifi with a score of 20.
         // Cell stays up because it would satisfy the default request if it validated.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);   // Score: 20
-        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
-        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         // Bring up wifi with a score of 70.
@@ -1653,33 +1706,33 @@
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.adjustScore(50);
         mWiFiNetworkAgent.connect(false);   // Score: 70
-        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
-        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         // Tear down wifi.
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
         // it's arguably correct to linger it, since it was the default network before it validated.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         // TODO: Investigate sending validated before losing.
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         mCellNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -1687,12 +1740,12 @@
         // If a network is lingering, and we add and remove a request from it, resume lingering.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
-        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         // TODO: Investigate sending validated before losing.
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
@@ -1711,7 +1764,7 @@
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
 
         // Cell is now the default network. Pin it with a cell-specific request.
         noopCallback = new NetworkCallback();  // Can't reuse NetworkCallbacks. http://b/20701525
@@ -1720,8 +1773,8 @@
         // Now connect wifi, and expect it to become the default network.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        callback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         // The default request is lingering on cell, but nothing happens to cell, and we send no
         // callbacks for it, because it's kept up by cellRequest.
         callback.assertNoCallback();
@@ -1737,14 +1790,14 @@
         // Register a TRACK_DEFAULT request and check that it does not affect lingering.
         TestNetworkCallback trackDefaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(trackDefaultCallback);
-        trackDefaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        trackDefaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
         mEthernetNetworkAgent.connect(true);
-        callback.expectAvailableCallbacks(mEthernetNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
         callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
-        trackDefaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+        trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
 
         // Let linger run its course.
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
@@ -1771,13 +1824,13 @@
         // Bring up validated cell.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
 
         // Bring up unvalidated wifi with explicitlySelected=true.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(false);
         mWiFiNetworkAgent.connect(false);
-        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
 
         // Cell Remains the default.
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1800,7 +1853,7 @@
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(false);
         mWiFiNetworkAgent.connect(false);
-        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
 
         // If the user chooses no on the "No Internet access, stay connected?" dialog, we ask the
         // network to disconnect.
@@ -1811,7 +1864,7 @@
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(false);
         mWiFiNetworkAgent.connect(true);
-        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1820,7 +1873,7 @@
         // TODO: fix this.
         mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
         mEthernetNetworkAgent.connect(true);
-        callback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+        callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
         assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         callback.assertNoCallback();
 
@@ -1993,7 +2046,7 @@
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
         mCellNetworkAgent.connectWithoutInternet();
-        networkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         verifyActiveNetwork(TRANSPORT_WIFI);
 
         // Test releasing NetworkRequest disconnects cellular with MMS
@@ -2022,7 +2075,7 @@
         MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
         mmsNetworkAgent.connectWithoutInternet();
-        networkCallback.expectAvailableCallbacks(mmsNetworkAgent);
+        networkCallback.expectAvailableCallbacksUnvalidated(mmsNetworkAgent);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
 
         // Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
@@ -2049,7 +2102,7 @@
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         String firstRedirectUrl = "http://example.com/firstPath";
         mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
-        captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
 
         // Take down network.
@@ -2062,7 +2115,7 @@
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         String secondRedirectUrl = "http://example.com/secondPath";
         mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
-        captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
 
         // Make captive portal disappear then revalidate.
@@ -2072,9 +2125,7 @@
         captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
         // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
-        validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
-        // TODO: Investigate only sending available callbacks.
-        validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
 
         // Break network connectivity.
         // Expect NET_CAPABILITY_VALIDATED onLost callback.
@@ -2098,7 +2149,7 @@
         // Bring up wifi.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        validatedCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+        validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
 
         // Check that calling startCaptivePortalApp does nothing.
@@ -2109,7 +2160,7 @@
         // Turn into a captive portal.
         mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 302;
         mCm.reportNetworkConnectivity(wifiNetwork, false);
-        captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
         // Check that startCaptivePortalApp sends the expected intent.
@@ -2122,7 +2173,7 @@
         mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
         CaptivePortal c = (CaptivePortal) intent.getExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
         c.reportCaptivePortalDismissed();
-        validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
         mCm.unregisterNetworkCallback(validatedCallback);
@@ -2165,7 +2216,7 @@
         mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
 
         // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
-        validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         // But there should be no CaptivePortal callback.
         captivePortalCallback.assertNoCallback();
     }
@@ -2203,14 +2254,14 @@
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        cEmpty1.expectAvailableCallbacks(mWiFiNetworkAgent);
-        cEmpty2.expectAvailableCallbacks(mWiFiNetworkAgent);
-        cEmpty3.expectAvailableCallbacks(mWiFiNetworkAgent);
-        cEmpty4.expectAvailableCallbacks(mWiFiNetworkAgent);
+        cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        cEmpty2.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        cEmpty3.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        cEmpty4.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertNoCallbacks(cFoo, cBar);
 
         mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("foo"));
-        cFoo.expectAvailableCallbacks(mWiFiNetworkAgent);
+        cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         for (TestNetworkCallback c: emptyCallbacks) {
             c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
         }
@@ -2219,7 +2270,7 @@
 
         mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("bar"));
         cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        cBar.expectAvailableCallbacks(mWiFiNetworkAgent);
+        cBar.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         for (TestNetworkCallback c: emptyCallbacks) {
             c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
         }
@@ -2348,14 +2399,14 @@
         // Bring up cell and expect CALLBACK_AVAILABLE.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
-        defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
 
         // Bring up wifi and expect CALLBACK_AVAILABLE.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         cellNetworkCallback.assertNoCallback();
-        defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+        defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
 
         // Bring down cell. Expect no default network callback, since it wasn't the default.
         mCellNetworkAgent.disconnect();
@@ -2365,7 +2416,7 @@
         // Bring up cell. Expect no default network callback, since it won't be the default.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         defaultNetworkCallback.assertNoCallback();
 
         // Bring down wifi. Expect the default network callback to notified of LOST wifi
@@ -2373,7 +2424,7 @@
         mWiFiNetworkAgent.disconnect();
         cellNetworkCallback.assertNoCallback();
         defaultNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         mCellNetworkAgent.disconnect();
         cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
         defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -2394,7 +2445,7 @@
         // We should get onAvailable(), onCapabilitiesChanged(), and
         // onLinkPropertiesChanged() in rapid succession. Additionally, we
         // should get onCapabilitiesChanged() when the mobile network validates.
-        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
 
         // Update LinkProperties.
@@ -2415,7 +2466,7 @@
         mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
         // We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(),
         // as well as onNetworkSuspended() in rapid succession.
-        dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent);
+        dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent, true);
         dfltNetworkCallback.assertNoCallback();
 
         mCm.unregisterNetworkCallback(dfltNetworkCallback);
@@ -2455,18 +2506,18 @@
 
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
-        fgCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        fgCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
 
         // When wifi connects, cell lingers.
-        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
-        fgCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        fgCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         fgCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
         fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
@@ -2490,8 +2541,8 @@
         // is currently delivered before the onAvailable() callbacks.
         // TODO: Fix this.
         cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
-        cellCallback.expectAvailableCallbacks(mCellNetworkAgent);
-        fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        cellCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         // Expect a network capabilities update with FOREGROUND, because the most recent
         // request causes its state to change.
         callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
@@ -2511,7 +2562,7 @@
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         fgCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
 
         mCm.unregisterNetworkCallback(callback);
@@ -2651,7 +2702,7 @@
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         testFactory.expectAddRequests(2);  // Because the cell request changes score twice.
         mCellNetworkAgent.connect(true);
-        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         testFactory.waitForNetworkRequests(2);
         assertFalse(testFactory.getMyStartRequested());  // Because the cell network outscores us.
 
@@ -2742,16 +2793,15 @@
         // Bring up validated cell.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         Network cellNetwork = mCellNetworkAgent.getNetwork();
 
         // Bring up validated wifi.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
-        validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
-        validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+        validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
 
         // Fail validation on wifi.
@@ -2772,18 +2822,18 @@
         // that we switch back to cell.
         tracker.configRestrictsAvoidBadWifi = false;
         tracker.reevaluate();
-        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), cellNetwork);
 
         // Switch back to a restrictive carrier.
         tracker.configRestrictsAvoidBadWifi = true;
         tracker.reevaluate();
-        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), wifiNetwork);
 
         // Simulate the user selecting "switch" on the dialog, and check that we switch to cell.
         mCm.setAvoidUnvalidated(wifiNetwork);
-        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
                 NET_CAPABILITY_VALIDATED));
         assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2794,9 +2844,8 @@
         mWiFiNetworkAgent.disconnect();
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
-        validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
-        validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+        validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         wifiNetwork = mWiFiNetworkAgent.getNetwork();
 
         // Fail validation on wifi and expect the dialog to appear.
@@ -2810,7 +2859,7 @@
         tracker.reevaluate();
 
         // We now switch to cell.
-        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
                 NET_CAPABILITY_VALIDATED));
         assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2821,17 +2870,17 @@
         // We switch to wifi and then to cell.
         Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
         tracker.reevaluate();
-        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), wifiNetwork);
         Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
         tracker.reevaluate();
-        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), cellNetwork);
 
         // If cell goes down, we switch to wifi.
         mCellNetworkAgent.disconnect();
         defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
-        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         validatedWifiCallback.assertNoCallback();
 
         mCm.unregisterNetworkCallback(cellNetworkCallback);
@@ -2873,7 +2922,7 @@
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, timeoutMs);
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, timeoutMs);
 
         // pass timeout and validate that UNAVAILABLE is not called
         networkCallback.assertNoCallback();
@@ -2894,7 +2943,7 @@
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         final int assertTimeoutMs = 100;
-        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, assertTimeoutMs);
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, assertTimeoutMs);
         mWiFiNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
@@ -3381,7 +3430,7 @@
 
         // Bring up wifi aware network.
         wifiAware.connect(false, false);
-        callback.expectAvailableCallbacks(wifiAware);
+        callback.expectAvailableCallbacksUnvalidated(wifiAware);
 
         assertNull(mCm.getActiveNetworkInfo());
         assertNull(mCm.getActiveNetwork());
@@ -3469,34 +3518,50 @@
     @Test
     public void testStatsIfacesChanged() throws Exception {
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+
+        Network[] onlyCell = new Network[]{mCellNetworkAgent.getNetwork()};
+        Network[] onlyWifi = new Network[]{mWiFiNetworkAgent.getNetwork()};
 
         // Simple connection should have updated ifaces
         mCellNetworkAgent.connect(false);
         waitForIdle();
-        verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
+        reset(mStatsService);
+
+        // Default network switch should update ifaces.
+        mWiFiNetworkAgent.connect(false);
+        waitForIdle();
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyWifi);
+        reset(mStatsService);
+
+        // Disconnect should update ifaces.
+        mWiFiNetworkAgent.disconnect();
+        waitForIdle();
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
         reset(mStatsService);
 
         // Metered change should update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         waitForIdle();
-        verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
         reset(mStatsService);
 
         mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         waitForIdle();
-        verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
         reset(mStatsService);
 
         // Captive portal change shouldn't update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
         waitForIdle();
-        verify(mStatsService, never()).forceUpdateIfaces();
+        verify(mStatsService, never()).forceUpdateIfaces(onlyCell);
         reset(mStatsService);
 
         // Roaming change should update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
         waitForIdle();
-        verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
         reset(mStatsService);
     }
 
@@ -3577,4 +3642,76 @@
             return;
         }
     }
+
+    @Test
+    public void testVpnNetworkActive() {
+        final int uid = Process.myUid();
+
+        final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
+        final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
+        final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest genericRequest = new NetworkRequest.Builder().build();
+        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build();
+        final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_VPN).build();
+        mCm.registerNetworkCallback(genericRequest, genericNetworkCallback);
+        mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+        mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(false);
+
+        genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        vpnNetworkCallback.assertNoCallback();
+
+        // TODO : check callbacks agree with the return value of mCm.getActiveNetwork().
+        // Right now this is not possible because establish() is not adequately instrumented
+        // in this test.
+
+        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        ranges.add(new UidRange(uid, uid));
+        vpnNetworkAgent.setUids(ranges);
+        vpnNetworkAgent.connect(false);
+
+        genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+        wifiNetworkCallback.assertNoCallback();
+        vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+
+        genericNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
+        vpnNetworkCallback.expectCapabilitiesLike(
+                nc -> nc.appliesToUid(uid) && !nc.appliesToUid(uid + 1), vpnNetworkAgent);
+
+        ranges.clear();
+        vpnNetworkAgent.setUids(ranges);
+
+        genericNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+        wifiNetworkCallback.assertNoCallback();
+        vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+
+        ranges.add(new UidRange(uid, uid));
+        vpnNetworkAgent.setUids(ranges);
+
+        genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+        wifiNetworkCallback.assertNoCallback();
+        vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+
+        mWiFiNetworkAgent.disconnect();
+
+        genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        vpnNetworkCallback.assertNoCallback();
+
+        vpnNetworkAgent.disconnect();
+
+        genericNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+        wifiNetworkCallback.assertNoCallback();
+        vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+
+        mCm.unregisterNetworkCallback(genericNetworkCallback);
+        mCm.unregisterNetworkCallback(wifiNetworkCallback);
+        mCm.unregisterNetworkCallback(vpnNetworkCallback);
+    }
 }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 2282c13..66e0955 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -32,7 +31,6 @@
 import android.net.IpSecConfig;
 import android.net.IpSecManager;
 import android.net.IpSecSpiResponse;
-import android.net.IpSecTransform;
 import android.net.IpSecTransformResponse;
 import android.net.NetworkUtils;
 import android.os.Binder;
@@ -54,14 +52,14 @@
 @RunWith(Parameterized.class)
 public class IpSecServiceParameterizedTest {
 
-    private static final int TEST_SPI_OUT = 0xD1201D;
-    private static final int TEST_SPI_IN = TEST_SPI_OUT + 1;
+    private static final int TEST_SPI = 0xD1201D;
 
-    private final String mRemoteAddr;
+    private final String mDestinationAddr;
+    private final String mSourceAddr;
 
     @Parameterized.Parameters
     public static Collection ipSecConfigs() {
-        return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
+        return Arrays.asList(new Object[][] {{"1.2.3.4", "8.8.4.4"}, {"2601::2", "2601::10"}});
     }
 
     private static final byte[] AEAD_KEY = {
@@ -96,11 +94,9 @@
     private static final IpSecAlgorithm AEAD_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
 
-    private static final int[] DIRECTIONS =
-            new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
-
-    public IpSecServiceParameterizedTest(String remoteAddr) {
-        mRemoteAddr = remoteAddr;
+    public IpSecServiceParameterizedTest(String sourceAddr, String destAddr) {
+        mSourceAddr = sourceAddr;
+        mDestinationAddr = destAddr;
     }
 
     @Before
@@ -116,44 +112,35 @@
 
     @Test
     public void testIpSecServiceReserveSpi() throws Exception {
-        when(mMockNetd.ipSecAllocateSpi(
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_OUT),
-                        anyString(),
-                        eq(mRemoteAddr),
-                        eq(TEST_SPI_OUT)))
-                .thenReturn(TEST_SPI_OUT);
+        when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+                .thenReturn(TEST_SPI);
 
         IpSecSpiResponse spiResp =
                 mIpSecService.allocateSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+                        mDestinationAddr, TEST_SPI, new Binder());
         assertEquals(IpSecManager.Status.OK, spiResp.status);
-        assertEquals(TEST_SPI_OUT, spiResp.spi);
+        assertEquals(TEST_SPI, spiResp.spi);
     }
 
     @Test
     public void testReleaseSecurityParameterIndex() throws Exception {
-        when(mMockNetd.ipSecAllocateSpi(
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_OUT),
-                        anyString(),
-                        eq(mRemoteAddr),
-                        eq(TEST_SPI_OUT)))
-                .thenReturn(TEST_SPI_OUT);
+        when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+                .thenReturn(TEST_SPI);
 
         IpSecSpiResponse spiResp =
                 mIpSecService.allocateSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+                        mDestinationAddr, TEST_SPI, new Binder());
 
         mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
                         eq(spiResp.resourceId),
+                        anyString(),
+                        anyString(),
+                        eq(TEST_SPI),
                         anyInt(),
-                        anyString(),
-                        anyString(),
-                        eq(TEST_SPI_OUT));
+                        anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
         IpSecService.UserRecord userRecord =
@@ -169,17 +156,12 @@
 
     @Test
     public void testSecurityParameterIndexBinderDeath() throws Exception {
-        when(mMockNetd.ipSecAllocateSpi(
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_OUT),
-                        anyString(),
-                        eq(mRemoteAddr),
-                        eq(TEST_SPI_OUT)))
-                .thenReturn(TEST_SPI_OUT);
+        when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+                .thenReturn(TEST_SPI);
 
         IpSecSpiResponse spiResp =
                 mIpSecService.allocateSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+                        mDestinationAddr, TEST_SPI, new Binder());
 
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -191,10 +173,11 @@
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
                         eq(spiResp.resourceId),
+                        anyString(),
+                        anyString(),
+                        eq(TEST_SPI),
                         anyInt(),
-                        anyString(),
-                        anyString(),
-                        eq(TEST_SPI_OUT));
+                        anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
@@ -206,14 +189,12 @@
         }
     }
 
-    private int getNewSpiResourceId(int direction, String remoteAddress, int returnSpi)
-            throws Exception {
-        when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
+    private int getNewSpiResourceId(String remoteAddress, int returnSpi) throws Exception {
+        when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), anyString(), anyInt()))
                 .thenReturn(returnSpi);
 
         IpSecSpiResponse spi =
                 mIpSecService.allocateSecurityParameterIndex(
-                        direction,
                         NetworkUtils.numericToInetAddress(remoteAddress).getHostAddress(),
                         IpSecManager.INVALID_SECURITY_PARAMETER_INDEX,
                         new Binder());
@@ -221,156 +202,128 @@
     }
 
     private void addDefaultSpisAndRemoteAddrToIpSecConfig(IpSecConfig config) throws Exception {
-        config.setSpiResourceId(
-                IpSecTransform.DIRECTION_OUT,
-                getNewSpiResourceId(IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT));
-        config.setSpiResourceId(
-                IpSecTransform.DIRECTION_IN,
-                getNewSpiResourceId(IpSecTransform.DIRECTION_IN, mRemoteAddr, TEST_SPI_IN));
-        config.setRemoteAddress(mRemoteAddr);
+        config.setSpiResourceId(getNewSpiResourceId(mDestinationAddr, TEST_SPI));
+        config.setSourceAddress(mSourceAddr);
+        config.setDestinationAddress(mDestinationAddr);
     }
 
     private void addAuthAndCryptToIpSecConfig(IpSecConfig config) throws Exception {
-        for (int direction : DIRECTIONS) {
-            config.setEncryption(direction, CRYPT_ALGO);
-            config.setAuthentication(direction, AUTH_ALGO);
+        config.setEncryption(CRYPT_ALGO);
+        config.setAuthentication(AUTH_ALGO);
+    }
+
+    @Test
+    public void testCreateTransform() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+        assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+        verify(mMockNetd)
+                .ipSecAddSecurityAssociation(
+                        eq(createTransformResp.resourceId),
+                        anyInt(),
+                        anyString(),
+                        anyString(),
+                        anyInt(),
+                        eq(TEST_SPI),
+                        anyInt(),
+                        anyInt(),
+                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
+                        eq(AUTH_KEY),
+                        anyInt(),
+                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
+                        eq(CRYPT_KEY),
+                        anyInt(),
+                        eq(""),
+                        eq(new byte[] {}),
+                        eq(0),
+                        anyInt(),
+                        anyInt(),
+                        anyInt());
+    }
+
+    @Test
+    public void testCreateTransformAead() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+
+        ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+        assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+        verify(mMockNetd)
+                .ipSecAddSecurityAssociation(
+                        eq(createTransformResp.resourceId),
+                        anyInt(),
+                        anyString(),
+                        anyString(),
+                        anyInt(),
+                        eq(TEST_SPI),
+                        anyInt(),
+                        anyInt(),
+                        eq(""),
+                        eq(new byte[] {}),
+                        eq(0),
+                        eq(""),
+                        eq(new byte[] {}),
+                        eq(0),
+                        eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
+                        eq(AEAD_KEY),
+                        anyInt(),
+                        anyInt(),
+                        anyInt(),
+                        anyInt());
+    }
+
+    public void testCreateTwoTransformsWithSameSpis() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+        assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+        // Attempting to create transform a second time with the same SPIs should throw an error...
+        try {
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+                fail("IpSecService should have thrown an error for reuse of SPI");
+        } catch (IllegalStateException expected) {
+        }
+
+        // ... even if the transform is deleted
+        mIpSecService.deleteTransform(createTransformResp.resourceId);
+        try {
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+                fail("IpSecService should have thrown an error for reuse of SPI");
+        } catch (IllegalStateException expected) {
         }
     }
 
     @Test
-    public void testCreateTransportModeTransform() throws Exception {
+    public void testDeleteTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-        assertEquals(IpSecManager.Status.OK, createTransformResp.status);
-
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_OUT),
-                        anyString(),
-                        anyString(),
-                        anyLong(),
-                        eq(TEST_SPI_OUT),
-                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
-                        eq(AUTH_KEY),
-                        anyInt(),
-                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
-                        eq(CRYPT_KEY),
-                        anyInt(),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_IN),
-                        anyString(),
-                        anyString(),
-                        anyLong(),
-                        eq(TEST_SPI_IN),
-                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
-                        eq(AUTH_KEY),
-                        anyInt(),
-                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
-                        eq(CRYPT_KEY),
-                        anyInt(),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
-    }
-
-    @Test
-    public void testCreateTransportModeTransformAead() throws Exception {
-        IpSecConfig ipSecConfig = new IpSecConfig();
-        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
-
-        ipSecConfig.setAuthenticatedEncryption(IpSecTransform.DIRECTION_OUT, AEAD_ALGO);
-        ipSecConfig.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
-
-        IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-        assertEquals(IpSecManager.Status.OK, createTransformResp.status);
-
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_OUT),
-                        anyString(),
-                        anyString(),
-                        anyLong(),
-                        eq(TEST_SPI_OUT),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
-                        eq(AEAD_KEY),
-                        anyInt(),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_IN),
-                        anyString(),
-                        anyString(),
-                        anyLong(),
-                        eq(TEST_SPI_IN),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
-                        eq(AEAD_KEY),
-                        anyInt(),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
-    }
-
-    @Test
-    public void testDeleteTransportModeTransform() throws Exception {
-        IpSecConfig ipSecConfig = new IpSecConfig();
-        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
-        addAuthAndCryptToIpSecConfig(ipSecConfig);
-
-        IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-        mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+        mIpSecService.deleteTransform(createTransformResp.resourceId);
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
                         eq(createTransformResp.resourceId),
-                        eq(IpSecTransform.DIRECTION_OUT),
                         anyString(),
                         anyString(),
-                        eq(TEST_SPI_OUT));
-        verify(mMockNetd)
-                .ipSecDeleteSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        eq(IpSecTransform.DIRECTION_IN),
-                        anyString(),
-                        anyString(),
-                        eq(TEST_SPI_IN));
+                        eq(TEST_SPI),
+                        anyInt(),
+                        anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
         IpSecService.UserRecord userRecord =
@@ -392,7 +345,7 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder());
 
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -405,17 +358,11 @@
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
                         eq(createTransformResp.resourceId),
-                        eq(IpSecTransform.DIRECTION_OUT),
                         anyString(),
                         anyString(),
-                        eq(TEST_SPI_OUT));
-        verify(mMockNetd)
-                .ipSecDeleteSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        eq(IpSecTransform.DIRECTION_IN),
-                        anyString(),
-                        anyString(),
-                        eq(TEST_SPI_IN));
+                        eq(TEST_SPI),
+                        anyInt(),
+                        anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
@@ -435,34 +382,26 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder());
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
 
         int resourceId = createTransformResp.resourceId;
-        mIpSecService.applyTransportModeTransform(pfd, resourceId);
+        mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId);
 
         verify(mMockNetd)
                 .ipSecApplyTransportModeTransform(
                         eq(pfd.getFileDescriptor()),
                         eq(resourceId),
-                        eq(IpSecTransform.DIRECTION_OUT),
+                        eq(IpSecManager.DIRECTION_OUT),
                         anyString(),
                         anyString(),
-                        eq(TEST_SPI_OUT));
-        verify(mMockNetd)
-                .ipSecApplyTransportModeTransform(
-                        eq(pfd.getFileDescriptor()),
-                        eq(resourceId),
-                        eq(IpSecTransform.DIRECTION_IN),
-                        anyString(),
-                        anyString(),
-                        eq(TEST_SPI_IN));
+                        eq(TEST_SPI));
     }
 
     @Test
     public void testRemoveTransportModeTransform() throws Exception {
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
-        mIpSecService.removeTransportModeTransform(pfd, 1);
+        mIpSecService.removeTransportModeTransforms(pfd);
 
         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
     }
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 0467989..2c94a60 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -105,9 +105,6 @@
     private static final IpSecAlgorithm AEAD_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
 
-    private static final int[] DIRECTIONS =
-            new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
-
     static {
         try {
             INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
@@ -169,6 +166,7 @@
         mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
         udpEncapResp.fileDescriptor.close();
 
+        // Verify quota and RefcountedResource objects cleaned up
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
         assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
@@ -182,10 +180,8 @@
 
     @Test
     public void testUdpEncapsulationSocketBinderDeath() throws Exception {
-        int localport = findUnusedPort();
-
         IpSecUdpEncapResponse udpEncapResp =
-                mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
+                mIpSecService.openUdpEncapsulationSocket(0, new Binder());
 
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -195,6 +191,7 @@
 
         refcountedRecord.binderDied();
 
+        // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
         try {
             userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
@@ -303,83 +300,75 @@
 
     @Test
     public void testValidateAlgorithmsAuth() {
-        for (int direction : DIRECTIONS) {
-            // Validate that correct algorithm type succeeds
-            IpSecConfig config = new IpSecConfig();
-            config.setAuthentication(direction, AUTH_ALGO);
-            mIpSecService.validateAlgorithms(config, direction);
+        // Validate that correct algorithm type succeeds
+        IpSecConfig config = new IpSecConfig();
+        config.setAuthentication(AUTH_ALGO);
+        mIpSecService.validateAlgorithms(config);
 
-            // Validate that incorrect algorithm types fails
-            for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
-                try {
-                    config = new IpSecConfig();
-                    config.setAuthentication(direction, algo);
-                    mIpSecService.validateAlgorithms(config, direction);
-                    fail("Did not throw exception on invalid algorithm type");
-                } catch (IllegalArgumentException expected) {
-                }
+        // Validate that incorrect algorithm types fails
+        for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
+            try {
+                config = new IpSecConfig();
+                config.setAuthentication(algo);
+                mIpSecService.validateAlgorithms(config);
+                fail("Did not throw exception on invalid algorithm type");
+            } catch (IllegalArgumentException expected) {
             }
         }
     }
 
     @Test
     public void testValidateAlgorithmsCrypt() {
-        for (int direction : DIRECTIONS) {
-            // Validate that correct algorithm type succeeds
-            IpSecConfig config = new IpSecConfig();
-            config.setEncryption(direction, CRYPT_ALGO);
-            mIpSecService.validateAlgorithms(config, direction);
+        // Validate that correct algorithm type succeeds
+        IpSecConfig config = new IpSecConfig();
+        config.setEncryption(CRYPT_ALGO);
+        mIpSecService.validateAlgorithms(config);
 
-            // Validate that incorrect algorithm types fails
-            for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
-                try {
-                    config = new IpSecConfig();
-                    config.setEncryption(direction, algo);
-                    mIpSecService.validateAlgorithms(config, direction);
-                    fail("Did not throw exception on invalid algorithm type");
-                } catch (IllegalArgumentException expected) {
-                }
+        // Validate that incorrect algorithm types fails
+        for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
+            try {
+                config = new IpSecConfig();
+                config.setEncryption(algo);
+                mIpSecService.validateAlgorithms(config);
+                fail("Did not throw exception on invalid algorithm type");
+            } catch (IllegalArgumentException expected) {
             }
         }
     }
 
     @Test
     public void testValidateAlgorithmsAead() {
-        for (int direction : DIRECTIONS) {
-            // Validate that correct algorithm type succeeds
-            IpSecConfig config = new IpSecConfig();
-            config.setAuthenticatedEncryption(direction, AEAD_ALGO);
-            mIpSecService.validateAlgorithms(config, direction);
+        // Validate that correct algorithm type succeeds
+        IpSecConfig config = new IpSecConfig();
+        config.setAuthenticatedEncryption(AEAD_ALGO);
+        mIpSecService.validateAlgorithms(config);
 
-            // Validate that incorrect algorithm types fails
-            for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
-                try {
-                    config = new IpSecConfig();
-                    config.setAuthenticatedEncryption(direction, algo);
-                    mIpSecService.validateAlgorithms(config, direction);
-                    fail("Did not throw exception on invalid algorithm type");
-                } catch (IllegalArgumentException expected) {
-                }
+        // Validate that incorrect algorithm types fails
+        for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
+            try {
+                config = new IpSecConfig();
+                config.setAuthenticatedEncryption(algo);
+                mIpSecService.validateAlgorithms(config);
+                fail("Did not throw exception on invalid algorithm type");
+            } catch (IllegalArgumentException expected) {
             }
         }
     }
 
     @Test
     public void testValidateAlgorithmsAuthCrypt() {
-        for (int direction : DIRECTIONS) {
-            // Validate that correct algorithm type succeeds
-            IpSecConfig config = new IpSecConfig();
-            config.setAuthentication(direction, AUTH_ALGO);
-            config.setEncryption(direction, CRYPT_ALGO);
-            mIpSecService.validateAlgorithms(config, direction);
-        }
+        // Validate that correct algorithm type succeeds
+        IpSecConfig config = new IpSecConfig();
+        config.setAuthentication(AUTH_ALGO);
+        config.setEncryption(CRYPT_ALGO);
+        mIpSecService.validateAlgorithms(config);
     }
 
     @Test
     public void testValidateAlgorithmsNoAlgorithms() {
         IpSecConfig config = new IpSecConfig();
         try {
-            mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+            mIpSecService.validateAlgorithms(config);
             fail("Expected exception; no algorithms specified");
         } catch (IllegalArgumentException expected) {
         }
@@ -388,10 +377,10 @@
     @Test
     public void testValidateAlgorithmsAeadWithAuth() {
         IpSecConfig config = new IpSecConfig();
-        config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
-        config.setAuthentication(IpSecTransform.DIRECTION_IN, AUTH_ALGO);
+        config.setAuthenticatedEncryption(AEAD_ALGO);
+        config.setAuthentication(AUTH_ALGO);
         try {
-            mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+            mIpSecService.validateAlgorithms(config);
             fail("Expected exception; both AEAD and auth algorithm specified");
         } catch (IllegalArgumentException expected) {
         }
@@ -400,10 +389,10 @@
     @Test
     public void testValidateAlgorithmsAeadWithCrypt() {
         IpSecConfig config = new IpSecConfig();
-        config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
-        config.setEncryption(IpSecTransform.DIRECTION_IN, CRYPT_ALGO);
+        config.setAuthenticatedEncryption(AEAD_ALGO);
+        config.setEncryption(CRYPT_ALGO);
         try {
-            mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+            mIpSecService.validateAlgorithms(config);
             fail("Expected exception; both AEAD and crypt algorithm specified");
         } catch (IllegalArgumentException expected) {
         }
@@ -412,20 +401,20 @@
     @Test
     public void testValidateAlgorithmsAeadWithAuthAndCrypt() {
         IpSecConfig config = new IpSecConfig();
-        config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
-        config.setAuthentication(IpSecTransform.DIRECTION_IN, AUTH_ALGO);
-        config.setEncryption(IpSecTransform.DIRECTION_IN, CRYPT_ALGO);
+        config.setAuthenticatedEncryption(AEAD_ALGO);
+        config.setAuthentication(AUTH_ALGO);
+        config.setEncryption(CRYPT_ALGO);
         try {
-            mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+            mIpSecService.validateAlgorithms(config);
             fail("Expected exception; AEAD, auth and crypt algorithm specified");
         } catch (IllegalArgumentException expected) {
         }
     }
 
     @Test
-    public void testDeleteInvalidTransportModeTransform() throws Exception {
+    public void testDeleteInvalidTransform() throws Exception {
         try {
-            mIpSecService.deleteTransportModeTransform(1);
+            mIpSecService.deleteTransform(1);
             fail("IllegalArgumentException not thrown");
         } catch (IllegalArgumentException e) {
         }
@@ -434,7 +423,7 @@
     @Test
     public void testRemoveTransportModeTransform() throws Exception {
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
-        mIpSecService.removeTransportModeTransform(pfd, 1);
+        mIpSecService.removeTransportModeTransforms(pfd);
 
         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
     }
@@ -447,7 +436,7 @@
             try {
                 IpSecSpiResponse spiResp =
                         mIpSecService.allocateSecurityParameterIndex(
-                                IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder());
+                                address, DROID_SPI, new Binder());
                 fail("Invalid address was passed through IpSecService validation: " + address);
             } catch (IllegalArgumentException e) {
             } catch (Exception e) {
@@ -519,7 +508,6 @@
         // tracks the resource ID.
         when(mMockNetd.ipSecAllocateSpi(
                         anyInt(),
-                        eq(IpSecTransform.DIRECTION_OUT),
                         anyString(),
                         eq(InetAddress.getLoopbackAddress().getHostAddress()),
                         anyInt()))
@@ -528,7 +516,6 @@
         for (int i = 0; i < MAX_NUM_SPIS; i++) {
             IpSecSpiResponse newSpi =
                     mIpSecService.allocateSecurityParameterIndex(
-                            0x1,
                             InetAddress.getLoopbackAddress().getHostAddress(),
                             DROID_SPI + i,
                             new Binder());
@@ -544,7 +531,6 @@
         // Try to reserve one more SPI, and should fail.
         IpSecSpiResponse extraSpi =
                 mIpSecService.allocateSecurityParameterIndex(
-                        0x1,
                         InetAddress.getLoopbackAddress().getHostAddress(),
                         DROID_SPI + MAX_NUM_SPIS,
                         new Binder());
@@ -558,7 +544,6 @@
         // Should successfully reserve one more spi.
         extraSpi =
                 mIpSecService.allocateSecurityParameterIndex(
-                        0x1,
                         InetAddress.getLoopbackAddress().getHostAddress(),
                         DROID_SPI + MAX_NUM_SPIS,
                         new Binder());
@@ -650,4 +635,25 @@
         verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid()));
         mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
     }
+
+    @Test
+    public void testReserveNetId() {
+        int start = mIpSecService.TUN_INTF_NETID_START;
+        for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
+            assertEquals(start + i, mIpSecService.reserveNetId());
+        }
+
+        // Check that resource exhaustion triggers an exception
+        try {
+            mIpSecService.reserveNetId();
+            fail("Did not throw error for all netIds reserved");
+        } catch (IllegalStateException expected) {
+        }
+
+        // Now release one and try again
+        int releasedNetId =
+                mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
+        mIpSecService.releaseNetId(releasedNetId);
+        assertEquals(releasedNetId, mIpSecService.reserveNetId());
+    }
 }
diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
index 2ac73db..56a075b 100644
--- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
@@ -99,8 +99,12 @@
 
     @After
     public void tearDown() throws Exception {
-        if (mSocket != null) mSocket.close();
-        if (mServerSocket != null) mServerSocket.close();
+        mNMService.shutdown();
+        // Once NetworkManagementService#shutdown() actually does something and shutdowns
+        // the underlying NativeDaemonConnector, the block below should be uncommented.
+        // if (mOutputStream != null) mOutputStream.close();
+        // if (mSocket != null) mSocket.close();
+        // if (mServerSocket != null) mServerSocket.close();
     }
 
     /**
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 10d6deb..8359fe2 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -632,6 +632,7 @@
         when(nai.getCurrentScore()).thenReturn(score);
         nai.linkProperties = new LinkProperties();
         nai.networkCapabilities = new NetworkCapabilities();
+        nai.lastValidated = true;
         for (int t : BitUtils.unpackBits(transports)) {
             nai.networkCapabilities.addTransportType(t);
         }
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index c29363c..1dbf9b2 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -22,6 +22,7 @@
 import static android.content.pm.UserInfo.FLAG_RESTRICTED;
 import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -436,11 +437,13 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addCapability(NET_CAPABILITY_NOT_METERED)
+                .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                 .setLinkDownstreamBandwidthKbps(10));
         networks.put(wifi, new NetworkCapabilities()
                 .addTransportType(TRANSPORT_WIFI)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addCapability(NET_CAPABILITY_NOT_ROAMING)
+                .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                 .setLinkUpstreamBandwidthKbps(20));
         setMockedNetworks(networks);
 
@@ -454,6 +457,7 @@
         assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
         Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile }, caps);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
@@ -463,6 +467,7 @@
         assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
         Vpn.updateCapabilities(mConnectivityManager, new Network[] { wifi }, caps);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
@@ -472,6 +477,7 @@
         assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
         Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile, wifi }, caps);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
@@ -481,6 +487,7 @@
         assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
     }
 
     /**
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index 9c10264..da0a48a 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -195,7 +195,7 @@
         final NetworkStats.Entry entry = new NetworkStats.Entry();
         final NetworkIdentitySet identSet = new NetworkIdentitySet();
         identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                TEST_IMSI, null, false, true));
+                TEST_IMSI, null, false, true, true));
 
         int myUid = Process.myUid();
         int otherUidInSameUser = Process.myUid() + 1;
@@ -447,7 +447,7 @@
         final NetworkStatsCollection large = new NetworkStatsCollection(HOUR_IN_MILLIS);
         final NetworkIdentitySet ident = new NetworkIdentitySet();
         ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null,
-                false, true));
+                false, true, true));
         large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B,
                 new NetworkStats.Entry(12_730_893_164L, 1, 0, 0, 0));
 
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index 2be5dae..185c3eb 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -18,6 +18,8 @@
 
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.SET_DEFAULT;
@@ -224,6 +226,15 @@
         Mockito.verifyZeroInteractions(mockBinder);
     }
 
+    private NetworkIdentitySet makeTestIdentSet() {
+        NetworkIdentitySet identSet = new NetworkIdentitySet();
+        identSet.add(new NetworkIdentity(
+                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */,
+                true /* defaultNetwork */));
+        return identSet;
+    }
+
     @Test
     public void testUpdateStats_initialSample_doesNotNotify() throws Exception {
         DataUsageRequest inputRequest = new DataUsageRequest(
@@ -235,10 +246,7 @@
         assertTrue(Objects.equals(sTemplateImsi1, request.template));
         assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
 
-        NetworkIdentitySet identSet = new NetworkIdentitySet();
-        identSet.add(new NetworkIdentity(
-                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+        NetworkIdentitySet identSet = makeTestIdentSet();
         mActiveIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
@@ -263,10 +271,7 @@
         assertTrue(Objects.equals(sTemplateImsi1, request.template));
         assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
 
-        NetworkIdentitySet identSet = new NetworkIdentitySet();
-        identSet.add(new NetworkIdentity(
-                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+        NetworkIdentitySet identSet = makeTestIdentSet();
         mActiveIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
@@ -298,10 +303,7 @@
         assertTrue(Objects.equals(sTemplateImsi1, request.template));
         assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
 
-        NetworkIdentitySet identSet = new NetworkIdentitySet();
-        identSet.add(new NetworkIdentity(
-                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+        NetworkIdentitySet identSet = makeTestIdentSet();
         mActiveIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
@@ -334,17 +336,14 @@
         assertTrue(Objects.equals(sTemplateImsi1, request.template));
         assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
 
-        NetworkIdentitySet identSet = new NetworkIdentitySet();
-        identSet.add(new NetworkIdentity(
-                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+        NetworkIdentitySet identSet = makeTestIdentSet();
         mActiveUidIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                        BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+                        DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
                 VPN_INFO, TEST_START);
@@ -352,7 +351,8 @@
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                        BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+                        DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
+                        BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
                 VPN_INFO, TEST_START);
@@ -371,17 +371,14 @@
         assertTrue(Objects.equals(sTemplateImsi1, request.template));
         assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
 
-        NetworkIdentitySet identSet = new NetworkIdentitySet();
-        identSet.add(new NetworkIdentity(
-                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+        NetworkIdentitySet identSet = makeTestIdentSet();
         mActiveUidIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                        BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+                        DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
                 VPN_INFO, TEST_START);
@@ -389,7 +386,8 @@
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                        BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+                        DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
+                        BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
                 VPN_INFO, TEST_START);
@@ -407,17 +405,14 @@
         assertTrue(Objects.equals(sTemplateImsi1, request.template));
         assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
 
-        NetworkIdentitySet identSet = new NetworkIdentitySet();
-        identSet.add(new NetworkIdentity(
-                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+        NetworkIdentitySet identSet = makeTestIdentSet();
         mActiveUidIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                        BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+                        DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
                 VPN_INFO, TEST_START);
@@ -425,7 +420,8 @@
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                        BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+                        DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L,
+                        BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
                 VPN_INFO, TEST_START);
@@ -444,17 +440,14 @@
         assertTrue(Objects.equals(sTemplateImsi1, request.template));
         assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
 
-        NetworkIdentitySet identSet = new NetworkIdentitySet();
-        identSet.add(new NetworkIdentity(
-                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+        NetworkIdentitySet identSet = makeTestIdentSet();
         mActiveUidIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
                 .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
-                        ROAMING_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+                        ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
                 VPN_INFO, TEST_START);
@@ -462,8 +455,8 @@
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
                 .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
-                        ROAMING_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES,
-                        2L, 0L);
+                        ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
+                        BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
                 VPN_INFO, TEST_START);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index ecc9932..47c3455 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -21,6 +21,9 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.METERED_ALL;
 import static android.net.NetworkStats.METERED_NO;
@@ -67,6 +70,7 @@
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkStatsSession;
 import android.net.LinkProperties;
+import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
@@ -136,6 +140,12 @@
     private static final int UID_BLUE = 1002;
     private static final int UID_GREEN = 1003;
 
+
+    private static final Network WIFI_NETWORK =  new Network(100);
+    private static final Network MOBILE_NETWORK =  new Network(101);
+    private static final Network[] NETWORKS_WIFI = new Network[]{ WIFI_NETWORK };
+    private static final Network[] NETWORKS_MOBILE = new Network[]{ MOBILE_NETWORK };
+
     private static final long WAIT_TIMEOUT = 2 * 1000;  // 2 secs
     private static final int INVALID_TYPE = -1;
 
@@ -231,7 +241,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_WIFI);
 
         // verify service has empty history for wifi
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -278,7 +288,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_WIFI);
 
         // verify service has empty history for wifi
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -306,10 +316,10 @@
         // verify service recorded history
         assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
         assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10);
-        assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, 512L, 4L, 256L,
-                2L, 4);
-        assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, 512L, 4L,
-                256L, 2L, 6);
+        assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 4);
+        assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 6);
         assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0);
 
 
@@ -331,10 +341,10 @@
         // after systemReady(), we should have historical stats loaded again
         assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
         assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10);
-        assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, 512L, 4L, 256L,
-                2L, 4);
-        assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, 512L, 4L,
-                256L, 2L, 6);
+        assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 4);
+        assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 6);
         assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0);
 
     }
@@ -356,7 +366,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_WIFI);
 
 
         // modify some number on wifi, and trigger poll event
@@ -401,7 +411,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_MOBILE);
 
 
         // create some traffic on first network
@@ -439,7 +449,7 @@
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_MOBILE);
         forcePollAndWaitForIdle();
 
 
@@ -481,7 +491,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_WIFI);
 
 
         // create some traffic
@@ -543,7 +553,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_MOBILE);
 
 
         // create some traffic
@@ -573,7 +583,7 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_MOBILE);
         forcePollAndWaitForIdle();
 
 
@@ -605,7 +615,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_WIFI);
 
 
         // create some traffic for two apps
@@ -641,12 +651,12 @@
         NetworkStats stats = mSession.getSummaryForAllUid(
                 sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
         assertEquals(3, stats.size());
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 50L,
-                5L, 50L, 5L, 1);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 10L,
-                1L, 10L, 1L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 50L, 5L, 50L, 5L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 10L, 1L, 10L, 1L, 1);
         assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                2048L, 16L, 1024L, 8L, 0);
+                DEFAULT_NETWORK_YES, 2048L, 16L, 1024L, 8L, 0);
 
         // now verify that recent history only contains one uid
         final long currentTime = currentTimeMillis();
@@ -654,7 +664,7 @@
                 sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true);
         assertEquals(1, stats.size());
         assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                1024L, 8L, 512L, 4L, 0);
+                DEFAULT_NETWORK_YES, 1024L, 8L, 512L, 4L, 0);
     }
 
     @Test
@@ -667,7 +677,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_WIFI);
 
 
         // create some initial traffic
@@ -708,14 +718,14 @@
         final NetworkStats stats = mSession.getSummaryForAllUid(
                 sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
         assertEquals(4, stats.size());
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L,
-                2L, 128L, 2L, 1);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L,
-                1L, 64L, 1L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1);
         assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                32L, 2L, 32L, 2L, 1);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, METERED_NO, ROAMING_NO, 1L,
-                1L, 1L, 1L, 1);
+                DEFAULT_NETWORK_YES, 32L, 2L, 32L, 2L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 1L, 1L, 1L, 1L, 1);
     }
 
     @Test
@@ -728,7 +738,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_WIFI);
 
 
         // create some initial traffic
@@ -736,14 +746,14 @@
         expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
-        // Note that all traffic from NetworkManagementService is tagged as METERED_NO and
-        // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it
-        // on top by inspecting the iface properties.
+        // Note that all traffic from NetworkManagementService is tagged as METERED_NO, ROAMING_NO
+        // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer.
+        // We layer them on top by inspecting the iface properties.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L,
-                        2L, 128L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L,
-                        1L, 64L, 1L, 0L));
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
         forcePollAndWaitForIdle();
@@ -755,9 +765,9 @@
                 sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
         assertEquals(2, stats.size());
         assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
-                128L, 2L, 128L, 2L, 1);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 64L,
-                1L, 64L, 1L, 1);
+                DEFAULT_NETWORK_YES,  128L, 2L, 128L, 2L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1);
     }
 
     @Test
@@ -770,7 +780,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_MOBILE);
 
 
         // Create some traffic
@@ -783,9 +793,9 @@
         // on top by inspecting the iface properties.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
-                        128L, 2L, 128L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO, 64L,
-                        1L, 64L, 1L, 0L));
+                        DEFAULT_NETWORK_YES,  128L, 2L, 128L, 2L, 0L)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
+                        DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
         forcePollAndWaitForIdle();
 
         // verify service recorded history
@@ -796,9 +806,9 @@
                 sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
         assertEquals(2, stats.size());
         assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_YES,
-                128L, 2L, 128L, 2L, 0);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_YES, 64L,
-                1L, 64L, 1L, 0);
+                DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_YES,
+                DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0);
     }
 
     @Test
@@ -811,7 +821,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_MOBILE);
 
 
         // create some tethering traffic
@@ -856,7 +866,7 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectBandwidthControlCheck();
 
-        mService.forceUpdateIfaces();
+        mService.forceUpdateIfaces(NETWORKS_WIFI);
 
         // verify service has empty history for wifi
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -977,18 +987,18 @@
         // verify summary API
         final NetworkStats stats = mSession.getSummaryForNetwork(template, start, end);
         assertValues(stats, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
-                rxBytes, rxPackets, txBytes, txPackets, operations);
+                DEFAULT_NETWORK_ALL,  rxBytes, rxPackets, txBytes, txPackets, operations);
     }
 
     private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets,
             long txBytes, long txPackets, int operations) throws Exception {
-        assertUidTotal(template, uid, SET_ALL, METERED_ALL, ROAMING_ALL, rxBytes, rxPackets,
-                txBytes, txPackets, operations);
+        assertUidTotal(template, uid, SET_ALL, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL,
+                rxBytes, rxPackets, txBytes, txPackets, operations);
     }
 
     private void assertUidTotal(NetworkTemplate template, int uid, int set, int metered,
-            int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations)
-            throws Exception {
+            int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes,
+            long txPackets, int operations) throws Exception {
         // verify history API
         final NetworkStatsHistory history = mSession.getHistoryForUid(
                 template, uid, set, TAG_NONE, FIELD_ALL);
@@ -998,8 +1008,8 @@
         // verify summary API
         final NetworkStats stats = mSession.getSummaryForAllUid(
                 template, Long.MIN_VALUE, Long.MAX_VALUE, false);
-        assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, metered, roaming, rxBytes, rxPackets,
-                txBytes, txPackets, operations);
+        assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, metered, roaming, defaultNetwork,
+                rxBytes, rxPackets, txBytes, txPackets, operations);
     }
 
     private void expectSystemReady() throws Exception {
@@ -1097,8 +1107,8 @@
     }
 
     private static void assertValues(NetworkStats stats, String iface, int uid, int set,
-            int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes,
-            long txPackets, int operations) {
+            int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
+            long txBytes, long txPackets, int operations) {
         final NetworkStats.Entry entry = new NetworkStats.Entry();
         final int[] sets;
         if (set == SET_ALL) {
@@ -1121,12 +1131,22 @@
             meterings = new int[] { metered };
         }
 
+        final int[] defaultNetworks;
+        if (defaultNetwork == DEFAULT_NETWORK_ALL) {
+            defaultNetworks = new int[] { DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES,
+                    DEFAULT_NETWORK_NO };
+        } else {
+            defaultNetworks = new int[] { defaultNetwork };
+        }
+
         for (int s : sets) {
             for (int r : roamings) {
                 for (int m : meterings) {
-                    final int i = stats.findIndex(iface, uid, s, tag, m, r);
-                    if (i != -1) {
-                        entry.add(stats.getValues(i, null));
+                    for (int d : defaultNetworks) {
+                        final int i = stats.findIndex(iface, uid, s, tag, m, r, d);
+                        if (i != -1) {
+                            entry.add(stats.getValues(i, null));
+                        }
                     }
                 }
             }
@@ -1161,7 +1181,7 @@
         final NetworkCapabilities capabilities = new NetworkCapabilities();
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
-        return new NetworkState(info, prop, capabilities, null, null, TEST_SSID);
+        return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
     }
 
     private static NetworkState buildMobile3gState(String subscriberId) {
@@ -1178,7 +1198,7 @@
         final NetworkCapabilities capabilities = new NetworkCapabilities();
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
-        return new NetworkState(info, prop, capabilities, null, subscriberId, null);
+        return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
     }
 
     private static NetworkState buildMobile4gState(String iface) {
@@ -1189,7 +1209,7 @@
         final NetworkCapabilities capabilities = new NetworkCapabilities();
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
-        return new NetworkState(info, prop, capabilities, null, null, null);
+        return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, null, null);
     }
 
     private NetworkStats buildEmptyStats() {
diff --git a/tests/net/res/raw/net_dev_typical b/tests/net/res/raw/net_dev_typical
new file mode 100644
index 0000000..290bf03
--- /dev/null
+++ b/tests/net/res/raw/net_dev_typical
@@ -0,0 +1,8 @@
+Inter-|   Receive                                                |  Transmit
+ face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
+    lo:    8308     116    0    0    0     0          0         0     8308     116    0    0    0     0       0          0
+rmnet0: 1507570    2205    0    0    0     0          0         0   489339    2237    0    0    0     0       0          0
+  ifb0:   52454     151    0  151    0     0          0         0        0       0    0    0    0     0       0          0
+  ifb1:   52454     151    0  151    0     0          0         0        0       0    0    0    0     0       0          0
+  sit0:       0       0    0    0    0     0          0         0        0       0  148    0    0     0       0          0
+ip6tnl0:       0       0    0    0    0     0          0         0        0       0  151  151    0     0       0          0
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
index ee0e36c..81a0773 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
@@ -18,7 +18,6 @@
 import java.util.LinkedList;
 import java.util.List;
 import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.commons.TryCatchBlockSorter;
@@ -101,7 +100,7 @@
             try {
                 a.analyze(owner, mn);
             } catch (AnalyzerException e) {
-                e.printStackTrace();
+                throw new RuntimeException("Locked region code injection: " + e.getMessage(), e);
             }
             InsnList instructions = mn.instructions;