Merge "Prevent Closure of Underlying Socket FDs"
diff --git a/Android.bp b/Android.bp
index d4b15ca..facc741 100644
--- a/Android.bp
+++ b/Android.bp
@@ -248,6 +248,7 @@
         "core/java/android/service/euicc/IGetEuiccInfoCallback.aidl",
         "core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl",
         "core/java/android/service/euicc/IGetOtaStatusCallback.aidl",
+        "core/java/android/service/euicc/IOtaStatusChangedCallback.aidl",
         "core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl",
         "core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl",
         "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
@@ -509,7 +510,9 @@
         "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/IEuiccCardController.aidl",
         "telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl",
+        "telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl",
         "wifi/java/android/net/wifi/IWifiManager.aidl",
         "wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
         "wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl",
@@ -595,6 +598,7 @@
         "android.hardware.vibrator-V1.0-java-constants",
         "android.hardware.vibrator-V1.1-java-constants",
         "android.hardware.wifi-V1.0-java-constants",
+        "android.hardware.radio-V1.0-java",
     ],
 
     // Loaded with System.loadLibrary by android.view.textclassifier
diff --git a/Android.mk b/Android.mk
index 9877557..cf6aa2a 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) \
   )
 
@@ -698,4 +698,4 @@
 include $(call first-makefiles-under,$(LOCAL_PATH))
 endif
 
-endif # ANDROID_BUILD_EMBEDDED
+endif # ANDROID_BUILD_EMBEDDED
\ No newline at end of file
diff --git a/api/current.txt b/api/current.txt
index 50e93b8..bfed6d2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19873,6 +19873,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 +19964,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();
@@ -25906,10 +25939,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();
@@ -25919,7 +25952,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 {
@@ -37414,6 +37446,7 @@
 
   public class CarrierIdentifier implements android.os.Parcelable {
     ctor public CarrierIdentifier(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    ctor public CarrierIdentifier(byte[], java.lang.String, java.lang.String);
     method public int describeContents();
     method public java.lang.String getGid1();
     method public java.lang.String getGid2();
@@ -40095,8 +40128,13 @@
     field public static final java.lang.String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
   }
 
-  public final class CellIdentityCdma implements android.os.Parcelable {
+  public abstract class CellIdentity implements android.os.Parcelable {
     method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.CellIdentity> CREATOR;
+  }
+
+  public final class CellIdentityCdma extends android.telephony.CellIdentity {
     method public int getBasestationId();
     method public int getLatitude();
     method public int getLongitude();
@@ -40108,8 +40146,7 @@
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
   }
 
-  public final class CellIdentityGsm implements android.os.Parcelable {
-    method public int describeContents();
+  public final class CellIdentityGsm extends android.telephony.CellIdentity {
     method public int getArfcn();
     method public int getBsic();
     method public int getCid();
@@ -40126,8 +40163,7 @@
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityGsm> CREATOR;
   }
 
-  public final class CellIdentityLte implements android.os.Parcelable {
-    method public int describeContents();
+  public final class CellIdentityLte extends android.telephony.CellIdentity {
     method public int getCi();
     method public int getEarfcn();
     method public deprecated int getMcc();
@@ -40143,8 +40179,17 @@
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityLte> CREATOR;
   }
 
-  public final class CellIdentityWcdma implements android.os.Parcelable {
-    method public int describeContents();
+  public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
+    method public int getCid();
+    method public int getCpid();
+    method public int getLac();
+    method public java.lang.String getMccStr();
+    method public java.lang.String getMncStr();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR;
+  }
+
+  public final class CellIdentityWcdma extends android.telephony.CellIdentity {
     method public int getCid();
     method public int getLac();
     method public deprecated int getMcc();
@@ -40445,6 +40490,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
@@ -40456,6 +40502,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 {
@@ -40921,6 +40968,78 @@
 
 }
 
+package android.telephony.data {
+
+  public class ApnSetting implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getApnName();
+    method public int getAuthType();
+    method public java.lang.String getEntryName();
+    method public int getId();
+    method public int getMmsPort();
+    method public java.net.InetAddress getMmsProxy();
+    method public java.net.URL getMmsc();
+    method public java.lang.String getMvnoType();
+    method public java.lang.String getOperatorNumeric();
+    method public java.lang.String getPassword();
+    method public int getPort();
+    method public java.lang.String getProtocol();
+    method public java.net.InetAddress getProxy();
+    method public java.lang.String getRoamingProtocol();
+    method public java.util.List<java.lang.String> getTypes();
+    method public java.lang.String getUser();
+    method public boolean isEnabled();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int AUTH_TYPE_CHAP = 2; // 0x2
+    field public static final int AUTH_TYPE_NONE = 0; // 0x0
+    field public static final int AUTH_TYPE_PAP = 1; // 0x1
+    field public static final int AUTH_TYPE_PAP_OR_CHAP = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.telephony.data.ApnSetting> CREATOR;
+    field public static final java.lang.String MVNO_TYPE_GID = "gid";
+    field public static final java.lang.String MVNO_TYPE_ICCID = "iccid";
+    field public static final java.lang.String MVNO_TYPE_IMSI = "imsi";
+    field public static final java.lang.String MVNO_TYPE_SPN = "spn";
+    field public static final java.lang.String PROTOCOL_IP = "IP";
+    field public static final java.lang.String PROTOCOL_IPV4V6 = "IPV4V6";
+    field public static final java.lang.String PROTOCOL_IPV6 = "IPV6";
+    field public static final java.lang.String PROTOCOL_PPP = "PPP";
+    field public static final java.lang.String TYPE_ALL = "*";
+    field public static final java.lang.String TYPE_CBS = "cbs";
+    field public static final java.lang.String TYPE_DEFAULT = "default";
+    field public static final java.lang.String TYPE_DUN = "dun";
+    field public static final java.lang.String TYPE_EMERGENCY = "emergency";
+    field public static final java.lang.String TYPE_FOTA = "fota";
+    field public static final java.lang.String TYPE_HIPRI = "hipri";
+    field public static final java.lang.String TYPE_IA = "ia";
+    field public static final java.lang.String TYPE_IMS = "ims";
+    field public static final java.lang.String TYPE_MMS = "mms";
+    field public static final java.lang.String TYPE_SUPL = "supl";
+  }
+
+  public static class ApnSetting.Builder {
+    ctor public ApnSetting.Builder();
+    method public android.telephony.data.ApnSetting build();
+    method public android.telephony.data.ApnSetting.Builder setApnName(java.lang.String);
+    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 setOperatorNumeric(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setPort(int);
+    method public android.telephony.data.ApnSetting.Builder setProtocol(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setProxy(java.net.InetAddress);
+    method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setTypes(java.util.List<java.lang.String>);
+    method public android.telephony.data.ApnSetting.Builder setUser(java.lang.String);
+  }
+
+}
+
 package android.telephony.gsm {
 
   public class GsmCellLocation extends android.telephony.CellLocation {
diff --git a/api/system-current.txt b/api/system-current.txt
index 2a757ef..aa84f32 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -628,6 +628,9 @@
     method public boolean isEncrypted();
     method public boolean removeBond();
     method public boolean setPhonebookAccessPermission(int);
+    field public static final int ACCESS_ALLOWED = 1; // 0x1
+    field public static final int ACCESS_REJECTED = 2; // 0x2
+    field public static final int ACCESS_UNKNOWN = 0; // 0x0
   }
 
   public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -636,6 +639,11 @@
     method public boolean setPriority(android.bluetooth.BluetoothDevice, int);
   }
 
+  public abstract interface BluetoothProfile {
+    field public static final int PRIORITY_OFF = 0; // 0x0
+    field public static final int PRIORITY_ON = 100; // 0x64
+  }
+
 }
 
 package android.bluetooth.le {
@@ -4068,11 +4076,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();
@@ -4115,17 +4123,6 @@
     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;
-  }
-
 }
 
 package android.telephony.ims {
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/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/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index c8d9839..f2160e1 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -268,4 +268,11 @@
      * @param token The IApplicationToken for the activity
      */
     public abstract void setFocusedActivity(IBinder token);
+
+    public interface ScreenObserver {
+        public void onAwakeStateChanged(boolean isAwake);
+        public void onKeyguardStateChanged(boolean isShowing);
+    }
+
+    public abstract void registerScreenObserver(ScreenObserver observer);
 }
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/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..044b780 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,7 @@
         autoStopProfiler = in.autoStopProfiler;
         streamingOutput = in.streamingOutput;
         agent = in.agent;
+        attachAgentDuringBind = in.attachAgentDuringBind;
     }
 
     /**
@@ -109,6 +120,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 +143,6 @@
         autoStopProfiler = in.readInt() != 0;
         streamingOutput = in.readInt() != 0;
         agent = in.readString();
+        attachAgentDuringBind = in.readBoolean();
     }
 }
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 8c47598..6ac15a5 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -71,6 +71,8 @@
 
     @GuardedBy("mLock")
     private Map<String, Object> mMap;
+    @GuardedBy("mLock")
+    private Throwable mThrowable;
 
     @GuardedBy("mLock")
     private int mDiskWritesInFlight = 0;
@@ -107,6 +109,7 @@
         mMode = mode;
         mLoaded = false;
         mMap = null;
+        mThrowable = null;
         startLoadFromDisk();
     }
 
@@ -139,13 +142,14 @@
 
         Map<String, Object> map = null;
         StructStat stat = null;
+        Throwable thrown = null;
         try {
             stat = Os.stat(mFile.getPath());
             if (mFile.canRead()) {
                 BufferedInputStream str = null;
                 try {
                     str = new BufferedInputStream(
-                            new FileInputStream(mFile), 16*1024);
+                            new FileInputStream(mFile), 16 * 1024);
                     map = (Map<String, Object>) XmlUtils.readMapXml(str);
                 } catch (Exception e) {
                     Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
@@ -154,19 +158,36 @@
                 }
             }
         } catch (ErrnoException e) {
-            /* ignore */
+            // An errno exception means the stat failed. Treat as empty/non-existing by
+            // ignoring.
+        } catch (Throwable t) {
+            thrown = t;
         }
 
         synchronized (mLock) {
             mLoaded = true;
-            if (map != null) {
-                mMap = map;
-                mStatTimestamp = stat.st_mtim;
-                mStatSize = stat.st_size;
-            } else {
-                mMap = new HashMap<>();
+            mThrowable = thrown;
+
+            // It's important that we always signal waiters, even if we'll make
+            // them fail with an exception. The try-finally is pretty wide, but
+            // better safe than sorry.
+            try {
+                if (thrown == null) {
+                    if (map != null) {
+                        mMap = map;
+                        mStatTimestamp = stat.st_mtim;
+                        mStatSize = stat.st_size;
+                    } else {
+                        mMap = new HashMap<>();
+                    }
+                }
+                // In case of a thrown exception, we retain the old map. That allows
+                // any open editors to commit and store updates.
+            } catch (Throwable t) {
+                mThrowable = t;
+            } finally {
+                mLock.notifyAll();
             }
-            mLock.notifyAll();
         }
     }
 
@@ -226,6 +247,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void awaitLoadedLocked() {
         if (!mLoaded) {
             // Raise an explicit StrictMode onReadFromDisk for this
@@ -239,6 +261,9 @@
             } catch (InterruptedException unused) {
             }
         }
+        if (mThrowable != null) {
+            throw new IllegalStateException(mThrowable);
+        }
     }
 
     @Override
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ab70f0e..97c6681 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -81,10 +81,10 @@
 import android.net.IpSecManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkScoreManager;
-import android.net.nsd.INsdManager;
-import android.net.nsd.NsdManager;
 import android.net.lowpan.ILowpanManager;
 import android.net.lowpan.LowpanManager;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
 import android.net.wifi.IRttManager;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.IWifiScanner;
@@ -130,6 +130,7 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.euicc.EuiccCardManager;
 import android.telephony.euicc.EuiccManager;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -504,6 +505,13 @@
                 return new EuiccManager(ctx.getOuterContext());
             }});
 
+        registerService(Context.EUICC_CARD_SERVICE, EuiccCardManager.class,
+                new CachedServiceFetcher<EuiccCardManager>() {
+                    @Override
+                    public EuiccCardManager createService(ContextImpl ctx) {
+                        return new EuiccCardManager(ctx.getOuterContext());
+                    }});
+
         registerService(Context.UI_MODE_SERVICE, UiModeManager.class,
                 new CachedServiceFetcher<UiModeManager>() {
             @Override
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index ad7a93c..9b736b7 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -618,6 +618,7 @@
      *
      * @hide
      */
+    @SystemApi
     public static final int ACCESS_UNKNOWN = 0;
 
     /**
@@ -626,6 +627,7 @@
      *
      * @hide
      */
+    @SystemApi
     public static final int ACCESS_ALLOWED = 1;
 
     /**
@@ -634,6 +636,7 @@
      *
      * @hide
      */
+    @SystemApi
     public static final int ACCESS_REJECTED = 2;
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index c94540a..a68f485 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -556,8 +556,8 @@
      * Set priority of the profile
      *
      * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or
-     * {@link #PRIORITY_OFF},
+     * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
+     * {@link BluetoothProfile#PRIORITY_OFF},
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
      * permission.
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 41cf809..0e2263f 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -19,6 +19,7 @@
 
 import android.Manifest;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 
 import java.util.List;
 
@@ -185,6 +186,7 @@
      *
      * @hide
      **/
+    @SystemApi
     public static final int PRIORITY_ON = 100;
 
     /**
@@ -193,6 +195,7 @@
      *
      * @hide
      **/
+    @SystemApi
     public static final int PRIORITY_OFF = 0;
 
     /**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7e2ac63..70087da 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3589,8 +3589,18 @@
     public static final String EUICC_SERVICE = "euicc_service";
 
     /**
-     * Use with {@link #getSystemService} to retrieve a
-     * {@link android.text.ClipboardManager} for accessing and modifying
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.telephony.euicc.EuiccCardManager} to access the device eUICC (embedded SIM).
+     *
+     * @see #getSystemService(String)
+     * @see android.telephony.euicc.EuiccCardManager
+     * TODO(b/35851809): Make this a SystemApi.
+     * @hide
+     */
+    public static final String EUICC_CARD_SERVICE = "euicc_card_service";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.content.ClipboardManager} for accessing and modifying
      * the contents of the global clipboard.
      *
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/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/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/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index a474b47..a5e1934 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -339,7 +339,8 @@
     /**
      * Configure name servers, search paths, and resolver parameters for the given network.
      */
-    void setDnsConfigurationForNetwork(int netId, in String[] servers, String domains);
+    void setDnsConfigurationForNetwork(int netId, in String[] servers, in String[] domains,
+            in int[] params, boolean useTls, String tlsHostname);
 
     void setFirewallEnabled(boolean enabled);
     boolean isFirewallEnabled();
diff --git a/core/java/android/os/Seccomp.java b/core/java/android/os/Seccomp.java
index 335e44b..f14e93f 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/java/android/os/Seccomp.java
@@ -20,6 +20,5 @@
  * @hide
  */
 public final class Seccomp {
-    public static native void setSystemServerPolicy();
-    public static native void setAppPolicy();
+    public static final native void setPolicy();
 }
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/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index b47e872..09bba4b 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -16,9 +16,14 @@
 
 package android.service.carrier;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.util.Objects;
+
 /**
  * Used to pass info to CarrierConfigService implementations so they can decide what values to
  * return.
@@ -40,13 +45,13 @@
 
     private String mMcc;
     private String mMnc;
-    private String mSpn;
-    private String mImsi;
-    private String mGid1;
-    private String mGid2;
+    private @Nullable String mSpn;
+    private @Nullable String mImsi;
+    private @Nullable String mGid1;
+    private @Nullable String mGid2;
 
-    public CarrierIdentifier(String mcc, String mnc, String spn, String imsi, String gid1,
-            String gid2) {
+    public CarrierIdentifier(String mcc, String mnc, @Nullable String spn, @Nullable String imsi,
+            @Nullable String gid1, @Nullable String gid2) {
         mMcc = mcc;
         mMnc = mnc;
         mSpn = spn;
@@ -55,6 +60,32 @@
         mGid2 = gid2;
     }
 
+    /**
+     * Creates a carrier identifier instance.
+     *
+     * @param mccMnc A 3-byte array as defined by 3GPP TS 24.008.
+     * @param gid1 The group identifier level 1.
+     * @param gid2 The group identifier level 2.
+     * @throws IllegalArgumentException If the length of {@code mccMnc} is not 3.
+     */
+    public CarrierIdentifier(byte[] mccMnc, @Nullable String gid1, @Nullable String gid2) {
+        if (mccMnc.length != 3) {
+            throw new IllegalArgumentException(
+                    "MCC & MNC must be set by a 3-byte array: byte[" + mccMnc.length + "]");
+        }
+        String hex = IccUtils.bytesToHexString(mccMnc);
+        mMcc = new String(new char[] {hex.charAt(1), hex.charAt(0), hex.charAt(3)});
+        if (hex.charAt(2) == 'F') {
+            mMnc = new String(new char[] {hex.charAt(5), hex.charAt(4)});
+        } else {
+            mMnc = new String(new char[] {hex.charAt(5), hex.charAt(4), hex.charAt(2)});
+        }
+        mGid1 = gid1;
+        mGid2 = gid2;
+        mSpn = null;
+        mImsi = null;
+    }
+
     /** @hide */
     public CarrierIdentifier(Parcel parcel) {
         readFromParcel(parcel);
@@ -71,26 +102,60 @@
     }
 
     /** Get the service provider name. */
+    @Nullable
     public String getSpn() {
         return mSpn;
     }
 
     /** Get the international mobile subscriber identity. */
+    @Nullable
     public String getImsi() {
         return mImsi;
     }
 
     /** Get the group identifier level 1. */
+    @Nullable
     public String getGid1() {
         return mGid1;
     }
 
     /** Get the group identifier level 2. */
+    @Nullable
     public String getGid2() {
         return mGid2;
     }
 
     @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        CarrierIdentifier that = (CarrierIdentifier) obj;
+        return Objects.equals(mMcc, that.mMcc)
+                && Objects.equals(mMnc, that.mMnc)
+                && Objects.equals(mSpn, that.mSpn)
+                && Objects.equals(mImsi, that.mImsi)
+                && Objects.equals(mGid1, that.mGid1)
+                && Objects.equals(mGid2, that.mGid2);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        result = 31 * result + Objects.hashCode(mMcc);
+        result = 31 * result + Objects.hashCode(mMnc);
+        result = 31 * result + Objects.hashCode(mSpn);
+        result = 31 * result + Objects.hashCode(mImsi);
+        result = 31 * result + Objects.hashCode(mGid1);
+        result = 31 * result + Objects.hashCode(mGid2);
+        return result;
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/core/java/android/service/euicc/EuiccProfileInfo.aidl
similarity index 81%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to core/java/android/service/euicc/EuiccProfileInfo.aidl
index d750363..321021b 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/core/java/android/service/euicc/EuiccProfileInfo.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,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.service.euicc;
 
-/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+parcelable EuiccProfileInfo;
diff --git a/core/java/android/service/euicc/EuiccProfileInfo.java b/core/java/android/service/euicc/EuiccProfileInfo.java
index ba6c9a2..8e752d1 100644
--- a/core/java/android/service/euicc/EuiccProfileInfo.java
+++ b/core/java/android/service/euicc/EuiccProfileInfo.java
@@ -15,12 +15,19 @@
  */
 package android.service.euicc;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.service.carrier.CarrierIdentifier;
 import android.telephony.UiccAccessRule;
 import android.text.TextUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
  * Information about an embedded profile (subscription) on an eUICC.
  *
@@ -30,18 +37,90 @@
  */
 public final class EuiccProfileInfo implements Parcelable {
 
+    /** Profile policy rules (bit mask) */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "POLICY_RULE_" }, value = {
+            POLICY_RULE_DO_NOT_DISABLE,
+            POLICY_RULE_DO_NOT_DELETE,
+            POLICY_RULE_DELETE_AFTER_DISABLING
+    })
+    public @interface PolicyRule {}
+    /** Once this profile is enabled, it cannot be disabled. */
+    public static final int POLICY_RULE_DO_NOT_DISABLE = 1;
+    /** This profile cannot be deleted. */
+    public static final int POLICY_RULE_DO_NOT_DELETE = 1 << 1;
+    /** This profile should be deleted after being disabled. */
+    public static final int POLICY_RULE_DELETE_AFTER_DISABLING = 1 << 2;
+
+    /** Class of the profile */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "PROFILE_CLASS_" }, value = {
+            PROFILE_CLASS_TESTING,
+            PROFILE_CLASS_PROVISIONING,
+            PROFILE_CLASS_OPERATIONAL,
+            PROFILE_CLASS_UNSET
+    })
+    public @interface ProfileClass {}
+    /** Testing profiles */
+    public static final int PROFILE_CLASS_TESTING = 0;
+    /** Provisioning profiles which are pre-loaded on eUICC */
+    public static final int PROFILE_CLASS_PROVISIONING = 1;
+    /** Operational profiles which can be pre-loaded or downloaded */
+    public static final int PROFILE_CLASS_OPERATIONAL = 2;
+    /**
+     * Profile class not set.
+     * @hide
+     */
+    public static final int PROFILE_CLASS_UNSET = -1;
+
+    /** State of the profile */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "PROFILE_STATE_" }, value = {
+            PROFILE_STATE_DISABLED,
+            PROFILE_STATE_ENABLED,
+            PROFILE_STATE_UNSET
+    })
+    public @interface ProfileState {}
+    /** Disabled profiles */
+    public static final int PROFILE_STATE_DISABLED = 0;
+    /** Enabled profile */
+    public static final int PROFILE_STATE_ENABLED = 1;
+    /**
+     * Profile state not set.
+     * @hide
+     */
+    public static final int PROFILE_STATE_UNSET = -1;
+
     /** The iccid of the subscription. */
     public final String iccid;
 
+    /** An optional nickname for the subscription. */
+    public final @Nullable String nickname;
+
+    /** The service provider name for the subscription. */
+    public final String serviceProviderName;
+
+    /** The profile name for the subscription. */
+    public final String profileName;
+
+    /** Profile class for the subscription. */
+    @ProfileClass public final int profileClass;
+
+    /** The profile state of the subscription. */
+    @ProfileState public final int state;
+
+    /** The operator Id of the subscription. */
+    public final CarrierIdentifier carrierIdentifier;
+
+    /** The policy rules of the subscription. */
+    @PolicyRule public final int policyRules;
+
     /**
      * Optional access rules defining which apps can manage this subscription. If unset, only the
      * platform can manage it.
      */
     public final @Nullable UiccAccessRule[] accessRules;
 
-    /** An optional nickname for the subscription. */
-    public final @Nullable String nickname;
-
     public static final Creator<EuiccProfileInfo> CREATOR = new Creator<EuiccProfileInfo>() {
         @Override
         public EuiccProfileInfo createFromParcel(Parcel in) {
@@ -54,6 +133,12 @@
         }
     };
 
+    // TODO(b/70292228): Remove this method when LPA can be updated.
+    /**
+     * @hide
+     * @deprecated - Do not use.
+     */
+    @Deprecated
     public EuiccProfileInfo(String iccid, @Nullable UiccAccessRule[] accessRules,
             @Nullable String nickname) {
         if (!TextUtils.isDigitsOnly(iccid)) {
@@ -62,23 +147,290 @@
         this.iccid = iccid;
         this.accessRules = accessRules;
         this.nickname = nickname;
+
+        this.serviceProviderName = null;
+        this.profileName = null;
+        this.profileClass = PROFILE_CLASS_UNSET;
+        this.state = PROFILE_CLASS_UNSET;
+        this.carrierIdentifier = null;
+        this.policyRules = 0;
     }
 
     private EuiccProfileInfo(Parcel in) {
         iccid = in.readString();
-        accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
         nickname = in.readString();
+        serviceProviderName = in.readString();
+        profileName = in.readString();
+        profileClass = in.readInt();
+        state = in.readInt();
+        byte exist = in.readByte();
+        if (exist == (byte) 1) {
+            carrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in);
+        } else {
+            carrierIdentifier = null;
+        }
+        policyRules = in.readInt();
+        accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(iccid);
-        dest.writeTypedArray(accessRules, flags);
         dest.writeString(nickname);
+        dest.writeString(serviceProviderName);
+        dest.writeString(profileName);
+        dest.writeInt(profileClass);
+        dest.writeInt(state);
+        if (carrierIdentifier != null) {
+            dest.writeByte((byte) 1);
+            carrierIdentifier.writeToParcel(dest, flags);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+        dest.writeInt(policyRules);
+        dest.writeTypedArray(accessRules, flags);
     }
 
     @Override
     public int describeContents() {
         return 0;
     }
+
+    /** The builder to build a new {@link EuiccProfileInfo} instance. */
+    public static final class Builder {
+        public String iccid;
+        public UiccAccessRule[] accessRules;
+        public String nickname;
+        public String serviceProviderName;
+        public String profileName;
+        @ProfileClass public int profileClass;
+        @ProfileState public int state;
+        public CarrierIdentifier carrierIdentifier;
+        @PolicyRule public int policyRules;
+
+        public Builder() {}
+
+        public Builder(EuiccProfileInfo baseProfile) {
+            iccid = baseProfile.iccid;
+            nickname = baseProfile.nickname;
+            serviceProviderName = baseProfile.serviceProviderName;
+            profileName = baseProfile.profileName;
+            profileClass = baseProfile.profileClass;
+            state = baseProfile.state;
+            carrierIdentifier = baseProfile.carrierIdentifier;
+            policyRules = baseProfile.policyRules;
+            accessRules = baseProfile.accessRules;
+        }
+
+        /** Builds the profile instance. */
+        public EuiccProfileInfo build() {
+            if (iccid == null) {
+                throw new IllegalStateException("ICCID must be set for a profile.");
+            }
+            return new EuiccProfileInfo(
+                    iccid,
+                    nickname,
+                    serviceProviderName,
+                    profileName,
+                    profileClass,
+                    state,
+                    carrierIdentifier,
+                    policyRules,
+                    accessRules);
+        }
+
+        /** Sets the iccId of the subscription. */
+        public Builder setIccid(String value) {
+            if (!TextUtils.isDigitsOnly(value)) {
+                throw new IllegalArgumentException("iccid contains invalid characters: " + value);
+            }
+            iccid = value;
+            return this;
+        }
+
+        /** Sets the nickname of the subscription. */
+        public Builder setNickname(String value) {
+            nickname = value;
+            return this;
+        }
+
+        /** Sets the service provider name of the subscription. */
+        public Builder setServiceProviderName(String value) {
+            serviceProviderName = value;
+            return this;
+        }
+
+        /** Sets the profile name of the subscription. */
+        public Builder setProfileName(String value) {
+            profileName = value;
+            return this;
+        }
+
+        /** Sets the profile class of the subscription. */
+        public Builder setProfileClass(@ProfileClass int value) {
+            profileClass = value;
+            return this;
+        }
+
+        /** Sets the state of the subscription. */
+        public Builder setState(@ProfileState int value) {
+            state = value;
+            return this;
+        }
+
+        /** Sets the carrier identifier of the subscription. */
+        public Builder setCarrierIdentifier(CarrierIdentifier value) {
+            carrierIdentifier = value;
+            return this;
+        }
+
+        /** Sets the policy rules of the subscription. */
+        public Builder setPolicyRules(@PolicyRule int value) {
+            policyRules = value;
+            return this;
+        }
+
+        /** Sets the access rules of the subscription. */
+        public Builder setUiccAccessRule(@Nullable UiccAccessRule[] value) {
+            accessRules = value;
+            return this;
+        }
+    }
+
+    private EuiccProfileInfo(
+            String iccid,
+            @Nullable String nickname,
+            String serviceProviderName,
+            String profileName,
+            @ProfileClass int profileClass,
+            @ProfileState int state,
+            CarrierIdentifier carrierIdentifier,
+            @PolicyRule int policyRules,
+            @Nullable UiccAccessRule[] accessRules) {
+        this.iccid = iccid;
+        this.nickname = nickname;
+        this.serviceProviderName = serviceProviderName;
+        this.profileName = profileName;
+        this.profileClass = profileClass;
+        this.state = state;
+        this.carrierIdentifier = carrierIdentifier;
+        this.policyRules = policyRules;
+        this.accessRules = accessRules;
+    }
+
+    /** Gets the ICCID string. */
+    public String getIccid() {
+        return iccid;
+    }
+
+    /** Gets the access rules. */
+    @Nullable
+    public UiccAccessRule[] getUiccAccessRules() {
+        return accessRules;
+    }
+
+    /** Gets the nickname. */
+    public String getNickname() {
+        return nickname;
+    }
+
+    /** Gets the service provider name. */
+    public String getServiceProviderName() {
+        return serviceProviderName;
+    }
+
+    /** Gets the profile name. */
+    public String getProfileName() {
+        return profileName;
+    }
+
+    /** Gets the profile class. */
+    @ProfileClass
+    public int getProfileClass() {
+        return profileClass;
+    }
+
+    /** Gets the state of the subscription. */
+    @ProfileState
+    public int getState() {
+        return state;
+    }
+
+    /** Gets the carrier identifier. */
+    public CarrierIdentifier getCarrierIdentifier() {
+        return carrierIdentifier;
+    }
+
+    /** Gets the policy rules. */
+    @PolicyRule
+    public int getPolicyRules() {
+        return policyRules;
+    }
+
+    /** Returns whether any policy rule exists. */
+    public boolean hasPolicyRules() {
+        return policyRules != 0;
+    }
+
+    /** Checks whether a certain policy rule exists. */
+    public boolean hasPolicyRule(@PolicyRule int policy) {
+        return (policyRules & policy) != 0;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        EuiccProfileInfo that = (EuiccProfileInfo) obj;
+        return Objects.equals(iccid, that.iccid)
+                && Objects.equals(nickname, that.nickname)
+                && Objects.equals(serviceProviderName, that.serviceProviderName)
+                && Objects.equals(profileName, that.profileName)
+                && profileClass == that.profileClass
+                && state == that.state
+                && Objects.equals(carrierIdentifier, that.carrierIdentifier)
+                && policyRules == that.policyRules
+                && Arrays.equals(accessRules, that.accessRules);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        result = 31 * result + Objects.hashCode(iccid);
+        result = 31 * result + Objects.hashCode(nickname);
+        result = 31 * result + Objects.hashCode(serviceProviderName);
+        result = 31 * result + Objects.hashCode(profileName);
+        result = 31 * result + profileClass;
+        result = 31 * result + state;
+        result = 31 * result + Objects.hashCode(carrierIdentifier);
+        result = 31 * result + policyRules;
+        result = 31 * result + Arrays.hashCode(accessRules);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "EuiccProfileInfo (nickname="
+                + nickname
+                + ", serviceProviderName="
+                + serviceProviderName
+                + ", profileName="
+                + profileName
+                + ", profileClass="
+                + profileClass
+                + ", state="
+                + state
+                + ", CarrierIdentifier="
+                + carrierIdentifier.toString()
+                + ", policyRules="
+                + policyRules
+                + ", accessRules="
+                + Arrays.toString(accessRules)
+                + ")";
+    }
 }
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index fb53007..be85800 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -193,6 +193,18 @@
     }
 
     /**
+     * Callback class for {@link #onStartOtaIfNecessary(int, OtaStatusChangedCallback)}
+     *
+     * The status of OTA which can be {@code android.telephony.euicc.EuiccManager#EUICC_OTA_}
+     *
+     * @see IEuiccService#startOtaIfNecessary
+     */
+    public interface OtaStatusChangedCallback {
+        /** Called when OTA status is changed. */
+        void onOtaStatusChanged(int status);
+    }
+
+    /**
      * Return the EID of the eUICC.
      *
      * @param slotId ID of the SIM slot being queried. This is currently not populated but is here
@@ -214,6 +226,16 @@
     public abstract @OtaStatus int onGetOtaStatus(int slotId);
 
     /**
+     * Perform OTA if current OS is not the latest one.
+     *
+     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
+     *     but is here to future-proof the APIs.
+     * @param statusChangedCallback Function called when OTA status changed.
+     */
+    public abstract void onStartOtaIfNecessary(
+            int slotId, OtaStatusChangedCallback statusChangedCallback);
+
+    /**
      * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription.
      *
      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
@@ -396,6 +418,26 @@
         }
 
         @Override
+        public void startOtaIfNecessary(
+                int slotId, IOtaStatusChangedCallback statusChangedCallback) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    EuiccService.this.onStartOtaIfNecessary(slotId, new OtaStatusChangedCallback() {
+                        @Override
+                        public void onOtaStatusChanged(int status) {
+                            try {
+                                statusChangedCallback.onOtaStatusChanged(status);
+                            } catch (RemoteException e) {
+                                // Can't communicate with the phone process; ignore.
+                            }
+                        }
+                    });
+                }
+            });
+        }
+
+        @Override
         public void getOtaStatus(int slotId, IGetOtaStatusCallback callback) {
             mExecutor.execute(new Runnable() {
                 @Override
diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/core/java/android/service/euicc/IEuiccService.aidl
index a24e5c3..45be527 100644
--- a/core/java/android/service/euicc/IEuiccService.aidl
+++ b/core/java/android/service/euicc/IEuiccService.aidl
@@ -25,6 +25,7 @@
 import android.service.euicc.IGetEuiccInfoCallback;
 import android.service.euicc.IGetEuiccProfileInfoListCallback;
 import android.service.euicc.IGetOtaStatusCallback;
+import android.service.euicc.IOtaStatusChangedCallback;
 import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback;
 import android.service.euicc.ISwitchToSubscriptionCallback;
 import android.service.euicc.IUpdateSubscriptionNicknameCallback;
@@ -39,6 +40,7 @@
             boolean forceDeactivateSim, in IGetDownloadableSubscriptionMetadataCallback callback);
     void getEid(int slotId, in IGetEidCallback callback);
     void getOtaStatus(int slotId, in IGetOtaStatusCallback callback);
+    void startOtaIfNecessary(int slotId, in IOtaStatusChangedCallback statusChangedCallback);
     void getEuiccProfileInfoList(int slotId, in IGetEuiccProfileInfoListCallback callback);
     void getDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim,
             in IGetDefaultDownloadableSubscriptionListCallback callback);
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/core/java/android/service/euicc/IOtaStatusChangedCallback.aidl
similarity index 76%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to core/java/android/service/euicc/IOtaStatusChangedCallback.aidl
index d750363..caec75f 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/core/java/android/service/euicc/IOtaStatusChangedCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 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.
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-/** @hide */
-package android.telephony.data;
+package android.service.euicc;
 
-parcelable InterfaceAddress;
+/** @hide */
+oneway interface IOtaStatusChangedCallback {
+    void onOtaStatusChanged(int status);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index ebebad2..3ee8b47 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -17,7 +17,6 @@
 package com.android.internal.os;
 
 
-import android.os.Seccomp;
 import android.os.Trace;
 import dalvik.system.ZygoteHooks;
 import android.system.ErrnoException;
@@ -156,9 +155,6 @@
      */
     public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
-        // Set system server specific seccomp policy.
-        Seccomp.setSystemServerPolicy();
-
         VM_HOOKS.preFork();
         // Resets nice priority for zygote process.
         resetNicePriority();
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 24c4a8d..6a87b1f 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -30,7 +30,6 @@
 import android.net.LocalSocket;
 import android.os.FactoryTest;
 import android.os.Process;
-import android.os.Seccomp;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.system.ErrnoException;
@@ -768,9 +767,6 @@
             Process.setArgV0(parsedArgs.niceName);
         }
 
-        // Set app specific seccomp policy.
-        Seccomp.setAppPolicy();
-
         // End of the postFork event.
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         if (parsedArgs.invokeWith != null) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 4016832..2be6212 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -782,6 +782,9 @@
             // Zygote process unmounts root storage spaces.
             Zygote.nativeUnmountStorageOnInit();
 
+            // Set seccomp policy
+            Seccomp.setPolicy();
+
             ZygoteHooks.stopZygoteNoThreadCreation();
 
             if (startSystemServer) {
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
index b9006e4..06e2a16 100644
--- a/core/jni/android_os_seccomp.cpp
+++ b/core/jni/android_os_seccomp.cpp
@@ -21,33 +21,20 @@
 
 #include "seccomp_policy.h"
 
-static void Seccomp_setSystemServerPolicy(JNIEnv* /*env*/) {
+static void Seccomp_setPolicy(JNIEnv* /*env*/) {
     if (security_getenforce() == 0) {
         ALOGI("seccomp disabled by setenforce 0");
         return;
     }
 
-    if (!set_system_seccomp_filter()) {
-        ALOGE("Failed to set seccomp policy - killing");
-        exit(1);
-    }
-}
-
-static void Seccomp_setAppPolicy(JNIEnv* /*env*/) {
-    if (security_getenforce() == 0) {
-        ALOGI("seccomp disabled by setenforce 0");
-        return;
-    }
-
-    if (!set_app_seccomp_filter()) {
+    if (!set_seccomp_filter()) {
         ALOGE("Failed to set seccomp policy - killing");
         exit(1);
     }
 }
 
 static const JNINativeMethod method_table[] = {
-    NATIVE_METHOD(Seccomp, setSystemServerPolicy, "()V"),
-    NATIVE_METHOD(Seccomp, setAppPolicy, "()V"),
+    NATIVE_METHOD(Seccomp, setPolicy, "()V"),
 };
 
 namespace android {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4b2ae06..fdda55b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -563,6 +563,9 @@
     <protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
     <protected-broadcast android:name="com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
 
+    <!-- Added in P -->
+    <protected-broadcast android:name="android.telephony.euicc.action.OTA_STATUS_CHANGED" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 960ad1b..e18265b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2557,6 +2557,13 @@
 
     <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,
+         or a single SIM device can have 1 phones/logical modems, but 2 physical slots (one eSIM
+         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 -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 438ed82..f1070de 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -445,6 +445,7 @@
   <java-symbol type="integer" name="config_keepPreloadsMinDays" />
   <java-symbol type="bool" name="config_hasPermanentDpad" />
   <java-symbol type="bool" name="config_useDefaultFocusHighlight" />
+  <java-symbol type="integer" name="config_num_physical_slots" />
 
   <java-symbol type="color" name="tab_indicator_text_v4" />
 
diff --git a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
new file mode 100644
index 0000000..1e3ddf3
--- /dev/null
+++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
@@ -0,0 +1,247 @@
+/*
+ * 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.
+ */
+package android.service.euicc;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.service.carrier.CarrierIdentifier;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.UiccAccessRule;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EuiccProfileInfoTest {
+    @Test
+    public void testWriteToParcel() {
+        EuiccProfileInfo p =
+                new EuiccProfileInfo.Builder()
+                        .setIccid("21430000000000006587")
+                        .setNickname("profile nickname")
+                        .setServiceProviderName("service provider")
+                        .setProfileName("profile name")
+                        .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL)
+                        .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+                        .setCarrierIdentifier(
+                                new CarrierIdentifier(
+                                        new byte[] {0x23, 0x45, 0x67},
+                                        "123",
+                                        "45"))
+                        .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+                        .setUiccAccessRule(
+                                new UiccAccessRule[] {
+                                        new UiccAccessRule(new byte[] {}, "package", 12345L)
+                                })
+                        .build();
+
+        Parcel parcel = Parcel.obtain();
+        assertTrue(parcel != null);
+        p.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        EuiccProfileInfo fromParcel = EuiccProfileInfo.CREATOR.createFromParcel(parcel);
+
+        assertEquals(p, fromParcel);
+    }
+
+    @Test
+    public void testWriteToParcelNullCarrierId() {
+        EuiccProfileInfo p =
+                new EuiccProfileInfo.Builder()
+                        .setIccid("21430000000000006587")
+                        .setNickname("profile nickname")
+                        .setServiceProviderName("service provider")
+                        .setProfileName("profile name")
+                        .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL)
+                        .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+                        .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+                        .setUiccAccessRule(
+                                new UiccAccessRule[] {
+                                        new UiccAccessRule(new byte[] {}, "package", 12345L)
+                                })
+                        .build();
+
+        Parcel parcel = Parcel.obtain();
+        assertTrue(parcel != null);
+        p.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        EuiccProfileInfo fromParcel = EuiccProfileInfo.CREATOR.createFromParcel(parcel);
+
+        assertEquals(p, fromParcel);
+    }
+
+    @Test
+    public void testBuilderAndGetters() {
+        EuiccProfileInfo p =
+                new EuiccProfileInfo.Builder()
+                        .setIccid("21430000000000006587")
+                        .setNickname("profile nickname")
+                        .setProfileName("profile name")
+                        .setServiceProviderName("service provider")
+                        .setCarrierIdentifier(
+                                new CarrierIdentifier(
+                                        new byte[] {0x23, 0x45, 0x67},
+                                        "123",
+                                        "45"))
+                        .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+                        .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL)
+                        .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+                        .setUiccAccessRule(
+                                new UiccAccessRule[] {
+                                        new UiccAccessRule(new byte[0], null, 0)
+                                })
+                        .build();
+
+        assertEquals("21430000000000006587", p.getIccid());
+        assertEquals("profile nickname", p.getNickname());
+        assertEquals("profile name", p.getProfileName());
+        assertEquals("service provider", p.getServiceProviderName());
+        assertEquals("325", p.getCarrierIdentifier().getMcc());
+        assertEquals("764", p.getCarrierIdentifier().getMnc());
+        assertEquals("123", p.getCarrierIdentifier().getGid1());
+        assertEquals("45", p.getCarrierIdentifier().getGid2());
+        assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, p.getState());
+        assertEquals(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL, p.getProfileClass());
+        assertEquals(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, p.getPolicyRules());
+        assertTrue(p.hasPolicyRules());
+        assertTrue(p.hasPolicyRule(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE));
+        assertFalse(p.hasPolicyRule(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE));
+        assertArrayEquals(
+                new UiccAccessRule[] {new UiccAccessRule(new byte[0], null, 0)},
+                p.getUiccAccessRules());
+    }
+
+    @Test
+    public void testBuilder_BasedOnAnotherProfile() {
+        EuiccProfileInfo p =
+                new EuiccProfileInfo.Builder()
+                        .setIccid("21430000000000006587")
+                        .setNickname("profile nickname")
+                        .setProfileName("profile name")
+                        .setServiceProviderName("service provider")
+                        .setCarrierIdentifier(
+                                new CarrierIdentifier(
+                                        new byte[] {0x23, 0x45, 0x67},
+                                        "123",
+                                        "45"))
+                        .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+                        .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL)
+                        .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+                        .setUiccAccessRule(
+                                new UiccAccessRule[] {
+                                        new UiccAccessRule(new byte[0], null, 0)
+                                })
+                        .build();
+
+        EuiccProfileInfo copied = new EuiccProfileInfo.Builder(p).build();
+
+        assertEquals(p, copied);
+        assertEquals(p.hashCode(), copied.hashCode());
+    }
+
+    @Test
+    public void testEqualsHashCode() {
+        EuiccProfileInfo p =
+                new EuiccProfileInfo.Builder()
+                        .setIccid("21430000000000006587")
+                        .setNickname("profile nickname")
+                        .setProfileName("profile name")
+                        .setServiceProviderName("service provider")
+                        .setCarrierIdentifier(
+                                new CarrierIdentifier(
+                                        new byte[] {0x23, 0x45, 0x67},
+                                        "123",
+                                        "45"))
+                        .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+                        .setProfileClass(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+                        .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+                        .setUiccAccessRule(
+                                new UiccAccessRule[] {
+                                        new UiccAccessRule(new byte[0], null, 0)
+                                })
+                        .build();
+
+        assertTrue(p.equals(p));
+        assertFalse(p.equals(new Object()));
+
+        EuiccProfileInfo t = null;
+        assertFalse(p.equals(t));
+
+        t = new EuiccProfileInfo.Builder(p).setIccid("21").build();
+        assertFalse(p.equals(t));
+        assertNotEquals(p.hashCode(), t.hashCode());
+
+        t = new EuiccProfileInfo.Builder(p).setNickname(null).build();
+        assertFalse(p.equals(t));
+        assertNotEquals(p.hashCode(), t.hashCode());
+
+        t = new EuiccProfileInfo.Builder(p).setProfileName(null).build();
+        assertFalse(p.equals(t));
+        assertNotEquals(p.hashCode(), t.hashCode());
+
+        t = new EuiccProfileInfo.Builder(p).setServiceProviderName(null).build();
+        assertFalse(p.equals(t));
+        assertNotEquals(p.hashCode(), t.hashCode());
+
+        t = new EuiccProfileInfo.Builder(p).setCarrierIdentifier(null).build();
+        assertFalse(p.equals(t));
+        assertNotEquals(p.hashCode(), t.hashCode());
+
+        t = new EuiccProfileInfo.Builder(p)
+                .setState(EuiccProfileInfo.PROFILE_STATE_DISABLED).build();
+        assertFalse(p.equals(t));
+        assertNotEquals(p.hashCode(), t.hashCode());
+
+        t = new EuiccProfileInfo.Builder(p)
+                .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_TESTING).build();
+        assertFalse(p.equals(t));
+        assertNotEquals(p.hashCode(), t.hashCode());
+
+        t = new EuiccProfileInfo.Builder(p).setPolicyRules(0).build();
+        assertFalse(p.equals(t));
+        assertNotEquals(p.hashCode(), t.hashCode());
+
+        t = new EuiccProfileInfo.Builder(p).setUiccAccessRule(null).build();
+        assertFalse(p.equals(t));
+        assertNotEquals(p.hashCode(), t.hashCode());
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testBuilderBuild_NoIccid() {
+        new EuiccProfileInfo.Builder().build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuilderSetOperatorMccMnc_Illegal() {
+        new EuiccProfileInfo.Builder()
+                .setCarrierIdentifier(new CarrierIdentifier(new byte[] {1, 2, 3, 4}, null, null));
+    }
+
+    @Test
+    public void testCreatorNewArray() {
+        EuiccProfileInfo[] profiles = EuiccProfileInfo.CREATOR.newArray(123);
+        assertEquals(123, profiles.length);
+    }
+}
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/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/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5228498..ed1e011 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -130,6 +130,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
+import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.LingerMonitor;
@@ -224,7 +225,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;
@@ -232,8 +237,6 @@
     // 0 is full bad, 100 is full good
     private int mDefaultInetConditionPublished = 0;
 
-    private int mNumDnsEntries;
-
     private boolean mTestMode;
     private static ConnectivityService sServiceInstance;
 
@@ -407,6 +410,7 @@
     final private InternalHandler mHandler;
     /** Handler used for incoming {@link NetworkStateTracker} events. */
     final private NetworkStateTrackerHandler mTrackerHandler;
+    private final DnsManager mDnsManager;
 
     private boolean mSystemReady;
     private Intent mInitialBroadcast;
@@ -857,6 +861,8 @@
         mMultinetworkPolicyTracker = createMultinetworkPolicyTracker(
                 mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
         mMultinetworkPolicyTracker.start();
+
+        mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
     }
 
     private Tethering makeTethering() {
@@ -972,9 +978,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();
@@ -1062,8 +1068,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);
+            }
         }
     }
 
@@ -1228,8 +1236,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();
@@ -1555,9 +1563,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);
@@ -1803,24 +1813,6 @@
         }
     }
 
-    private void flushVmDnsCache() {
-        /*
-         * Tell the VMs to toss their DNS caches
-         */
-        Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        /*
-         * Connectivity events can happen before boot has completed ...
-         */
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     @Override
     public int getRestoreDefaultNetworkDelay(int networkType) {
         String restoreDefaultNetworkDelayStr = mSystemProperties.get(
@@ -2488,6 +2480,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) {
@@ -2500,6 +2493,7 @@
                 mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
             }
         }
+
         mNetworkRequestInfoLogs.log("RELEASE " + nri);
         if (nri.request.isRequest()) {
             boolean wasKept = false;
@@ -3422,9 +3416,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);
@@ -3468,9 +3462,9 @@
      */
     @Override
     public ParcelFileDescriptor establishVpn(VpnConfig config) {
-        throwIfLockdownEnabled();
         int user = UserHandle.getUserId(Binder.getCallingUid());
         synchronized (mVpns) {
+            throwIfLockdownEnabled();
             return mVpns.get(user).establish(config);
         }
     }
@@ -3481,13 +3475,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);
         }
     }
@@ -3513,11 +3507,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));
@@ -3582,33 +3576,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;
@@ -3618,6 +3612,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;
@@ -3632,6 +3627,7 @@
         }
     }
 
+    @GuardedBy("mVpns")
     private void throwIfLockdownEnabled() {
         if (mLockdownEnabled) {
             throw new IllegalStateException("Unavailable in lockdown mode");
@@ -3679,12 +3675,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");
@@ -3860,9 +3856,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();
+            }
         }
     }
 
@@ -3899,11 +3895,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);
+            }
         }
     }
 
@@ -4558,41 +4556,17 @@
             return;  // no updating necessary
         }
 
+        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);
         try {
-            mNetd.setDnsConfigurationForNetwork(
-                    netId, NetworkUtils.makeStrings(dnses), newLp.getDomains());
+            mDnsManager.setDnsConfigurationForNetwork(
+                    netId, dnses, newLp.getDomains(), isDefaultNetwork);
         } catch (Exception e) {
             loge("Exception in setDnsConfigurationForNetwork: " + e);
         }
-        final NetworkAgentInfo defaultNai = getDefaultNetwork();
-        if (defaultNai != null && defaultNai.network.netId == netId) {
-            setDefaultDnsSystemProperties(dnses);
-        }
-        flushVmDnsCache();
-    }
-
-    private void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
-        int last = 0;
-        for (InetAddress dns : dnses) {
-            ++last;
-            setNetDnsProperty(last, dns.getHostAddress());
-        }
-        for (int i = last + 1; i <= mNumDnsEntries; ++i) {
-            setNetDnsProperty(i, "");
-        }
-        mNumDnsEntries = last;
-    }
-
-    private void setNetDnsProperty(int which, String value) {
-        final String key = "net.dns" + which;
-        // Log and forget errors setting unsupported properties.
-        try {
-            mSystemProperties.set(key, value);
-        } catch (Exception e) {
-            Log.e(TAG, "Error setting unsupported net.dns property: ", e);
-        }
     }
 
     private String getNetworkPermission(NetworkCapabilities nc) {
@@ -4607,51 +4581,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);
@@ -4663,11 +4653,10 @@
         final NetworkCapabilities prevNc;
         synchronized (nai) {
             prevNc = nai.networkCapabilities;
-            nai.networkCapabilities = networkCapabilities;
+            nai.networkCapabilities = newNc;
         }
 
-        if (nai.getCurrentScore() == oldScore &&
-                networkCapabilities.equalRequestableCapabilities(prevNc)) {
+        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
@@ -4683,15 +4672,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) {
@@ -4865,7 +4854,7 @@
         notifyLockdownVpn(newNetwork);
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
         updateTcpBufferSizes(newNetwork);
-        setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
+        mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
     }
 
     private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -5215,11 +5204,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();
+                }
             }
         }
     }
@@ -5449,28 +5440,28 @@
 
     @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) {
@@ -5530,31 +5521,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/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8a15ded..8f646e7 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.DUMP;
+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;
@@ -209,12 +210,6 @@
         public static final int StrictCleartext           = 617;
     }
 
-    /* Defaults for resolver parameters. */
-    public static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
-    public static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
-    public static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
-    public static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
-
     /**
      * String indicating a softap command.
      */
@@ -1768,6 +1763,8 @@
 
     @Override
     public boolean setDataSaverModeEnabled(boolean enable) {
+        mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG);
+
         if (DBG) Log.d(TAG, "setDataSaverMode: " + enable);
         synchronized (mQuotaLock) {
             if (mDataSaverMode == enable) {
@@ -1947,66 +1944,14 @@
     }
 
     @Override
-    public void setDnsConfigurationForNetwork(int netId, String[] servers, String domains) {
+    public void setDnsConfigurationForNetwork(int netId, String[] servers, String[] domains,
+                    int[] params, boolean useTls, String tlsHostname) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        final ContentResolver cr = mContext.getContentResolver();
-
-        int sampleValidity = Settings.Global.getInt(cr,
-                Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
-                DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
-        if (sampleValidity < 0 || sampleValidity > 65535) {
-            Slog.w(TAG, "Invalid sampleValidity=" + sampleValidity + ", using default=" +
-                    DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
-            sampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
-        }
-
-        int successThreshold = Settings.Global.getInt(cr,
-                Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
-                DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
-        if (successThreshold < 0 || successThreshold > 100) {
-            Slog.w(TAG, "Invalid successThreshold=" + successThreshold + ", using default=" +
-                    DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
-            successThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
-        }
-
-        int minSamples = Settings.Global.getInt(cr,
-                Settings.Global.DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
-        int maxSamples = Settings.Global.getInt(cr,
-                Settings.Global.DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
-        if (minSamples < 0 || minSamples > maxSamples || maxSamples > 64) {
-            Slog.w(TAG, "Invalid sample count (min, max)=(" + minSamples + ", " + maxSamples +
-                    "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
-                    DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
-            minSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
-            maxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
-        }
-
-        final String[] domainStrs = domains == null ? new String[0] : domains.split(" ");
-        final int[] params = { sampleValidity, successThreshold, minSamples, maxSamples };
-        final boolean useTls = shouldUseTls(cr);
-        // 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 = "";
         final String[] tlsFingerprints = new String[0];
         try {
-            mNetdService.setResolverConfiguration(netId, servers, domainStrs, params,
-                    useTls, tlsHostname, tlsFingerprints);
+            mNetdService.setResolverConfiguration(
+                    netId, servers, domains, params, useTls, tlsHostname, tlsFingerprints);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -2563,12 +2508,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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dce0c38..002381a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -200,6 +200,7 @@
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityManager.TaskThumbnailInfo;
 import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
 import android.app.ActivityManagerInternal.SleepToken;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
@@ -1548,6 +1549,8 @@
         }
     }
 
+    final List<ScreenObserver> mScreenObservers = new ArrayList<>();
+
     final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
     ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
 
@@ -1689,6 +1692,8 @@
     static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
     static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
     static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
+    static final int DISPATCH_SCREEN_AWAKE_MSG = 71;
+    static final int DISPATCH_SCREEN_KEYGUARD_MSG = 72;
     static final int START_USER_SWITCH_FG_MSG = 712;
     static final int NOTIFY_VR_KEYGUARD_MSG = 74;
 
@@ -2412,11 +2417,17 @@
                     }
                 }
             } break;
-            case NOTIFY_VR_SLEEPING_MSG: {
-                notifyVrManagerOfSleepState(msg.arg1 != 0);
+            case DISPATCH_SCREEN_AWAKE_MSG: {
+                final boolean isAwake = msg.arg1 != 0;
+                for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+                    mScreenObservers.get(i).onAwakeStateChanged(isAwake);
+                }
             } break;
-            case NOTIFY_VR_KEYGUARD_MSG: {
-                notifyVrManagerOfKeyguardState(msg.arg1 != 0);
+            case DISPATCH_SCREEN_KEYGUARD_MSG: {
+                final boolean isShowing = msg.arg1 != 0;
+                for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+                    mScreenObservers.get(i).onKeyguardStateChanged(isShowing);
+                }
             } break;
             case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
                 synchronized (ActivityManagerService.this) {
@@ -3260,32 +3271,6 @@
                 mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
     }
 
-    private void sendNotifyVrManagerOfSleepState(boolean isSleeping) {
-        mHandler.sendMessage(
-                mHandler.obtainMessage(NOTIFY_VR_SLEEPING_MSG, isSleeping ? 1 : 0, 0));
-    }
-
-    private void notifyVrManagerOfSleepState(boolean isSleeping) {
-        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-        if (vrService == null) {
-            return;
-        }
-        vrService.onSleepStateChanged(isSleeping);
-    }
-
-    private void sendNotifyVrManagerOfKeyguardState(boolean isShowing) {
-        mHandler.sendMessage(
-                mHandler.obtainMessage(NOTIFY_VR_KEYGUARD_MSG, isShowing ? 1 : 0, 0));
-    }
-
-    private void notifyVrManagerOfKeyguardState(boolean isShowing) {
-        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-        if (vrService == null) {
-            return;
-        }
-        vrService.onKeyguardStateChanged(isShowing);
-    }
-
     final void showAskCompatModeDialogLocked(ActivityRecord r) {
         Message msg = Message.obtain();
         msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -7027,15 +7012,22 @@
             }
 
             ProfilerInfo profilerInfo = null;
-            String agent = null;
+            String preBindAgent = 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;
+                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.attachAgentDuringBind) {
+                        preBindAgent = mProfilerInfo.agent;
+                    }
+                }
             } else if (app.instr != null && app.instr.mProfileFile != null) {
                 profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
-                        null);
+                        null, false);
             }
 
             boolean enableTrackAllocation = false;
@@ -7102,8 +7094,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");
@@ -12497,7 +12489,8 @@
             if (wasAwake != isAwake) {
                 // Also update state in a special way for running foreground services UI.
                 mServices.updateScreenStateLocked(isAwake);
-                sendNotifyVrManagerOfSleepState(!isAwake);
+                mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
+                        .sendToTarget();
             }
         }
     }
@@ -12650,7 +12643,9 @@
                 Binder.restoreCallingIdentity(ident);
             }
         }
-        sendNotifyVrManagerOfKeyguardState(showing);
+
+        mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, showing ? 1 : 0, 0)
+                .sendToTarget();
     }
 
     @Override
@@ -24258,6 +24253,11 @@
                 }
             }
         }
+
+        @Override
+        public void registerScreenObserver(ScreenObserver observer) {
+            mScreenObservers.add(observer);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 8488e52..a7ace21 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;
@@ -295,7 +296,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 +396,7 @@
                     }
                 }
                 profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop,
-                        mStreaming, mAgent);
+                        mStreaming, mAgent, mAttachAgentDuringBind);
             }
 
             pw.println("Starting: " + intent);
@@ -755,7 +770,7 @@
                 return -1;
             }
             profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming,
-                    null);
+                    null, false);
         }
 
         try {
@@ -2679,6 +2694,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/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
new file mode 100644
index 0000000..a4170ce
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -0,0 +1,226 @@
+/*
+ * 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;
+
+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.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
+import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
+import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
+import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
+import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkUtils;
+import android.os.Binder;
+import android.os.INetworkManagementService;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.server.connectivity.MockableSystemProperties;
+
+import java.net.InetAddress;
+import java.util.Collection;
+
+
+/**
+ * Encapsulate the management of DNS settings for networks.
+ *
+ * This class it NOT designed for concurrent access. Furthermore, all non-static
+ * methods MUST be called from ConnectivityService's thread.
+ *
+ * @hide
+ */
+public class DnsManager {
+    private static final String TAG = DnsManager.class.getSimpleName();
+
+    /* Defaults for resolver parameters. */
+    private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
+    private static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
+    private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
+    private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
+
+    private final Context mContext;
+    private final ContentResolver mContentResolver;
+    private final INetworkManagementService mNMS;
+    private final MockableSystemProperties mSystemProperties;
+
+    private int mNumDnsEntries;
+    private int mSampleValidity;
+    private int mSuccessThreshold;
+    private int mMinSamples;
+    private int mMaxSamples;
+    private String mPrivateDnsMode;
+    private String mPrivateDnsSpecifier;
+
+    public DnsManager(Context ctx, INetworkManagementService nms, MockableSystemProperties sp) {
+        mContext = ctx;
+        mContentResolver = mContext.getContentResolver();
+        mNMS = nms;
+        mSystemProperties = sp;
+
+        // 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 void setDnsConfigurationForNetwork(
+            int netId, Collection<InetAddress> servers, String domains, boolean isDefaultNetwork) {
+        updateParametersSettings();
+        updatePrivateDnsSettings();
+
+        final String[] serverStrs = NetworkUtils.makeStrings(servers);
+        final String[] domainStrs = (domains == null) ? new String[0] : domains.split(" ");
+        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 = "";
+        try {
+            mNMS.setDnsConfigurationForNetwork(
+                    netId, serverStrs, domainStrs, params, useTls, tlsHostname);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error setting DNS configuration: " + e);
+            return;
+        }
+
+        // TODO: netd should listen on [::1]:53 and proxy queries to the current
+        // 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);
+        flushVmDnsCache();
+    }
+
+    public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
+        int last = 0;
+        for (InetAddress dns : dnses) {
+            ++last;
+            setNetDnsProperty(last, dns.getHostAddress());
+        }
+        for (int i = last + 1; i <= mNumDnsEntries; ++i) {
+            setNetDnsProperty(i, "");
+        }
+        mNumDnsEntries = last;
+    }
+
+    private void flushVmDnsCache() {
+        /*
+         * Tell the VMs to toss their DNS caches
+         */
+        final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        /*
+         * Connectivity events can happen before boot has completed ...
+         */
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void updatePrivateDnsSettings() {
+        mPrivateDnsMode = getStringSetting(PRIVATE_DNS_MODE);
+        mPrivateDnsSpecifier = getStringSetting(PRIVATE_DNS_SPECIFIER);
+    }
+
+    private void updateParametersSettings() {
+        mSampleValidity = getIntSetting(
+                DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
+                DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
+        if (mSampleValidity < 0 || mSampleValidity > 65535) {
+            Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" +
+                    DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
+            mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
+        }
+
+        mSuccessThreshold = getIntSetting(
+                DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
+                DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
+        if (mSuccessThreshold < 0 || mSuccessThreshold > 100) {
+            Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" +
+                    DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
+            mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
+        }
+
+        mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
+        mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
+        if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) {
+            Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples +
+                    "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
+                    DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
+            mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
+            mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
+        }
+    }
+
+    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);
+    }
+
+    private void setNetDnsProperty(int which, String value) {
+        final String key = "net.dns" + which;
+        // Log and forget errors setting unsupported properties.
+        try {
+            mSystemProperties.set(key, value);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error setting unsupported net.dns property: ", e);
+        }
+    }
+
+    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);
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d1bab89..7b171b3 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -19,7 +19,6 @@
 import static android.hardware.usb.UsbManager.USB_CONFIGURED;
 import static android.hardware.usb.UsbManager.USB_CONNECTED;
 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
-import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -43,7 +42,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
@@ -53,9 +51,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
-import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
-import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
@@ -76,7 +72,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
-import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -85,8 +80,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.MessageUtils;
@@ -110,12 +103,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
 
 
 /**
@@ -193,8 +182,6 @@
     private int mLastNotificationId;
 
     private boolean mRndisEnabled;       // track the RNDIS function enabled state
-    private boolean mUsbTetherRequested; // true if USB tethering should be started
-                                         // when RNDIS is enabled
     // True iff. WiFi tethering should be started when soft AP is ready.
     private boolean mWifiTetherRequested;
 
@@ -867,33 +854,18 @@
             //
             // For more explanation, see b/62552150 .
             synchronized (Tethering.this.mPublicSync) {
-                // Always record the state of RNDIS.
-                // TODO: consider:
-                //     final boolean disconnected = !usbConnected;
-                //     if (disconnected) {
-                //         mRndisEnabled = false;
-                //         mUsbTetherRequested = false;
-                //         return;
-                //     }
-                //     final boolean configured = usbConnected && usbConfigured;
-                //     mRndisEnabled = configured ? rndisEnabled : false;
-                //     if (!configured) return;
-                mRndisEnabled = rndisEnabled;
-
-                if (usbConnected && !usbConfigured) {
-                    // Nothing to do here (only CONNECTED, not yet CONFIGURED).
-                    return;
-                }
-
-                // start tethering if we have a request pending
-                if (usbConfigured && mRndisEnabled && mUsbTetherRequested) {
+                if (!usbConnected && mRndisEnabled) {
+                    // Turn off tethering if it was enabled and there is a disconnect.
+                    tetherMatchingInterfaces(
+                            IControlsTethering.STATE_AVAILABLE,
+                            ConnectivityManager.TETHERING_USB);
+                } else if (usbConfigured && rndisEnabled) {
+                    // Tether if rndis is enabled and usb is configured.
                     tetherMatchingInterfaces(
                             IControlsTethering.STATE_TETHERED,
                             ConnectivityManager.TETHERING_USB);
                 }
-
-                // TODO: Figure out how to remove the need for this variable.
-                mUsbTetherRequested = false;
+                mRndisEnabled = usbConfigured && rndisEnabled;
             }
         }
 
@@ -1065,34 +1037,8 @@
     public int setUsbTethering(boolean enable) {
         if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
         UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
-
         synchronized (mPublicSync) {
-            if (enable) {
-                if (mRndisEnabled) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        tetherMatchingInterfaces(IControlsTethering.STATE_TETHERED,
-                                ConnectivityManager.TETHERING_USB);
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-                } else {
-                    mUsbTetherRequested = true;
-                    usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
-                }
-            } else {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    tetherMatchingInterfaces(IControlsTethering.STATE_AVAILABLE,
-                            ConnectivityManager.TETHERING_USB);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-                if (mRndisEnabled) {
-                    usbManager.setCurrentFunction(null, false);
-                }
-                mUsbTetherRequested = false;
-            }
+            usbManager.setCurrentFunction(enable ? UsbManager.USB_FUNCTION_RNDIS : null, false);
         }
         return ConnectivityManager.TETHER_ERROR_NO_ERROR;
     }
@@ -1149,7 +1095,7 @@
         if (!mForwardedDownstreams.isEmpty()) return true;
 
         synchronized (mPublicSync) {
-            return mUsbTetherRequested || mWifiTetherRequested;
+            return mWifiTetherRequested;
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index c7a4315..3c2d724 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -128,7 +128,7 @@
 
     // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on
     // the device idle whitelist during service launch and VPN bootstrap.
-    private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION = 60 * 1000;
+    private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS = 60 * 1000;
 
     // TODO: create separate trackers for each unique VPN to support
     // automated reconnection
@@ -183,10 +183,10 @@
     @GuardedBy("this")
     private Set<UidRange> mBlockedUsers = new ArraySet<>();
 
-    // Handle of user initiating VPN.
+    // Handle of the user initiating VPN.
     private final int mUserHandle;
 
-    // Listen to package remove and change event in this user
+    // Listen to package removal and change events (update/uninstall) for this user
     private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -197,14 +197,14 @@
             }
 
             synchronized (Vpn.this) {
-                // Avoid race that always-on package has been unset
+                // Avoid race where always-on package has been unset
                 if (!packageName.equals(getAlwaysOnPackage())) {
                     return;
                 }
 
                 final String action = intent.getAction();
-                Log.i(TAG, "Received broadcast " + action + " for always-on package " + packageName
-                        + " in user " + mUserHandle);
+                Log.i(TAG, "Received broadcast " + action + " for always-on VPN package "
+                        + packageName + " in user " + mUserHandle);
 
                 switch(action) {
                     case Intent.ACTION_PACKAGE_REPLACED:
@@ -248,7 +248,8 @@
             Log.wtf(TAG, "Problem registering observer", e);
         }
 
-        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
+        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE,
+                "" /* subtypeName */);
         mNetworkCapabilities = new NetworkCapabilities();
         mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
         mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
@@ -258,7 +259,7 @@
     }
 
     /**
-     * Set if this object is responsible for watching for {@link NetworkInfo}
+     * Set whether this object is responsible for watching for {@link NetworkInfo}
      * teardown. When {@code false}, teardown is handled externally by someone
      * else.
      */
@@ -481,7 +482,6 @@
     }
 
     private void unregisterPackageChangeReceiverLocked() {
-        // register previous intent filter
         if (mIsPackageIntentReceiverRegistered) {
             mContext.unregisterReceiver(mPackageIntentReceiver);
             mIsPackageIntentReceiverRegistered = false;
@@ -582,7 +582,7 @@
             DeviceIdleController.LocalService idleController =
                     LocalServices.getService(DeviceIdleController.LocalService.class);
             idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
-                    VPN_LAUNCH_IDLE_WHITELIST_DURATION, mUserHandle, false, "vpn");
+                    VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS, mUserHandle, false, "vpn");
 
             // Start the VPN service declared in the app's manifest.
             Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
@@ -612,9 +612,10 @@
      * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
      * it can be revoked by itself.
      *
-     * Note: when we added VPN pre-consent in http://ag/522961 the names oldPackage
-     * and newPackage become misleading, because when an app is pre-consented, we
-     * actually prepare oldPackage, not newPackage.
+     * Note: when we added VPN pre-consent in
+     * https://android.googlesource.com/platform/frameworks/base/+/0554260
+     * the names oldPackage and newPackage became misleading, because when
+     * an app is pre-consented, we actually prepare oldPackage, not newPackage.
      *
      * Their meanings actually are:
      *
@@ -630,7 +631,7 @@
      * @param oldPackage The package name of the old VPN application
      * @param newPackage The package name of the new VPN application
      *
-     * @return true if the operation is succeeded.
+     * @return true if the operation succeeded.
      */
     public synchronized boolean prepare(String oldPackage, String newPackage) {
         if (oldPackage != null) {
@@ -639,7 +640,7 @@
                 return false;
             }
 
-            // Package is not same or old package was reinstalled.
+            // Package is not the same or old package was reinstalled.
             if (!isCurrentPreparedPackage(oldPackage)) {
                 // The package doesn't match. We return false (to obtain user consent) unless the
                 // user has already consented to that VPN package.
@@ -861,8 +862,8 @@
 
         long token = Binder.clearCallingIdentity();
         try {
-            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
-                    mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
+            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
+                    mNetworkInfo, mNetworkCapabilities, lp, 0 /* score */, networkMisc) {
                             @Override
                             public void unwanted() {
                                 // We are user controlled, not driven by NetworkRequest.
@@ -936,7 +937,7 @@
             }
 
             ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
-                                                                        null, 0, mUserHandle);
+                    null, 0, mUserHandle);
             if (info == null) {
                 throw new SecurityException("Cannot find " + config.user);
             }
@@ -944,7 +945,7 @@
                 throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
             }
         } catch (RemoteException e) {
-                throw new SecurityException("Cannot find " + config.user);
+            throw new SecurityException("Cannot find " + config.user);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1337,7 +1338,7 @@
     }
 
     private void enforceControlPermissionOrInternalCaller() {
-        // Require caller to be either an application with CONTROL_VPN permission or a process
+        // Require the caller to be either an application with CONTROL_VPN permission or a process
         // in the system server.
         mContext.enforceCallingOrSelfPermission(Manifest.permission.CONTROL_VPN,
                 "Unauthorized Caller");
@@ -1417,7 +1418,7 @@
     }
 
     /**
-     * This method should only be called by ConnectivityService. Because it doesn't
+     * This method should only be called by ConnectivityService because it doesn't
      * have enough data to fill VpnInfo.primaryUnderlyingIface field.
      */
     public synchronized VpnInfo getVpnInfo() {
@@ -1768,7 +1769,7 @@
      * Bringing up a VPN connection takes time, and that is all this thread
      * does. Here we have plenty of time. The only thing we need to take
      * care of is responding to interruptions as soon as possible. Otherwise
-     * requests will be piled up. This can be done in a Handler as a state
+     * requests will pile up. This could be done in a Handler as a state
      * machine, but it is much easier to read in the current form.
      */
     private class LegacyVpnRunner extends Thread {
@@ -1781,7 +1782,7 @@
         private final AtomicInteger mOuterConnection =
                 new AtomicInteger(ConnectivityManager.TYPE_NONE);
 
-        private long mTimer = -1;
+        private long mBringupStartTime = -1;
 
         /**
          * Watch for the outer connection (passing in the constructor) going away.
@@ -1861,8 +1862,8 @@
             synchronized (TAG) {
                 Log.v(TAG, "Executing");
                 try {
-                    execute();
-                    monitorDaemons();
+                    bringup();
+                    waitForDaemonsToStop();
                     interrupted(); // Clear interrupt flag if execute called exit.
                 } catch (InterruptedException e) {
                 } finally {
@@ -1883,30 +1884,27 @@
             }
         }
 
-        private void checkpoint(boolean yield) throws InterruptedException {
+        private void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException {
             long now = SystemClock.elapsedRealtime();
-            if (mTimer == -1) {
-                mTimer = now;
-                Thread.sleep(1);
-            } else if (now - mTimer <= 60000) {
-                Thread.sleep(yield ? 200 : 1);
+            if (now - mBringupStartTime <= 60000) {
+                Thread.sleep(sleepLonger ? 200 : 1);
             } else {
                 updateState(DetailedState.FAILED, "checkpoint");
-                throw new IllegalStateException("Time is up");
+                throw new IllegalStateException("VPN bringup took too long");
             }
         }
 
-        private void execute() {
-            // Catch all exceptions so we can clean up few things.
+        private void bringup() {
+            // Catch all exceptions so we can clean up a few things.
             boolean initFinished = false;
             try {
                 // Initialize the timer.
-                checkpoint(false);
+                mBringupStartTime = SystemClock.elapsedRealtime();
 
                 // Wait for the daemons to stop.
                 for (String daemon : mDaemons) {
                     while (!SystemService.isStopped(daemon)) {
-                        checkpoint(true);
+                        checkInterruptAndDelay(true);
                     }
                 }
 
@@ -1943,7 +1941,7 @@
 
                     // Wait for the daemon to start.
                     while (!SystemService.isRunning(daemon)) {
-                        checkpoint(true);
+                        checkInterruptAndDelay(true);
                     }
 
                     // Create the control socket.
@@ -1959,7 +1957,7 @@
                         } catch (Exception e) {
                             // ignore
                         }
-                        checkpoint(true);
+                        checkInterruptAndDelay(true);
                     }
                     mSockets[i].setSoTimeout(500);
 
@@ -1973,7 +1971,7 @@
                         out.write(bytes.length >> 8);
                         out.write(bytes.length);
                         out.write(bytes);
-                        checkpoint(false);
+                        checkInterruptAndDelay(false);
                     }
                     out.write(0xFF);
                     out.write(0xFF);
@@ -1989,7 +1987,7 @@
                         } catch (Exception e) {
                             // ignore
                         }
-                        checkpoint(true);
+                        checkInterruptAndDelay(true);
                     }
                 }
 
@@ -2002,7 +2000,7 @@
                             throw new IllegalStateException(daemon + " is dead");
                         }
                     }
-                    checkpoint(true);
+                    checkInterruptAndDelay(true);
                 }
 
                 // Now we are connected. Read and parse the new state.
@@ -2058,8 +2056,8 @@
                     // Set the start time
                     mConfig.startTime = SystemClock.elapsedRealtime();
 
-                    // Check if the thread is interrupted while we are waiting.
-                    checkpoint(false);
+                    // Check if the thread was interrupted while we were waiting on the lock.
+                    checkInterruptAndDelay(false);
 
                     // Check if the interface is gone while we are waiting.
                     if (jniCheck(mConfig.interfaze) == 0) {
@@ -2082,10 +2080,11 @@
         }
 
         /**
-         * Monitor the daemons we started, moving to disconnected state if the
-         * underlying services fail.
+         * Check all daemons every two seconds. Return when one of them is stopped.
+         * The caller will move to the disconnected state when this function returns,
+         * which can happen if a daemon failed or if the VPN was torn down.
          */
-        private void monitorDaemons() throws InterruptedException{
+        private void waitForDaemonsToStop() throws InterruptedException {
             if (!mNetworkInfo.isConnected()) {
                 return;
             }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 17adb1a..2224913 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -30,6 +30,7 @@
 import android.net.ip.InterfaceController;
 import android.net.ip.RouterAdvertisementDaemon;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
+import android.net.util.InterfaceParams;
 import android.net.util.NetdService;
 import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
@@ -48,7 +49,6 @@
 
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
@@ -120,8 +120,7 @@
     private int mLastError;
     private int mServingMode;
     private String mMyUpstreamIfaceName;  // may change over time
-    private NetworkInterface mNetworkInterface;
-    private byte[] mHwAddr;
+    private InterfaceParams mInterfaceParams;
     // TODO: De-duplicate this with mLinkProperties above. Currently, these link
     // properties are those selected by the IPv6TetheringCoordinator and relayed
     // to us. By comparison, mLinkProperties contains the addresses and directly
@@ -247,31 +246,16 @@
     }
 
     private boolean startIPv6() {
-        // TODO: Refactor for testability (perhaps passing an android.system.Os
-        // instance and calling getifaddrs() directly).
-        try {
-            mNetworkInterface = NetworkInterface.getByName(mIfaceName);
-        } catch (SocketException e) {
-            mLog.e("Error looking up NetworkInterfaces: " + e);
-            stopIPv6();
-            return false;
-        }
-        if (mNetworkInterface == null) {
-            mLog.e("Failed to find NetworkInterface");
+        // TODO: Refactor for better testability.  This is one of the things
+        // that prohibits unittesting IPv6 tethering setup.
+        mInterfaceParams = InterfaceParams.getByName(mIfaceName);
+        if (mInterfaceParams == null) {
+            mLog.e("Failed to find InterfaceParams");
             stopIPv6();
             return false;
         }
 
-        try {
-            mHwAddr = mNetworkInterface.getHardwareAddress();
-        } catch (SocketException e) {
-            mLog.e("Failed to find hardware address: " + e);
-            stopIPv6();
-            return false;
-        }
-
-        final int ifindex = mNetworkInterface.getIndex();
-        mRaDaemon = new RouterAdvertisementDaemon(mIfaceName, ifindex, mHwAddr);
+        mRaDaemon = new RouterAdvertisementDaemon(mInterfaceParams);
         if (!mRaDaemon.start()) {
             stopIPv6();
             return false;
@@ -281,8 +265,7 @@
     }
 
     private void stopIPv6() {
-        mNetworkInterface = null;
-        mHwAddr = null;
+        mInterfaceParams = null;
         setRaParams(null);
 
         if (mRaDaemon != null) {
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/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 72b2211..3aca330 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -24,8 +24,6 @@
 import android.app.ProgressDialog;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.IBluetoothManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -120,7 +118,6 @@
     private static String METRIC_AM = "shutdown_activity_manager";
     private static String METRIC_PM = "shutdown_package_manager";
     private static String METRIC_RADIOS = "shutdown_radios";
-    private static String METRIC_BT = "shutdown_bt";
     private static String METRIC_RADIO = "shutdown_radio";
     private static String METRIC_SM = "shutdown_storage_manager";
 
@@ -415,7 +412,7 @@
 
     /**
      * Makes sure we handle the shutdown gracefully.
-     * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
+     * Shuts off power regardless of radio state if the allotted time has passed.
      */
     public void run() {
         TimingsTraceLog shutdownTimingLog = newTimingsLog();
@@ -627,27 +624,10 @@
         Thread t = new Thread() {
             public void run() {
                 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
-                boolean bluetoothReadyForShutdown;
                 boolean radioOff;
 
                 final ITelephony phone =
                         ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
-                final IBluetoothManager bluetooth =
-                        IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
-                                BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
-
-                try {
-                    bluetoothReadyForShutdown = bluetooth == null ||
-                            bluetooth.getState() == BluetoothAdapter.STATE_OFF;
-                    if (!bluetoothReadyForShutdown) {
-                        Log.w(TAG, "Disabling Bluetooth...");
-                        metricStarted(METRIC_BT);
-                        bluetooth.disable(mContext.getPackageName(), false);  // disable but don't persist new state
-                    }
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
-                    bluetoothReadyForShutdown = true;
-                }
 
                 try {
                     radioOff = phone == null || !phone.needMobileRadioShutdown();
@@ -661,7 +641,7 @@
                     radioOff = true;
                 }
 
-                Log.i(TAG, "Waiting for Bluetooth and Radio...");
+                Log.i(TAG, "Waiting for Radio...");
 
                 long delay = endTime - SystemClock.elapsedRealtime();
                 while (delay > 0) {
@@ -672,25 +652,6 @@
                         sInstance.setRebootProgress(status, null);
                     }
 
-                    if (!bluetoothReadyForShutdown) {
-                        try {
-                          // BLE only mode can happen when BT is turned off
-                          // We will continue shutting down in such case
-                          bluetoothReadyForShutdown =
-                                  bluetooth.getState() == BluetoothAdapter.STATE_OFF ||
-                                  bluetooth.getState() == BluetoothAdapter.STATE_BLE_TURNING_OFF ||
-                                  bluetooth.getState() == BluetoothAdapter.STATE_BLE_ON;
-                        } catch (RemoteException ex) {
-                            Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
-                            bluetoothReadyForShutdown = true;
-                        }
-                        if (bluetoothReadyForShutdown) {
-                            Log.i(TAG, "Bluetooth turned off.");
-                            metricEnded(METRIC_BT);
-                            shutdownTimingsTraceLog
-                                    .logDuration("ShutdownBt", TRON_METRICS.get(METRIC_BT));
-                        }
-                    }
                     if (!radioOff) {
                         try {
                             radioOff = !phone.needMobileRadioShutdown();
@@ -706,8 +667,8 @@
                         }
                     }
 
-                    if (radioOff && bluetoothReadyForShutdown) {
-                        Log.i(TAG, "Radio and Bluetooth shutdown complete.");
+                    if (radioOff) {
+                        Log.i(TAG, "Radio shutdown complete.");
                         done[0] = true;
                         break;
                     }
@@ -724,7 +685,7 @@
         } catch (InterruptedException ex) {
         }
         if (!done[0]) {
-            Log.w(TAG, "Timed out waiting for Radio and Bluetooth shutdown.");
+            Log.w(TAG, "Timed out waiting for Radio shutdown.");
         }
     }
 
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 7b1e12e..35b6ad3 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -59,13 +59,6 @@
             int userId, int processId, @NonNull ComponentName calling);
 
     /**
-     * Set whether the system has acquired a sleep token.
-     *
-     * @param isAsleep is {@code true} if the device is asleep, or {@code false} otherwise.
-     */
-    public abstract void onSleepStateChanged(boolean isAsleep);
-
-    /**
      * Set whether the display used for VR output is on.
      *
      * @param isScreenOn is {@code true} if the display is on and can receive commands,
@@ -74,13 +67,6 @@
     public abstract void onScreenStateChanged(boolean isScreenOn);
 
     /**
-     * Set whether the keyguard is currently active/showing.
-     *
-     * @param isShowing is {@code true} if the keyguard is active/showing.
-     */
-    public abstract void onKeyguardStateChanged(boolean isShowing);
-
-    /**
      * Return NO_ERROR if the given package is installed on the device and enabled as a
      * VrListenerService for the given current user, or a negative error code indicating a failure.
      *
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b0fd248..56cacf4 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -19,6 +19,7 @@
 
 import android.Manifest;
 import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.INotificationManager;
@@ -104,7 +105,8 @@
  *
  * @hide
  */
-public class VrManagerService extends SystemService implements EnabledComponentChangeListener{
+public class VrManagerService extends SystemService
+        implements EnabledComponentChangeListener, ScreenObserver {
 
     public static final String TAG = "VrManagerService";
     static final boolean DBG = false;
@@ -231,15 +233,17 @@
         }
     }
 
-    private void setSleepState(boolean isAsleep) {
-        setSystemState(FLAG_AWAKE, !isAsleep);
-    }
-
     private void setScreenOn(boolean isScreenOn) {
         setSystemState(FLAG_SCREEN_ON, isScreenOn);
     }
 
-    private void setKeyguardShowing(boolean isShowing) {
+    @Override
+    public void onAwakeStateChanged(boolean isAwake) {
+        setSystemState(FLAG_AWAKE, isAwake);
+    }
+
+    @Override
+    public void onKeyguardStateChanged(boolean isShowing) {
         setSystemState(FLAG_KEYGUARD_UNLOCKED, !isShowing);
     }
 
@@ -675,21 +679,11 @@
         }
 
         @Override
-        public void onSleepStateChanged(boolean isAsleep) {
-            VrManagerService.this.setSleepState(isAsleep);
-        }
-
-        @Override
         public void onScreenStateChanged(boolean isScreenOn) {
             VrManagerService.this.setScreenOn(isScreenOn);
         }
 
         @Override
-        public void onKeyguardStateChanged(boolean isShowing) {
-            VrManagerService.this.setKeyguardShowing(isShowing);
-        }
-
-        @Override
         public boolean isCurrentVrListener(String packageName, int userId) {
             return VrManagerService.this.isCurrentVrListener(packageName, userId);
         }
@@ -740,6 +734,9 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            LocalServices.getService(ActivityManagerInternal.class)
+                    .registerScreenObserver(this);
+
             mNotificationManager = INotificationManager.Stub.asInterface(
                     ServiceManager.getService(Context.NOTIFICATION_SERVICE));
             synchronized (mLock) {
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 31a1abb..7d9736e 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -38,6 +38,7 @@
 import android.net.metrics.ApfStats;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.RaEvent;
+import android.net.util.InterfaceParams;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.PacketSocketAddress;
@@ -56,7 +57,6 @@
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
@@ -247,7 +247,7 @@
 
     private final ApfCapabilities mApfCapabilities;
     private final IpClient.Callback mIpClientCallback;
-    private final NetworkInterface mNetworkInterface;
+    private final InterfaceParams mInterfaceParams;
     private final IpConnectivityLog mMetricsLog;
 
     @VisibleForTesting
@@ -269,11 +269,11 @@
     private int mIPv4PrefixLength;
 
     @VisibleForTesting
-    ApfFilter(ApfConfiguration config, NetworkInterface networkInterface,
+    ApfFilter(ApfConfiguration config, InterfaceParams ifParams,
             IpClient.Callback ipClientCallback, IpConnectivityLog log) {
         mApfCapabilities = config.apfCapabilities;
         mIpClientCallback = ipClientCallback;
-        mNetworkInterface = networkInterface;
+        mInterfaceParams = ifParams;
         mMulticastFilter = config.multicastFilter;
         mDrop802_3Frames = config.ieee802_3Filter;
 
@@ -287,7 +287,7 @@
     }
 
     private void log(String s) {
-        Log.d(TAG, "(" + mNetworkInterface.getName() + "): " + s);
+        Log.d(TAG, "(" + mInterfaceParams.name + "): " + s);
     }
 
     @GuardedBy("this")
@@ -332,14 +332,14 @@
     void maybeStartFilter() {
         FileDescriptor socket;
         try {
-            mHardwareAddress = mNetworkInterface.getHardwareAddress();
+            mHardwareAddress = mInterfaceParams.macAddr.toByteArray();
             synchronized(this) {
                 // Install basic filters
                 installNewProgramLocked();
             }
             socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
-            PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6,
-                    mNetworkInterface.getIndex());
+            PacketSocketAddress addr = new PacketSocketAddress(
+                    (short) ETH_P_IPV6, mInterfaceParams.index);
             Os.bind(socket, addr);
             NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
         } catch(SocketException|ErrnoException e) {
@@ -1168,10 +1168,10 @@
      * filtering using APF programs.
      */
     public static ApfFilter maybeCreate(ApfConfiguration config,
-            NetworkInterface networkInterface, IpClient.Callback ipClientCallback) {
-        if (config == null) return null;
+            InterfaceParams ifParams, IpClient.Callback ipClientCallback) {
+        if (config == null || ifParams == null) return null;
         ApfCapabilities apfCapabilities =  config.apfCapabilities;
-        if (apfCapabilities == null || networkInterface == null) return null;
+        if (apfCapabilities == null) return null;
         if (apfCapabilities.apfVersionSupported == 0) return null;
         if (apfCapabilities.maximumApfProgramSize < 512) {
             Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize);
@@ -1186,7 +1186,7 @@
             Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
             return null;
         }
-        return new ApfFilter(config, networkInterface, ipClientCallback, new IpConnectivityLog());
+        return new ApfFilter(config, ifParams, ipClientCallback, new IpConnectivityLog());
     }
 
     public synchronized void shutdown() {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index ed78175..a956cef 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -34,6 +34,7 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.DhcpClientEvent;
 import android.net.metrics.DhcpErrorEvent;
+import android.net.util.InterfaceParams;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -50,7 +51,6 @@
 import java.io.IOException;
 import java.lang.Thread;
 import java.net.Inet4Address;
-import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
@@ -187,7 +187,8 @@
     private final String mIfaceName;
 
     private boolean mRegisteredForPreDhcpNotification;
-    private NetworkInterface mIface;
+    private InterfaceParams mIface;
+    // TODO: MacAddress-ify more of this class hierarchy.
     private byte[] mHwAddr;
     private PacketSocketAddress mInterfaceBroadcastAddr;
     private int mTransactionId;
@@ -221,8 +222,9 @@
         return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
     }
 
+    // TODO: Take an InterfaceParams instance instead of an interface name String.
     private DhcpClient(Context context, StateMachine controller, String iface) {
-        super(TAG);
+        super(TAG, controller.getHandler());
 
         mContext = context;
         mController = controller;
@@ -262,23 +264,23 @@
     }
 
     public static DhcpClient makeDhcpClient(
-            Context context, StateMachine controller, String intf) {
-        DhcpClient client = new DhcpClient(context, controller, intf);
+            Context context, StateMachine controller, InterfaceParams ifParams) {
+        DhcpClient client = new DhcpClient(context, controller, ifParams.name);
+        client.mIface = ifParams;
         client.start();
         return client;
     }
 
     private boolean initInterface() {
-        try {
-            mIface = NetworkInterface.getByName(mIfaceName);
-            mHwAddr = mIface.getHardwareAddress();
-            mInterfaceBroadcastAddr = new PacketSocketAddress(mIface.getIndex(),
-                    DhcpPacket.ETHER_BROADCAST);
-            return true;
-        } catch(SocketException | NullPointerException e) {
-            Log.e(TAG, "Can't determine ifindex or MAC address for " + mIfaceName, e);
+        if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
+        if (mIface == null) {
+            Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
             return false;
         }
+
+        mHwAddr = mIface.macAddr.toByteArray();
+        mInterfaceBroadcastAddr = new PacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
+        return true;
     }
 
     private void startNewTransaction() {
@@ -293,7 +295,7 @@
     private boolean initPacketSocket() {
         try {
             mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
-            PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.getIndex());
+            PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.index);
             Os.bind(mPacketSock, addr);
             NetworkUtils.attachDhcpFilter(mPacketSock);
         } catch(SocketException|ErrnoException e) {
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index 6cf4fa9a..e6ddbbc 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -21,6 +21,7 @@
 import android.net.NetworkUtils;
 import android.net.util.PacketReader;
 import android.net.util.ConnectivityPacketSummary;
+import android.net.util.InterfaceParams;
 import android.os.Handler;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -35,7 +36,6 @@
 import java.io.FileDescriptor;
 import java.io.InterruptedIOException;
 import java.io.IOException;
-import java.net.NetworkInterface;
 import java.net.SocketException;
 
 
@@ -69,24 +69,12 @@
     private boolean mRunning;
     private String mDisplayName;
 
-    public ConnectivityPacketTracker(Handler h, NetworkInterface netif, LocalLog log) {
-        final String ifname;
-        final int ifindex;
-        final byte[] hwaddr;
-        final int mtu;
+    public ConnectivityPacketTracker(Handler h, InterfaceParams ifParams, LocalLog log) {
+        if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
 
-        try {
-            ifname = netif.getName();
-            ifindex = netif.getIndex();
-            hwaddr = netif.getHardwareAddress();
-            mtu = netif.getMTU();
-        } catch (NullPointerException|SocketException e) {
-            throw new IllegalArgumentException("bad network interface", e);
-        }
-
-        mTag = TAG + "." + ifname;
+        mTag = TAG + "." + ifParams.name;
         mLog = log;
-        mPacketListener = new PacketListener(h, ifindex, hwaddr, mtu);
+        mPacketListener = new PacketListener(h, ifParams);
     }
 
     public void start(String displayName) {
@@ -102,13 +90,11 @@
     }
 
     private final class PacketListener extends PacketReader {
-        private final int mIfIndex;
-        private final byte mHwAddr[];
+        private final InterfaceParams mInterface;
 
-        PacketListener(Handler h, int ifindex, byte[] hwaddr, int mtu) {
-            super(h, mtu);
-            mIfIndex = ifindex;
-            mHwAddr = hwaddr;
+        PacketListener(Handler h, InterfaceParams ifParams) {
+            super(h, ifParams.defaultMtu);
+            mInterface = ifParams;
         }
 
         @Override
@@ -117,7 +103,7 @@
             try {
                 s = Os.socket(AF_PACKET, SOCK_RAW, 0);
                 NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
-                Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mIfIndex));
+                Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mInterface.index));
             } catch (ErrnoException | IOException e) {
                 logError("Failed to create packet tracking socket: ", e);
                 closeFd(s);
@@ -129,7 +115,7 @@
         @Override
         protected void handlePacket(byte[] recvbuf, int length) {
             final String summary = ConnectivityPacketSummary.summarize(
-                    mHwAddr, recvbuf, length);
+                    mInterface.macAddr, recvbuf, length);
             if (summary == null) return;
 
             if (DBG) Log.d(mTag, summary);
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index fdb366c..d3a97b3 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -35,6 +35,7 @@
 import android.net.dhcp.DhcpClient;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpManagerEvent;
+import android.net.util.InterfaceParams;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.NetdService;
 import android.net.util.NetworkConstants;
@@ -63,7 +64,6 @@
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -556,7 +556,7 @@
     private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
     private final InterfaceController mInterfaceCtrl;
 
-    private NetworkInterface mNetworkInterface;
+    private InterfaceParams mInterfaceParams;
 
     /**
      * Non-final member variables accessed only from within our StateMachine.
@@ -722,7 +722,12 @@
             return;
         }
 
-        getNetworkInterface();
+        mInterfaceParams = InterfaceParams.getByName(mInterfaceName);
+        if (mInterfaceParams == null) {
+            logError("Failed to find InterfaceParams for " + mInterfaceName);
+            // TODO: call doImmediateProvisioningFailure() with an error code
+            // indicating something like "interface not ready".
+        }
 
         mCallback.setNeighborDiscoveryOffload(true);
         sendMessage(CMD_START, new ProvisioningConfiguration(req));
@@ -858,7 +863,7 @@
     protected String getLogRecString(Message msg) {
         final String logLine = String.format(
                 "%s/%d %d %d %s [%s]",
-                mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(),
+                mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index,
                 msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
 
         final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
@@ -889,15 +894,6 @@
         mLog.log(msg);
     }
 
-    private void getNetworkInterface() {
-        try {
-            mNetworkInterface = NetworkInterface.getByName(mInterfaceName);
-        } catch (SocketException | NullPointerException e) {
-            // TODO: throw new IllegalStateException.
-            logError("Failed to get interface object: %s", e);
-        }
-    }
-
     // This needs to be called with care to ensure that our LinkProperties
     // are in sync with the actual LinkProperties of the interface. For example,
     // we should only call this if we know for sure that there are no IP addresses
@@ -1218,7 +1214,7 @@
             }
         } else {
             // Start DHCPv4.
-            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceName);
+            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
             mDhcpClient.registerForPreDhcpNotification();
             mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
         }
@@ -1245,7 +1241,7 @@
         try {
             mIpReachabilityMonitor = new IpReachabilityMonitor(
                     mContext,
-                    mInterfaceName,
+                    mInterfaceParams,
                     getHandler(),
                     mLog,
                     new IpReachabilityMonitor.Callback() {
@@ -1447,7 +1443,7 @@
                     mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
             apfConfig.ethTypeBlackList =
                     mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
-            mApfFilter = ApfFilter.maybeCreate(apfConfig, mNetworkInterface, mCallback);
+            mApfFilter = ApfFilter.maybeCreate(apfConfig, mInterfaceParams, mCallback);
             // TODO: investigate the effects of any multicast filtering racing/interfering with the
             // rest of this IP configuration startup.
             if (mApfFilter == null) {
@@ -1515,7 +1511,7 @@
         private ConnectivityPacketTracker createPacketTracker() {
             try {
                 return new ConnectivityPacketTracker(
-                        getHandler(), mNetworkInterface, mConnectivityPacketLog);
+                        getHandler(), mInterfaceParams, mConnectivityPacketLog);
             } catch (IllegalArgumentException e) {
                 return null;
             }
diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java
index 6807334..fc07aa1 100644
--- a/services/net/java/android/net/ip/IpNeighborMonitor.java
+++ b/services/net/java/android/net/ip/IpNeighborMonitor.java
@@ -16,7 +16,11 @@
 
 package android.net.ip;
 
-import android.net.netlink.NetlinkConstants;
+import static android.net.netlink.NetlinkConstants.hexify;
+import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
+import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
+
+import android.net.MacAddress;
 import android.net.netlink.NetlinkErrorMessage;
 import android.net.netlink.NetlinkMessage;
 import android.net.netlink.NetlinkSocket;
@@ -92,37 +96,35 @@
         final int ifindex;
         final InetAddress ip;
         final short nudState;
-        final byte[] linkLayerAddr;
+        final MacAddress macAddr;
 
         public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
-                short nudState, byte[] linkLayerAddr) {
+                short nudState, MacAddress macAddr) {
             this.elapsedMs = elapsedMs;
             this.msgType = msgType;
             this.ifindex = ifindex;
             this.ip = ip;
             this.nudState = nudState;
-            this.linkLayerAddr = linkLayerAddr;
+            this.macAddr = macAddr;
         }
 
         boolean isConnected() {
-            return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
-                   StructNdMsg.isNudStateConnected(nudState);
+            return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
         }
 
         boolean isValid() {
-            return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
-                   StructNdMsg.isNudStateValid(nudState);
+            return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
         }
 
         @Override
         public String toString() {
             final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
             return j.add("@" + elapsedMs)
-                    .add(NetlinkConstants.stringForNlMsgType(msgType))
+                    .add(stringForNlMsgType(msgType))
                     .add("if=" + ifindex)
                     .add(ip.getHostAddress())
                     .add(StructNdMsg.stringForNudState(nudState))
-                    .add("[" + NetlinkConstants.hexify(linkLayerAddr) + "]")
+                    .add("[" + macAddr + "]")
                     .toString();
         }
     }
@@ -183,7 +185,7 @@
             final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
             if (nlMsg == null || nlMsg.getHeader() == null) {
                 byteBuffer.position(position);
-                mLog.e("unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer));
+                mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
                 break;
             }
 
@@ -217,12 +219,13 @@
         final int ifindex = ndMsg.ndm_ifindex;
         final InetAddress destination = neighMsg.getDestination();
         final short nudState =
-                (msgType == NetlinkConstants.RTM_DELNEIGH)
+                (msgType == RTM_DELNEIGH)
                 ? StructNdMsg.NUD_NONE
                 : ndMsg.ndm_state;
 
         final NeighborEvent event = new NeighborEvent(
-                whenMs, msgType, ifindex, destination, nudState, neighMsg.getLinkLayerAddress());
+                whenMs, msgType, ifindex, destination, nudState,
+                getMacAddress(neighMsg.getLinkLayerAddress()));
 
         if (VDBG) {
             Log.d(TAG, neighMsg.toString());
@@ -233,4 +236,16 @@
 
         mConsumer.accept(event);
     }
+
+    private static MacAddress getMacAddress(byte[] linkLayerAddress) {
+        if (linkLayerAddress != null) {
+            try {
+                return MacAddress.fromBytes(linkLayerAddress);
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
+            }
+        }
+
+        return null;
+    }
 }
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index b31ffbb..7e02a28 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -26,6 +26,7 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpReachabilityEvent;
 import android.net.netlink.StructNdMsg;
+import android.net.util.InterfaceParams;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.SharedLog;
 import android.os.Handler;
@@ -46,9 +47,7 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
 import java.net.SocketAddress;
-import java.net.SocketException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -168,8 +167,7 @@
         }
     }
 
-    private final String mInterfaceName;
-    private final int mInterfaceIndex;
+    private final InterfaceParams mInterfaceParams;
     private final IpNeighborMonitor mIpNeighborMonitor;
     private final SharedLog mLog;
     private final Callback mCallback;
@@ -182,30 +180,25 @@
     private volatile long mLastProbeTimeMs;
 
     public IpReachabilityMonitor(
-            Context context, String ifName, Handler h, SharedLog log, Callback callback) {
-        this(context, ifName, h, log, callback, null);
-    }
-
-    public IpReachabilityMonitor(
-            Context context, String ifName, Handler h, SharedLog log, Callback callback,
+            Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
             MultinetworkPolicyTracker tracker) {
-        this(ifName, getInterfaceIndex(ifName), h, log, callback, tracker,
-                Dependencies.makeDefault(context, ifName));
+        this(ifParams, h, log, callback, tracker, Dependencies.makeDefault(context, ifParams.name));
     }
 
     @VisibleForTesting
-    IpReachabilityMonitor(String ifName, int ifIndex, Handler h, SharedLog log, Callback callback,
+    IpReachabilityMonitor(InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
             MultinetworkPolicyTracker tracker, Dependencies dependencies) {
-        mInterfaceName = ifName;
+        if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
+
+        mInterfaceParams = ifParams;
         mLog = log.forSubComponent(TAG);
         mCallback = callback;
         mMultinetworkPolicyTracker = tracker;
-        mInterfaceIndex = ifIndex;
         mDependencies = dependencies;
 
         mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
                 (NeighborEvent event) -> {
-                    if (mInterfaceIndex != event.ifindex) return;
+                    if (mInterfaceParams.index != event.ifindex) return;
                     if (!mNeighborWatchList.containsKey(event.ip)) return;
 
                     final NeighborEvent prev = mNeighborWatchList.put(event.ip, event);
@@ -241,7 +234,7 @@
 
     private String describeWatchList(String sep) {
         final StringBuilder sb = new StringBuilder();
-        sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}," + sep);
+        sb.append("iface{" + mInterfaceParams + "}," + sep);
         sb.append("ntable=[" + sep);
         String delimiter = "";
         for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
@@ -262,10 +255,10 @@
     }
 
     public void updateLinkProperties(LinkProperties lp) {
-        if (!mInterfaceName.equals(lp.getInterfaceName())) {
+        if (!mInterfaceParams.name.equals(lp.getInterfaceName())) {
             // TODO: figure out whether / how to cope with interface changes.
             Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() +
-                    "' does not match: " + mInterfaceName);
+                    "' does not match: " + mInterfaceParams.name);
             return;
         }
 
@@ -353,10 +346,10 @@
             mDependencies.acquireWakeLock(getProbeWakeLockDuration());
         }
 
-        for (InetAddress target : ipProbeList) {
-            final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceIndex, target);
+        for (InetAddress ip : ipProbeList) {
+            final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceParams.index, ip);
             mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
-                     target.getHostAddress(), rval));
+                     ip.getHostAddress(), rval));
             logEvent(IpReachabilityEvent.PROBE, rval);
         }
         mLastProbeTimeMs = SystemClock.elapsedRealtime();
@@ -378,22 +371,9 @@
         return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
     }
 
-    private static int getInterfaceIndex(String ifname) {
-        final NetworkInterface iface;
-        try {
-            iface = NetworkInterface.getByName(ifname);
-        } catch (SocketException e) {
-            throw new IllegalArgumentException("invalid interface '" + ifname + "': ", e);
-        }
-        if (iface == null) {
-            throw new IllegalArgumentException("NetworkInterface was null for " + ifname);
-        }
-        return iface.getIndex();
-    }
-
     private void logEvent(int probeType, int errorCode) {
         int eventType = probeType | (errorCode & 0xff);
-        mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType));
+        mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
     }
 
     private void logNudFailed(ProvisioningChange delta) {
@@ -401,6 +381,6 @@
         boolean isFromProbe = (duration < getProbeWakeLockDuration());
         boolean isProvisioningLost = (delta == ProvisioningChange.LOST_PROVISIONING);
         int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost);
-        mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType));
+        mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
     }
 }
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index cb3123c..49a1e79 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -25,6 +25,7 @@
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
+import android.net.util.InterfaceParams;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructGroupReq;
@@ -96,9 +97,7 @@
             (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
     };
 
-    private final String mIfName;
-    private final int mIfIndex;
-    private final byte[] mHwAddr;
+    private final InterfaceParams mInterface;
     private final InetSocketAddress mAllNodes;
 
     // This lock is to protect the RA from being updated while being
@@ -223,11 +222,9 @@
     }
 
 
-    public RouterAdvertisementDaemon(String ifname, int ifindex, byte[] hwaddr) {
-        mIfName = ifname;
-        mIfIndex = ifindex;
-        mHwAddr = hwaddr;
-        mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mIfIndex), 0);
+    public RouterAdvertisementDaemon(InterfaceParams ifParams) {
+        mInterface = ifParams;
+        mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0);
         mDeprecatedInfoTracker = new DeprecatedInfoTracker();
     }
 
@@ -279,7 +276,7 @@
 
         try {
             putHeader(ra, mRaParams != null && mRaParams.hasDefaultRoute);
-            putSlla(ra, mHwAddr);
+            putSlla(ra, mInterface.macAddr.toByteArray());
             mRaLength = ra.position();
 
             // https://tools.ietf.org/html/rfc5175#section-4 says:
@@ -579,9 +576,9 @@
             // Setting SNDTIMEO is purely for defensive purposes.
             Os.setsockoptTimeval(
                     mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(SEND_TIMEOUT_MS));
-            Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
+            Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name);
             NetworkUtils.protectFromVpn(mSocket);
-            NetworkUtils.setupRaSocket(mSocket, mIfIndex);
+            NetworkUtils.setupRaSocket(mSocket, mInterface.index);
         } catch (ErrnoException | IOException e) {
             Log.e(TAG, "Failed to create RA daemon socket: " + e);
             return false;
@@ -614,7 +611,7 @@
         final InetAddress destip = dest.getAddress();
         return (destip instanceof Inet6Address) &&
                 destip.isLinkLocalAddress() &&
-               (((Inet6Address) destip).getScopeId() == mIfIndex);
+               (((Inet6Address) destip).getScopeId() == mInterface.index);
     }
 
     private void maybeSendRA(InetSocketAddress dest) {
diff --git a/services/net/java/android/net/util/ConnectivityPacketSummary.java b/services/net/java/android/net/util/ConnectivityPacketSummary.java
index dae93af..4951400 100644
--- a/services/net/java/android/net/util/ConnectivityPacketSummary.java
+++ b/services/net/java/android/net/util/ConnectivityPacketSummary.java
@@ -17,6 +17,7 @@
 package android.net.util;
 
 import android.net.dhcp.DhcpPacket;
+import android.net.MacAddress;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
@@ -45,21 +46,20 @@
     private final ByteBuffer mPacket;
     private final String mSummary;
 
-    public static String summarize(byte[] hwaddr, byte[] buffer) {
+    public static String summarize(MacAddress hwaddr, byte[] buffer) {
         return summarize(hwaddr, buffer, buffer.length);
     }
 
     // Methods called herein perform some but by no means all error checking.
     // They may throw runtime exceptions on malformed packets.
-    public static String summarize(byte[] hwaddr, byte[] buffer, int length) {
-        if ((hwaddr == null) || (hwaddr.length != ETHER_ADDR_LEN)) return null;
-        if (buffer == null) return null;
+    public static String summarize(MacAddress macAddr, byte[] buffer, int length) {
+        if ((macAddr == null) || (buffer == null)) return null;
         length = Math.min(length, buffer.length);
-        return (new ConnectivityPacketSummary(hwaddr, buffer, length)).toString();
+        return (new ConnectivityPacketSummary(macAddr, buffer, length)).toString();
     }
 
-    private ConnectivityPacketSummary(byte[] hwaddr, byte[] buffer, int length) {
-        mHwAddr = hwaddr;
+    private ConnectivityPacketSummary(MacAddress macAddr, byte[] buffer, int length) {
+        mHwAddr = macAddr.toByteArray();
         mBytes = buffer;
         mLength = Math.min(length, mBytes.length);
         mPacket = ByteBuffer.wrap(mBytes, 0, mLength);
diff --git a/services/net/java/android/net/util/InterfaceParams.java b/services/net/java/android/net/util/InterfaceParams.java
new file mode 100644
index 0000000..a4b2fbb
--- /dev/null
+++ b/services/net/java/android/net/util/InterfaceParams.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+package android.net.util;
+
+import static android.net.MacAddress.ALL_ZEROS_ADDRESS;
+import static android.net.util.NetworkConstants.ETHER_MTU;
+import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.net.MacAddress;
+import android.text.TextUtils;
+
+import java.net.NetworkInterface;
+import java.net.SocketException;
+
+
+/**
+ * Encapsulate the interface parameters common to IpClient/IpServer components.
+ *
+ * Basically all java.net.NetworkInterface methods throw Exceptions. IpClient
+ * and IpServer (sub)components need most or all of this information at some
+ * point during their lifecycles, so pass only this simplified object around
+ * which can be created once when IpClient/IpServer are told to start.
+ *
+ * @hide
+ */
+public class InterfaceParams {
+    public final String name;
+    public final int index;
+    public final MacAddress macAddr;
+    public final int defaultMtu;
+
+    public static InterfaceParams getByName(String name) {
+        final NetworkInterface netif = getNetworkInterfaceByName(name);
+        if (netif == null) return null;
+
+        // Not all interfaces have MAC addresses, e.g. rmnet_data0.
+        final MacAddress macAddr = getMacAddress(netif);
+
+        try {
+            return new InterfaceParams(name, netif.getIndex(), macAddr, netif.getMTU());
+        } catch (IllegalArgumentException|SocketException e) {
+            return null;
+        }
+    }
+
+    public InterfaceParams(String name, int index, MacAddress macAddr) {
+        this(name, index, macAddr, ETHER_MTU);
+    }
+
+    public InterfaceParams(String name, int index, MacAddress macAddr, int defaultMtu) {
+        checkArgument((!TextUtils.isEmpty(name)), "impossible interface name");
+        checkArgument((index > 0), "invalid interface index");
+        this.name = name;
+        this.index = index;
+        this.macAddr = (macAddr != null) ? macAddr : ALL_ZEROS_ADDRESS;
+        this.defaultMtu = (defaultMtu > IPV6_MIN_MTU) ? defaultMtu : IPV6_MIN_MTU;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s/%d/%s/%d", name, index, macAddr, defaultMtu);
+    }
+
+    private static NetworkInterface getNetworkInterfaceByName(String name) {
+        try {
+            return NetworkInterface.getByName(name);
+        } catch (NullPointerException|SocketException e) {
+            return null;
+        }
+    }
+
+    private static MacAddress getMacAddress(NetworkInterface netif) {
+        try {
+            return MacAddress.fromBytes(netif.getHardwareAddress());
+        } catch (IllegalArgumentException|NullPointerException|SocketException e) {
+            return null;
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index c668100..4a7072d 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;
@@ -38,6 +42,7 @@
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
 import android.os.BatteryManager;
+import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
@@ -60,6 +65,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;
@@ -75,7 +81,7 @@
 /**
  * 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;
@@ -97,6 +103,12 @@
     private static final String USB_STATE_PROPERTY = "sys.usb.state";
 
     /**
+     * The SharedPreference setting per user that stores the screen unlocked functions between
+     * sessions.
+     */
+    private static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d";
+
+    /**
      * ro.bootmode value when phone boots into usual Android.
      */
     private static final String NORMAL_BOOT = "normal";
@@ -128,6 +140,8 @@
     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 AUDIO_MODE_SOURCE = 1;
 
@@ -169,6 +183,7 @@
     private Intent mBroadcastedIntent;
     private boolean mPendingBootBroadcast;
     private static Set<Integer> sBlackListedInterfaces;
+    private SharedPreferences mSettings;
 
     static {
         sBlackListedInterfaces = new HashSet<>();
@@ -217,6 +232,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;
@@ -303,6 +343,8 @@
     public void systemReady() {
         if (DEBUG) Slog.d(TAG, "systemReady");
 
+        LocalServices.getService(ActivityManagerInternal.class).registerScreenObserver(this);
+
         mNotificationManager = (NotificationManager)
                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
 
@@ -407,6 +449,14 @@
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
     }
 
+    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 final class UsbHandler extends Handler {
 
         // current USB state
@@ -423,17 +473,21 @@
         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;
 
         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);
@@ -447,6 +501,9 @@
                             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.
@@ -651,7 +708,7 @@
         private boolean trySetEnabledFunctions(String functions, boolean forceRestart) {
             if (functions == null || applyAdbFunction(functions)
                     .equals(UsbManager.USB_FUNCTION_NONE)) {
-                functions = getDefaultFunctions();
+                functions = getChargingFunctions();
             }
             functions = applyAdbFunction(functions);
 
@@ -662,8 +719,7 @@
             }
 
             if ((!functions.equals(oemFunctions) &&
-                    (mCurrentOemFunctions == null ||
-                            !mCurrentOemFunctions.equals(oemFunctions)))
+                            !mCurrentOemFunctions.equals(oemFunctions))
                     || !mCurrentFunctions.equals(functions)
                     || !mCurrentFunctionsApplied
                     || forceRestart) {
@@ -875,6 +931,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) {
@@ -894,7 +958,13 @@
                     if (mBootCompleted) {
                         if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_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 {
@@ -977,6 +1047,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
@@ -1000,7 +1111,13 @@
                         updateUsbStateBroadcastIfNeeded(false);
                         mPendingBootBroadcast = false;
                     }
-                    setEnabledFunctions(null, false, false);
+
+                    if (!mScreenLocked
+                            && !UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)) {
+                        setScreenUnlockedFunctions();
+                    } else {
+                        setEnabledFunctions(null, false, false);
+                    }
                     if (mCurrentAccessory != null) {
                         getCurrentSettings().accessoryAttached(mCurrentAccessory);
                     }
@@ -1010,16 +1127,15 @@
                     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;
                 }
@@ -1071,20 +1187,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,
@@ -1235,7 +1343,7 @@
             }
         }
 
-        private String getDefaultFunctions() {
+        private String getChargingFunctions() {
             String func = SystemProperties.get(getPersistProp(true),
                     UsbManager.USB_FUNCTION_NONE);
             // if ADB is enabled, reset functions to ADB
@@ -1252,6 +1360,8 @@
             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);
@@ -1308,6 +1418,17 @@
         mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked);
     }
 
+    /**
+     * 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 void readOemUsbOverrideConfig() {
         String[] configList = mContext.getResources().getStringArray(
                 com.android.internal.R.array.config_oemUsbModeOverride);
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/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 0a8d9cb..0c05a02 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -950,8 +950,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";
@@ -1006,6 +1007,13 @@
     public static final String KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL =
             "always_show_data_rat_icon_bool";
 
+    /**
+     * Boolean to decide whether to show precise call failed cause to user
+     * @hide
+     */
+    public static final String KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL =
+            "show_precise_failed_cause_bool";
+
     // These variables are used by the MMS service and exposed through another API, {@link
     // SmsManager}. The variable names and string values are copied from there.
     public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
@@ -2012,6 +2020,7 @@
         sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
         sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false);
         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);
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/CellIdentity.aidl
similarity index 90%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/CellIdentity.aidl
index d750363..aeee769 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/CellIdentity.aidl
@@ -15,6 +15,6 @@
  */
 
 /** @hide */
-package android.telephony.data;
+package android.telephony;
 
-parcelable InterfaceAddress;
+parcelable CellIdentity;
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
new file mode 100644
index 0000000..e092d52
--- /dev/null
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -0,0 +1,173 @@
+/*
+ * 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.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * CellIdentity represents the identity of a unique cell. This is the base class for
+ * CellIdentityXxx which represents cell identity for specific network access technology.
+ */
+public abstract class CellIdentity implements Parcelable {
+    /**
+     * Cell identity type
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "TYPE_", value = {TYPE_GSM, TYPE_CDMA, TYPE_LTE, TYPE_WCDMA, TYPE_TDSCDMA})
+    public @interface Type {}
+
+    /**
+     * Unknown cell identity type
+     * @hide
+     */
+    public static final int TYPE_UNKNOWN        = 0;
+    /**
+     * GSM cell identity type
+     * @hide
+     */
+    public static final int TYPE_GSM            = 1;
+    /**
+     * CDMA cell identity type
+     * @hide
+     */
+    public static final int TYPE_CDMA           = 2;
+    /**
+     * LTE cell identity type
+     * @hide
+     */
+    public static final int TYPE_LTE            = 3;
+    /**
+     * WCDMA cell identity type
+     * @hide
+     */
+    public static final int TYPE_WCDMA          = 4;
+    /**
+     * TDS-CDMA cell identity type
+     * @hide
+     */
+    public static final int TYPE_TDSCDMA        = 5;
+
+    // Log tag
+    /** @hide */
+    protected final String mTag;
+    // Cell identity type
+    /** @hide */
+    protected final int mType;
+    // 3-digit Mobile Country Code in string format. Null for CDMA cell identity.
+    /** @hide */
+    protected final String mMccStr;
+    // 2 or 3-digit Mobile Network Code in string format. Null for CDMA cell identity.
+    /** @hide */
+    protected final String mMncStr;
+
+    /** @hide */
+    protected CellIdentity(String tag, int type, String mcc, String mnc) {
+        mTag = tag;
+        mType = type;
+
+        // Only allow INT_MAX if unknown string mcc/mnc
+        if (mcc == null || mcc.matches("^[0-9]{3}$")) {
+            mMccStr = mcc;
+        } else if (mcc.isEmpty() || mcc.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mccStr is empty or unknown, set it as null.
+            mMccStr = null;
+        } else {
+            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+            // after the bug got fixed.
+            mMccStr = null;
+            log("invalid MCC format: " + mcc);
+        }
+
+        if (mnc == null || mnc.matches("^[0-9]{2,3}$")) {
+            mMncStr = mnc;
+        } else if (mnc.isEmpty() || mnc.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mncStr is empty or unknown, set it as null.
+            mMncStr = null;
+        } else {
+            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+            // after the bug got fixed.
+            mMncStr = null;
+            log("invalid MNC format: " + mnc);
+        }
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @hide
+     * @return The type of the cell identity
+     */
+    public @Type int getType() { return mType; }
+
+    /**
+     * Used by child classes for parceling.
+     *
+     * @hide
+     */
+    @CallSuper
+    public void writeToParcel(Parcel dest, int type) {
+        dest.writeInt(type);
+        dest.writeString(mMccStr);
+        dest.writeString(mMncStr);
+    }
+
+    /**
+     * Construct from Parcel
+     * @hide
+     */
+    protected CellIdentity(String tag, int type, Parcel source) {
+        this(tag, type, source.readString(), source.readString());
+    }
+
+    /** Implement the Parcelable interface */
+    public static final Creator<CellIdentity> CREATOR =
+            new Creator<CellIdentity>() {
+                @Override
+                public CellIdentity createFromParcel(Parcel in) {
+                    int type = in.readInt();
+                    switch (type) {
+                        case TYPE_GSM: return CellIdentityGsm.createFromParcelBody(in);
+                        case TYPE_WCDMA: return CellIdentityWcdma.createFromParcelBody(in);
+                        case TYPE_CDMA: return CellIdentityCdma.createFromParcelBody(in);
+                        case TYPE_LTE: return CellIdentityLte.createFromParcelBody(in);
+                        case TYPE_TDSCDMA: return CellIdentityTdscdma.createFromParcelBody(in);
+                        default: throw new IllegalArgumentException("Bad Cell identity Parcel");
+                    }
+                }
+
+                @Override
+                public CellIdentity[] newArray(int size) {
+                    return new CellIdentity[size];
+                }
+            };
+
+    /** @hide */
+    protected void log(String s) {
+        Rlog.w(mTag, s);
+    }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index ddc938e..2e1d1dc 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -17,8 +17,6 @@
 package android.telephony;
 
 import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 
 import java.util.Objects;
@@ -26,9 +24,8 @@
 /**
  * CellIdentity is to represent a unique CDMA cell
  */
-public final class CellIdentityCdma implements Parcelable {
-
-    private static final String LOG_TAG = "CellSignalStrengthCdma";
+public final class CellIdentityCdma extends CellIdentity {
+    private static final String TAG = CellIdentityCdma.class.getSimpleName();
     private static final boolean DBG = false;
 
     // Network Id 0..65535
@@ -60,6 +57,7 @@
      * @hide
      */
     public CellIdentityCdma() {
+        super(TAG, TYPE_CDMA, null, null);
         mNetworkId = Integer.MAX_VALUE;
         mSystemId = Integer.MAX_VALUE;
         mBasestationId = Integer.MAX_VALUE;
@@ -81,7 +79,7 @@
      *
      * @hide
      */
-    public CellIdentityCdma (int nid, int sid, int bid, int lon, int lat) {
+    public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat) {
         this(nid, sid, bid, lon, lat, null, null);
     }
 
@@ -99,8 +97,9 @@
      *
      * @hide
      */
-    public CellIdentityCdma (int nid, int sid, int bid, int lon, int lat, String alphal,
+    public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat, String alphal,
                              String alphas) {
+        super(TAG, TYPE_CDMA, null, null);
         mNetworkId = nid;
         mSystemId = sid;
         mBasestationId = bid;
@@ -196,40 +195,33 @@
 
         CellIdentityCdma o = (CellIdentityCdma) other;
 
-        return mNetworkId == o.mNetworkId &&
-                mSystemId == o.mSystemId &&
-                mBasestationId == o.mBasestationId &&
-                mLatitude == o.mLatitude &&
-                mLongitude == o.mLongitude &&
-                TextUtils.equals(mAlphaLong, o.mAlphaLong) &&
-                TextUtils.equals(mAlphaShort, o.mAlphaShort);
+        return mNetworkId == o.mNetworkId
+                && mSystemId == o.mSystemId
+                && mBasestationId == o.mBasestationId
+                && mLatitude == o.mLatitude
+                && mLongitude == o.mLongitude
+                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
+                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
     }
 
     @Override
     public String toString() {
-        StringBuilder sb = new StringBuilder("CellIdentityCdma:{");
-        sb.append(" mNetworkId="); sb.append(mNetworkId);
-        sb.append(" mSystemId="); sb.append(mSystemId);
-        sb.append(" mBasestationId="); sb.append(mBasestationId);
-        sb.append(" mLongitude="); sb.append(mLongitude);
-        sb.append(" mLatitude="); sb.append(mLatitude);
-        sb.append(" mAlphaLong="); sb.append(mAlphaLong);
-        sb.append(" mAlphaShort="); sb.append(mAlphaShort);
-        sb.append("}");
-
-        return sb.toString();
-    }
-
-    /** Implement the Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
+        return new StringBuilder(TAG)
+        .append(":{ mNetworkId=").append(mNetworkId)
+        .append(" mSystemId=").append(mSystemId)
+        .append(" mBasestationId=").append(mBasestationId)
+        .append(" mLongitude=").append(mLongitude)
+        .append(" mLatitude=").append(mLatitude)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
+        .append("}").toString();
     }
 
     /** Implement the Parcelable interface */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, TYPE_CDMA);
         dest.writeInt(mNetworkId);
         dest.writeInt(mSystemId);
         dest.writeInt(mBasestationId);
@@ -241,10 +233,16 @@
 
     /** Construct from Parcel, type has already been processed */
     private CellIdentityCdma(Parcel in) {
-        this(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(),
-                in.readString(), in.readString());
+        super(TAG, TYPE_CDMA, in);
+        mNetworkId = in.readInt();
+        mSystemId = in.readInt();
+        mBasestationId = in.readInt();
+        mLongitude = in.readInt();
+        mLatitude = in.readInt();
+        mAlphaLong = in.readString();
+        mAlphaShort = in.readString();
 
-        if (DBG) log("CellIdentityCdma(Parcel): " + toString());
+        if (DBG) log(toString());
     }
 
     /** Implement the Parcelable interface */
@@ -253,7 +251,8 @@
             new Creator<CellIdentityCdma>() {
         @Override
         public CellIdentityCdma createFromParcel(Parcel in) {
-            return new CellIdentityCdma(in);
+            in.readInt();   // skip
+            return createFromParcelBody(in);
         }
 
         @Override
@@ -262,10 +261,8 @@
         }
     };
 
-    /**
-     * log
-     */
-    private static void log(String s) {
-        Rlog.w(LOG_TAG, s);
+    /** @hide */
+    protected static CellIdentityCdma createFromParcelBody(Parcel in) {
+        return new CellIdentityCdma(in);
     }
 }
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 376e6aa..f948f81 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -17,8 +17,6 @@
 package android.telephony;
 
 import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 
 import java.util.Objects;
@@ -26,9 +24,8 @@
 /**
  * CellIdentity to represent a unique GSM cell
  */
-public final class CellIdentityGsm implements Parcelable {
-
-    private static final String LOG_TAG = "CellIdentityGsm";
+public final class CellIdentityGsm extends CellIdentity {
+    private static final String TAG = CellIdentityGsm.class.getSimpleName();
     private static final boolean DBG = false;
 
     // 16-bit Location Area Code, 0..65535
@@ -39,10 +36,6 @@
     private final int mArfcn;
     // 6-bit Base Station Identity Code
     private final int mBsic;
-    // 3-digit Mobile Country Code in string format
-    private final String mMccStr;
-    // 2 or 3-digit Mobile Network Code in string format
-    private final String mMncStr;
     // long alpha Operator Name String or Enhanced Operator Name String
     private final String mAlphaLong;
     // short alpha Operator Name String or Enhanced Operator Name String
@@ -52,12 +45,11 @@
      * @hide
      */
     public CellIdentityGsm() {
+        super(TAG, TYPE_GSM, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mArfcn = Integer.MAX_VALUE;
         mBsic = Integer.MAX_VALUE;
-        mMccStr = null;
-        mMncStr = null;
         mAlphaLong = null;
         mAlphaShort = null;
     }
@@ -70,7 +62,7 @@
      *
      * @hide
      */
-    public CellIdentityGsm (int mcc, int mnc, int lac, int cid) {
+    public CellIdentityGsm(int mcc, int mnc, int lac, int cid) {
         this(lac, cid, Integer.MAX_VALUE, Integer.MAX_VALUE,
                 String.valueOf(mcc), String.valueOf(mnc), null, null);
     }
@@ -86,7 +78,7 @@
      *
      * @hide
      */
-    public CellIdentityGsm (int mcc, int mnc, int lac, int cid, int arfcn, int bsic) {
+    public CellIdentityGsm(int mcc, int mnc, int lac, int cid, int arfcn, int bsic) {
         this(lac, cid, arfcn, bsic, String.valueOf(mcc), String.valueOf(mnc), null, null);
     }
 
@@ -103,8 +95,9 @@
      *
      * @hide
      */
-    public CellIdentityGsm (int lac, int cid, int arfcn, int bsic, String mccStr,
+    public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
                             String mncStr, String alphal, String alphas) {
+        super(TAG, TYPE_GSM, mccStr, mncStr);
         mLac = lac;
         mCid = cid;
         mArfcn = arfcn;
@@ -112,31 +105,6 @@
         // for inbound parcels
         mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
 
-        // Only allow INT_MAX if unknown string mcc/mnc
-        if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
-            mMccStr = mccStr;
-        } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
-            // If the mccStr is empty or unknown, set it as null.
-            mMccStr = null;
-        } else {
-            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
-            // after the bug got fixed.
-            mMccStr = null;
-            log("invalid MCC format: " + mccStr);
-        }
-
-        if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
-            mMncStr = mncStr;
-        } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
-            // If the mncStr is empty or unknown, set it as null.
-            mMncStr = null;
-        } else {
-            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
-            // after the bug got fixed.
-            mMncStr = null;
-            log("invalid MNC format: " + mncStr);
-        }
-
         mAlphaLong = alphal;
         mAlphaShort = alphas;
     }
@@ -237,6 +205,7 @@
 
 
     /**
+     * @deprecated Primary Scrambling Code is not applicable to GSM.
      * @return Integer.MAX_VALUE, undefined for GSM
      */
     @Deprecated
@@ -260,58 +229,54 @@
         }
 
         CellIdentityGsm o = (CellIdentityGsm) other;
-        return mLac == o.mLac &&
-                mCid == o.mCid &&
-                mArfcn == o.mArfcn &&
-                mBsic == o.mBsic &&
-                TextUtils.equals(mMccStr, o.mMccStr) &&
-                TextUtils.equals(mMncStr, o.mMncStr) &&
-                TextUtils.equals(mAlphaLong, o.mAlphaLong) &&
-                TextUtils.equals(mAlphaShort, o.mAlphaShort);
+        return mLac == o.mLac
+                && mCid == o.mCid
+                && mArfcn == o.mArfcn
+                && mBsic == o.mBsic
+                && TextUtils.equals(mMccStr, o.mMccStr)
+                && TextUtils.equals(mMncStr, o.mMncStr)
+                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
+                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
     }
 
     @Override
     public String toString() {
-        StringBuilder sb = new StringBuilder("CellIdentityGsm:{");
-        sb.append(" mLac=").append(mLac);
-        sb.append(" mCid=").append(mCid);
-        sb.append(" mArfcn=").append(mArfcn);
-        sb.append(" mBsic=").append("0x").append(Integer.toHexString(mBsic));
-        sb.append(" mMcc=").append(mMccStr);
-        sb.append(" mMnc=").append(mMncStr);
-        sb.append(" mAlphaLong=").append(mAlphaLong);
-        sb.append(" mAlphaShort=").append(mAlphaShort);
-        sb.append("}");
-
-        return sb.toString();
-    }
-
-    /** Implement the Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
+        return new StringBuilder(TAG)
+        .append(":{ mLac=").append(mLac)
+        .append(" mCid=").append(mCid)
+        .append(" mArfcn=").append(mArfcn)
+        .append(" mBsic=").append("0x").append(Integer.toHexString(mBsic))
+        .append(" mMcc=").append(mMccStr)
+        .append(" mMnc=").append(mMncStr)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
+        .append("}").toString();
     }
 
     /** Implement the Parcelable interface */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, TYPE_GSM);
         dest.writeInt(mLac);
         dest.writeInt(mCid);
         dest.writeInt(mArfcn);
         dest.writeInt(mBsic);
-        dest.writeString(mMccStr);
-        dest.writeString(mMncStr);
         dest.writeString(mAlphaLong);
         dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
     private CellIdentityGsm(Parcel in) {
-        this(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readString(),
-                in.readString(), in.readString(), in.readString());
+        super(TAG, TYPE_GSM, in);
+        mLac = in.readInt();
+        mCid = in.readInt();
+        mArfcn = in.readInt();
+        mBsic = in.readInt();
+        mAlphaLong = in.readString();
+        mAlphaShort = in.readString();
 
-        if (DBG) log("CellIdentityGsm(Parcel): " + toString());
+        if (DBG) log(toString());
     }
 
     /** Implement the Parcelable interface */
@@ -320,7 +285,8 @@
             new Creator<CellIdentityGsm>() {
                 @Override
                 public CellIdentityGsm createFromParcel(Parcel in) {
-                    return new CellIdentityGsm(in);
+                    in.readInt();   // skip
+                    return createFromParcelBody(in);
                 }
 
                 @Override
@@ -329,10 +295,8 @@
                 }
             };
 
-    /**
-     * log
-     */
-    private static void log(String s) {
-        Rlog.w(LOG_TAG, s);
+    /** @hide */
+    protected static CellIdentityGsm createFromParcelBody(Parcel in) {
+        return new CellIdentityGsm(in);
     }
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 6ca5daf6..7f20c8a 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -17,8 +17,6 @@
 package android.telephony;
 
 import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 
 import java.util.Objects;
@@ -26,9 +24,8 @@
 /**
  * CellIdentity is to represent a unique LTE cell
  */
-public final class CellIdentityLte implements Parcelable {
-
-    private static final String LOG_TAG = "CellIdentityLte";
+public final class CellIdentityLte extends CellIdentity {
+    private static final String TAG = CellIdentityLte.class.getSimpleName();
     private static final boolean DBG = false;
 
     // 28-bit cell identity
@@ -39,10 +36,6 @@
     private final int mTac;
     // 18-bit Absolute RF Channel Number
     private final int mEarfcn;
-    // 3-digit Mobile Country Code in string format
-    private final String mMccStr;
-    // 2 or 3-digit Mobile Network Code in string format
-    private final String mMncStr;
     // long alpha Operator Name String or Enhanced Operator Name String
     private final String mAlphaLong;
     // short alpha Operator Name String or Enhanced Operator Name String
@@ -52,12 +45,11 @@
      * @hide
      */
     public CellIdentityLte() {
+        super(TAG, TYPE_LTE, null, null);
         mCi = Integer.MAX_VALUE;
         mPci = Integer.MAX_VALUE;
         mTac = Integer.MAX_VALUE;
         mEarfcn = Integer.MAX_VALUE;
-        mMccStr = null;
-        mMncStr = null;
         mAlphaLong = null;
         mAlphaShort = null;
     }
@@ -72,7 +64,7 @@
      *
      * @hide
      */
-    public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac) {
+    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);
     }
 
@@ -87,7 +79,7 @@
      *
      * @hide
      */
-    public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
+    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);
     }
 
@@ -104,38 +96,13 @@
      *
      * @hide
      */
-    public CellIdentityLte (int ci, int pci, int tac, int earfcn, String mccStr,
+    public CellIdentityLte(int ci, int pci, int tac, int earfcn, String mccStr,
                             String mncStr, String alphal, String alphas) {
+        super(TAG, TYPE_LTE, mccStr, mncStr);
         mCi = ci;
         mPci = pci;
         mTac = tac;
         mEarfcn = earfcn;
-
-        // Only allow INT_MAX if unknown string mcc/mnc
-        if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
-            mMccStr = mccStr;
-        } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
-            // If the mccStr is empty or unknown, set it as null.
-            mMccStr = null;
-        } else {
-            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
-            // after the bug got fixed.
-            mMccStr = null;
-            log("invalid MCC format: " + mccStr);
-        }
-
-        if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
-            mMncStr = mncStr;
-        } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
-            // If the mncStr is empty or unknown, set it as null.
-            mMncStr = null;
-        } else {
-            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
-            // after the bug got fixed.
-            mMncStr = null;
-            log("invalid MNC format: " + mncStr);
-        }
-
         mAlphaLong = alphal;
         mAlphaShort = alphas;
     }
@@ -248,58 +215,54 @@
         }
 
         CellIdentityLte o = (CellIdentityLte) other;
-        return mCi == o.mCi &&
-                mPci == o.mPci &&
-                mTac == o.mTac &&
-                mEarfcn == o.mEarfcn &&
-                TextUtils.equals(mMccStr, o.mMccStr) &&
-                TextUtils.equals(mMncStr, o.mMncStr) &&
-                TextUtils.equals(mAlphaLong, o.mAlphaLong) &&
-                TextUtils.equals(mAlphaShort, o.mAlphaShort);
+        return mCi == o.mCi
+                && mPci == o.mPci
+                && mTac == o.mTac
+                && mEarfcn == o.mEarfcn
+                && TextUtils.equals(mMccStr, o.mMccStr)
+                && TextUtils.equals(mMncStr, o.mMncStr)
+                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
+                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
     }
 
     @Override
     public String toString() {
-        StringBuilder sb = new StringBuilder("CellIdentityLte:{");
-        sb.append(" mCi="); sb.append(mCi);
-        sb.append(" mPci="); sb.append(mPci);
-        sb.append(" mTac="); sb.append(mTac);
-        sb.append(" mEarfcn="); sb.append(mEarfcn);
-        sb.append(" mMcc="); sb.append(mMccStr);
-        sb.append(" mMnc="); sb.append(mMncStr);
-        sb.append(" mAlphaLong="); sb.append(mAlphaLong);
-        sb.append(" mAlphaShort="); sb.append(mAlphaShort);
-        sb.append("}");
-
-        return sb.toString();
-    }
-
-    /** Implement the Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
+        return new StringBuilder(TAG)
+        .append(":{ mCi=").append(mCi)
+        .append(" mPci=").append(mPci)
+        .append(" mTac=").append(mTac)
+        .append(" mEarfcn=").append(mEarfcn)
+        .append(" mMcc=").append(mMccStr)
+        .append(" mMnc=").append(mMncStr)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
+        .append("}").toString();
     }
 
     /** Implement the Parcelable interface */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, TYPE_LTE);
         dest.writeInt(mCi);
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mEarfcn);
-        dest.writeString(mMccStr);
-        dest.writeString(mMncStr);
         dest.writeString(mAlphaLong);
         dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
     private CellIdentityLte(Parcel in) {
-        this(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readString(),
-                in.readString(), in.readString(), in.readString());
+        super(TAG, TYPE_LTE, in);
+        mCi = in.readInt();
+        mPci = in.readInt();
+        mTac = in.readInt();
+        mEarfcn = in.readInt();
+        mAlphaLong = in.readString();
+        mAlphaShort = in.readString();
 
-        if (DBG) log("CellIdentityLte(Parcel): " + toString());
+        if (DBG) log(toString());
     }
 
     /** Implement the Parcelable interface */
@@ -308,7 +271,8 @@
             new Creator<CellIdentityLte>() {
                 @Override
                 public CellIdentityLte createFromParcel(Parcel in) {
-                    return new CellIdentityLte(in);
+                    in.readInt();   // skip;
+                    return createFromParcelBody(in);
                 }
 
                 @Override
@@ -317,10 +281,8 @@
                 }
             };
 
-    /**
-     * log
-     */
-    private static void log(String s) {
-        Rlog.w(LOG_TAG, s);
+    /** @hide */
+    protected static CellIdentityLte createFromParcelBody(Parcel in) {
+        return new CellIdentityLte(in);
     }
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/CellIdentityTdscdma.aidl
similarity index 90%
rename from telephony/java/android/telephony/data/InterfaceAddress.aidl
rename to telephony/java/android/telephony/CellIdentityTdscdma.aidl
index d750363..2a182cd 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.aidl
@@ -15,6 +15,6 @@
  */
 
 /** @hide */
-package android.telephony.data;
+package android.telephony;
 
-parcelable InterfaceAddress;
+parcelable CellIdentityTdscdma;
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
new file mode 100644
index 0000000..001d19f
--- /dev/null
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -0,0 +1,196 @@
+/*
+ * 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.os.Parcel;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * CellIdentity is to represent a unique TD-SCDMA cell
+ */
+public final class CellIdentityTdscdma extends CellIdentity {
+    private static final String TAG = CellIdentityTdscdma.class.getSimpleName();
+    private static final boolean DBG = false;
+
+    // 16-bit Location Area Code, 0..65535, INT_MAX if unknown.
+    private final int mLac;
+    // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown.
+    private final int mCid;
+    // 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
+    private final int mCpid;
+
+    /**
+     * @hide
+     */
+    public CellIdentityTdscdma() {
+        super(TAG, TYPE_TDSCDMA, null, null);
+        mLac = Integer.MAX_VALUE;
+        mCid = Integer.MAX_VALUE;
+        mCpid = Integer.MAX_VALUE;
+    }
+
+    /**
+     * @param mcc 3-digit Mobile Country Code, 0..999
+     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+     * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     *
+     * @hide
+     */
+    public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid) {
+        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid);
+    }
+
+    /**
+     * @param mcc 3-digit Mobile Country Code in string format
+     * @param mnc 2 or 3-digit Mobile Network Code in string format
+     * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     *
+     * @hide
+     */
+    public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
+        super(TAG, TYPE_TDSCDMA, mcc, mnc);
+        mLac = lac;
+        mCid = cid;
+        mCpid = cpid;
+    }
+
+    private CellIdentityTdscdma(CellIdentityTdscdma cid) {
+        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid, cid.mCpid);
+    }
+
+    CellIdentityTdscdma copy() {
+        return new CellIdentityTdscdma(this);
+    }
+
+    /**
+     * Get Mobile Country Code in string format
+     * @return Mobile Country Code in string format, null if unknown
+     */
+    public String getMccStr() {
+        return mMccStr;
+    }
+
+    /**
+     * Get Mobile Network Code in string format
+     * @return Mobile Network Code in string format, null if unknown
+     */
+    public String getMncStr() {
+        return mMncStr;
+    }
+
+    /**
+     * @return 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+     */
+    public int getLac() {
+        return mLac;
+    }
+
+    /**
+     * @return 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+     */
+    public int getCid() {
+        return mCid;
+    }
+
+    /**
+     * @return 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     */
+    public int getCpid() {
+        return mCpid;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (!(other instanceof CellIdentityTdscdma)) {
+            return false;
+        }
+
+        CellIdentityTdscdma o = (CellIdentityTdscdma) other;
+        return TextUtils.equals(mMccStr, o.mMccStr)
+                && TextUtils.equals(mMncStr, o.mMncStr)
+                && mLac == o.mLac
+                && mCid == o.mCid
+                && mCpid == o.mCpid;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(TAG)
+        .append(":{ mMcc=").append(mMccStr)
+        .append(" mMnc=").append(mMncStr)
+        .append(" mLac=").append(mLac)
+        .append(" mCid=").append(mCid)
+        .append(" mCpid=").append(mCpid)
+        .append("}").toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, TYPE_TDSCDMA);
+        dest.writeInt(mLac);
+        dest.writeInt(mCid);
+        dest.writeInt(mCpid);
+    }
+
+    /** Construct from Parcel, type has already been processed */
+    private CellIdentityTdscdma(Parcel in) {
+        super(TAG, TYPE_TDSCDMA, in);
+        mLac = in.readInt();
+        mCid = in.readInt();
+        mCpid = in.readInt();
+
+        if (DBG) log(toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    public static final Creator<CellIdentityTdscdma> CREATOR =
+            new Creator<CellIdentityTdscdma>() {
+                @Override
+                public CellIdentityTdscdma createFromParcel(Parcel in) {
+                    in.readInt();   // skip
+                    return createFromParcelBody(in);
+                }
+
+                @Override
+                public CellIdentityTdscdma[] newArray(int size) {
+                    return new CellIdentityTdscdma[size];
+                }
+            };
+
+    /** @hide */
+    protected static CellIdentityTdscdma createFromParcelBody(Parcel in) {
+        return new CellIdentityTdscdma(in);
+    }
+}
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index e4bb4f2..1aa1715 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -17,8 +17,6 @@
 package android.telephony;
 
 import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 
 import java.util.Objects;
@@ -26,9 +24,8 @@
 /**
  * CellIdentity to represent a unique UMTS cell
  */
-public final class CellIdentityWcdma implements Parcelable {
-
-    private static final String LOG_TAG = "CellIdentityWcdma";
+public final class CellIdentityWcdma extends CellIdentity {
+    private static final String TAG = CellIdentityWcdma.class.getSimpleName();
     private static final boolean DBG = false;
 
     // 16-bit Location Area Code, 0..65535
@@ -39,10 +36,6 @@
     private final int mPsc;
     // 16-bit UMTS Absolute RF Channel Number
     private final int mUarfcn;
-    // 3-digit Mobile Country Code in string format
-    private final String mMccStr;
-    // 2 or 3-digit Mobile Network Code in string format
-    private final String mMncStr;
     // long alpha Operator Name String or Enhanced Operator Name String
     private final String mAlphaLong;
     // short alpha Operator Name String or Enhanced Operator Name String
@@ -52,12 +45,11 @@
      * @hide
      */
     public CellIdentityWcdma() {
+        super(TAG, TYPE_TDSCDMA, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mPsc = Integer.MAX_VALUE;
         mUarfcn = Integer.MAX_VALUE;
-        mMccStr = null;
-        mMncStr = null;
         mAlphaLong = null;
         mAlphaShort = null;
     }
@@ -106,36 +98,11 @@
      */
     public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
                               String mccStr, String mncStr, String alphal, String alphas) {
+        super(TAG, TYPE_WCDMA, mccStr, mncStr);
         mLac = lac;
         mCid = cid;
         mPsc = psc;
         mUarfcn = uarfcn;
-
-        // Only allow INT_MAX if unknown string mcc/mnc
-        if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
-            mMccStr = mccStr;
-        } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
-            // If the mccStr is empty or unknown, set it as null.
-            mMccStr = null;
-        } else {
-            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
-            // after the bug got fixed.
-            mMccStr = null;
-            log("invalid MCC format: " + mccStr);
-        }
-
-        if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
-            mMncStr = mncStr;
-        } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
-            // If the mncStr is empty or unknown, set it as null.
-            mMncStr = null;
-        } else {
-            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
-            // after the bug got fixed.
-            mMncStr = null;
-            log("invalid MNC format: " + mncStr);
-        }
-
         mAlphaLong = alphal;
         mAlphaShort = alphas;
     }
@@ -250,58 +217,53 @@
         }
 
         CellIdentityWcdma o = (CellIdentityWcdma) other;
-        return mLac == o.mLac &&
-                mCid == o.mCid &&
-                mPsc == o.mPsc &&
-                mUarfcn == o.mUarfcn &&
-                TextUtils.equals(mMccStr, o.mMccStr) &&
-                TextUtils.equals(mMncStr, o.mMncStr) &&
-                TextUtils.equals(mAlphaLong, o.mAlphaLong) &&
-                TextUtils.equals(mAlphaShort, o.mAlphaShort);
+        return mLac == o.mLac
+                && mCid == o.mCid
+                && mPsc == o.mPsc
+                && mUarfcn == o.mUarfcn
+                && TextUtils.equals(mMccStr, o.mMccStr)
+                && TextUtils.equals(mMncStr, o.mMncStr)
+                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
+                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
     }
 
     @Override
     public String toString() {
-        StringBuilder sb = new StringBuilder("CellIdentityWcdma:{");
-        sb.append(" mLac=").append(mLac);
-        sb.append(" mCid=").append(mCid);
-        sb.append(" mPsc=").append(mPsc);
-        sb.append(" mUarfcn=").append(mUarfcn);
-        sb.append(" mMcc=").append(mMccStr);
-        sb.append(" mMnc=").append(mMncStr);
-        sb.append(" mAlphaLong=").append(mAlphaLong);
-        sb.append(" mAlphaShort=").append(mAlphaShort);
-        sb.append("}");
-
-        return sb.toString();
-    }
-
-    /** Implement the Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
+        return new StringBuilder(TAG)
+        .append(":{ mLac=").append(mLac)
+        .append(" mCid=").append(mCid)
+        .append(" mPsc=").append(mPsc)
+        .append(" mUarfcn=").append(mUarfcn)
+        .append(" mMcc=").append(mMccStr)
+        .append(" mMnc=").append(mMncStr)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
+        .append("}").toString();
     }
 
     /** Implement the Parcelable interface */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, TYPE_WCDMA);
         dest.writeInt(mLac);
         dest.writeInt(mCid);
         dest.writeInt(mPsc);
         dest.writeInt(mUarfcn);
-        dest.writeString(mMccStr);
-        dest.writeString(mMncStr);
         dest.writeString(mAlphaLong);
         dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
     private CellIdentityWcdma(Parcel in) {
-        this(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readString(),
-                in.readString(), in.readString(), in.readString());
-
-        if (DBG) log("CellIdentityWcdma(Parcel): " + toString());
+        super(TAG, TYPE_WCDMA, in);
+        mLac = in.readInt();
+        mCid = in.readInt();
+        mPsc = in.readInt();
+        mUarfcn = in.readInt();
+        mAlphaLong = in.readString();
+        mAlphaShort = in.readString();
+        if (DBG) log(toString());
     }
 
     /** Implement the Parcelable interface */
@@ -310,7 +272,8 @@
             new Creator<CellIdentityWcdma>() {
                 @Override
                 public CellIdentityWcdma createFromParcel(Parcel in) {
-                    return new CellIdentityWcdma(in);
+                    in.readInt();   // skip
+                    return createFromParcelBody(in);
                 }
 
                 @Override
@@ -319,10 +282,8 @@
                 }
             };
 
-    /**
-     * log
-     */
-    private static void log(String s) {
-        Rlog.w(LOG_TAG, s);
+    /** @hide */
+    protected static CellIdentityWcdma createFromParcelBody(Parcel in) {
+        return new CellIdentityWcdma(in);
     }
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index c7e5131..98ea451 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -244,6 +244,13 @@
      */
     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;
+
      /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -349,6 +356,9 @@
                     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;
@@ -543,6 +553,14 @@
     }
 
     /**
+     * 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 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 +672,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/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index e42a758..3937201 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -32,6 +32,7 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Describes a single UICC access rule according to the GlobalPlatform Secure Element Access Control
@@ -205,6 +206,21 @@
     }
 
     @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccAccessRule that = (UiccAccessRule) obj;
+        return Arrays.equals(mCertificateHash, that.mCertificateHash)
+                && Objects.equals(mPackageName, that.mPackageName)
+                && mAccessType == that.mAccessType;
+    }
+
+    @Override
     public String toString() {
         return "cert: " + IccUtils.bytesToHexString(mCertificateHash) + " pkg: " +
                 mPackageName + " access: " + mAccessType;
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/data/ApnSetting.aidl
similarity index 86%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/data/ApnSetting.aidl
index d750363..381e5e8 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/data/ApnSetting.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,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-/** @hide */
 package android.telephony.data;
 
-parcelable InterfaceAddress;
+parcelable ApnSetting;
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
new file mode 100644
index 0000000..2ab8d4f
--- /dev/null
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -0,0 +1,1370 @@
+/*
+ * 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.data;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+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.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.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A class representing an APN configuration.
+ */
+public class ApnSetting implements Parcelable {
+
+    static final String LOG_TAG = "ApnSetting";
+    private static final boolean VDBG = false;
+
+    private final String mEntryName;
+    private final String mApnName;
+    private final InetAddress mProxy;
+    private final int mPort;
+    private final URL mMmsc;
+    private final InetAddress mMmsProxy;
+    private final int mMmsPort;
+    private final String mUser;
+    private final String mPassword;
+    private final int mAuthType;
+    private final List<String> mTypes;
+    private final int mTypesBitmap;
+    private final int mId;
+    private final String mOperatorNumeric;
+    private final String mProtocol;
+    private final String mRoamingProtocol;
+    private final int mMtu;
+
+    private final boolean mCarrierEnabled;
+    private final int mBearer;
+    private final int mBearerBitmask;
+
+    private final int mProfileId;
+
+    private final boolean mModemCognitive;
+    private final int mMaxConns;
+    private final int mWaitTime;
+    private final int mMaxConnsTime;
+
+    private final String mMvnoType;
+    private final String mMvnoMatchData;
+
+    private boolean mPermanentFailed = false;
+
+    /**
+     * Returns the types bitmap of the APN.
+     *
+     * @return types bitmap of the APN
+     * @hide
+     */
+    public int getTypesBitmap() {
+        return mTypesBitmap;
+    }
+
+    /**
+     * Returns the MTU size of the mobile interface to which the APN connected.
+     *
+     * @return the MTU size of the APN
+     * @hide
+     */
+    public int getMtu() {
+        return mMtu;
+    }
+
+    /**
+     * 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
+     * @hide
+     */
+    public int getProfileId() {
+        return mProfileId;
+    }
+
+    /**
+     * Returns if the APN setting is to be set in modem.
+     *
+     * @return is the APN setting to be set in modem
+     * @hide
+     */
+    public boolean getModemCognitive() {
+        return mModemCognitive;
+    }
+
+    /**
+     * Returns the max connections of this APN.
+     *
+     * @return the max connections of this APN
+     * @hide
+     */
+    public int getMaxConns() {
+        return mMaxConns;
+    }
+
+    /**
+     * Returns the wait time for retry of the APN.
+     *
+     * @return the wait time for retry of the APN
+     * @hide
+     */
+    public int getWaitTime() {
+        return mWaitTime;
+    }
+
+    /**
+     * Returns the time to limit max connection for the APN.
+     *
+     * @return the time to limit max connection for the APN
+     * @hide
+     */
+    public int getMaxConnsTime() {
+        return mMaxConnsTime;
+    }
+
+    /**
+     * Returns the MVNO data. Examples:
+     *   "spn": A MOBILE, BEN NL
+     *   "imsi": 302720x94, 2060188
+     *   "gid": 4E, 33
+     *   "iccid": 898603 etc..
+     *
+     * @return the mvno match data
+     * @hide
+     */
+    public String getMvnoMatchData() {
+        return mMvnoMatchData;
+    }
+
+    /**
+     * Indicates this APN setting is permanently failed and cannot be
+     * retried by the retry manager anymore.
+     *
+     * @return if this APN setting is permanently failed
+     * @hide
+     */
+    public boolean getPermanentFailed() {
+        return mPermanentFailed;
+    }
+
+    /**
+     * Sets if this APN setting is permanently failed.
+     *
+     * @param permanentFailed if this APN setting is permanently failed
+     * @hide
+     */
+    public void setPermanentFailed(boolean permanentFailed) {
+        mPermanentFailed = permanentFailed;
+    }
+
+    /**
+     * Returns the entry name of the APN.
+     *
+     * @return the entry name for the APN
+     */
+    public String getEntryName() {
+        return mEntryName;
+    }
+
+    /**
+     * Returns the name of the APN.
+     *
+     * @return APN name
+     */
+    public String getApnName() {
+        return mApnName;
+    }
+
+    /**
+     * Returns the proxy address of the APN.
+     *
+     * @return proxy address.
+     */
+    public InetAddress getProxy() {
+        return mProxy;
+    }
+
+    /**
+     * Returns the proxy port of the APN.
+     *
+     * @return proxy port
+     */
+    public int getPort() {
+        return mPort;
+    }
+    /**
+     * Returns the MMSC URL of the APN.
+     *
+     * @return MMSC URL.
+     */
+    public URL getMmsc() {
+        return mMmsc;
+    }
+
+    /**
+     * Returns the MMS proxy address of the APN.
+     *
+     * @return MMS proxy address.
+     */
+    public InetAddress getMmsProxy() {
+        return mMmsProxy;
+    }
+
+    /**
+     * Returns the MMS proxy port of the APN.
+     *
+     * @return MMS proxy port
+     */
+    public int getMmsPort() {
+        return mMmsPort;
+    }
+
+    /**
+     * Returns the APN username of the APN.
+     *
+     * @return APN username
+     */
+    public String getUser() {
+        return mUser;
+    }
+
+    /**
+     * Returns the APN password of the APN.
+     *
+     * @return APN password
+     */
+    public String getPassword() {
+        return mPassword;
+    }
+
+    /** @hide */
+    @IntDef({
+            AUTH_TYPE_NONE,
+            AUTH_TYPE_PAP,
+            AUTH_TYPE_CHAP,
+            AUTH_TYPE_PAP_OR_CHAP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AuthType {}
+
+    /**
+     * Returns the authentication type of the APN.
+     *
+     * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
+     *
+     * @return authentication type
+     */
+    @AuthType
+    public int getAuthType() {
+        return mAuthType;
+    }
+
+    /** @hide */
+    @StringDef({
+            TYPE_DEFAULT,
+            TYPE_MMS,
+            TYPE_SUPL,
+            TYPE_DUN,
+            TYPE_HIPRI,
+            TYPE_FOTA,
+            TYPE_IMS,
+            TYPE_CBS,
+            TYPE_IA,
+            TYPE_EMERGENCY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApnType {}
+
+    /**
+     * Returns the list of APN types of the APN.
+     *
+     * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+     *
+     * @return the list of APN types
+     */
+    @ApnType
+    public List<String> getTypes() {
+        return mTypes;
+    }
+
+    /**
+     * Returns the unique database id for this entry.
+     *
+     * @return the unique database id
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the numeric operator ID for the APN. Usually
+     * {@link android.provider.Telephony.Carriers#MCC} +
+     * {@link android.provider.Telephony.Carriers#MNC}.
+     *
+     * @return the numeric operator ID
+     */
+    public String getOperatorNumeric() {
+        return mOperatorNumeric;
+    }
+
+    /** @hide */
+    @StringDef({
+            PROTOCOL_IP,
+            PROTOCOL_IPV6,
+            PROTOCOL_IPV4V6,
+            PROTOCOL_PPP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtocolType {}
+
+    /**
+     * Returns the protocol to use to connect to this APN.
+     *
+     * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
+     * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+     *
+     * @return the protocol
+     */
+    @ProtocolType
+    public String getProtocol() {
+        return mProtocol;
+    }
+
+    /**
+     * Returns the protocol to use to connect to this APN when roaming.
+     *
+     * The syntax is the same as {@link android.provider.Telephony.Carriers#PROTOCOL}.
+     *
+     * @return the roaming protocol
+     */
+    public String getRoamingProtocol() {
+        return mRoamingProtocol;
+    }
+
+    /**
+     * Returns the current status of APN.
+     *
+     * {@code true} : enabled APN.
+     * {@code false} : disabled APN.
+     *
+     * @return the current status
+     */
+    public boolean isEnabled() {
+        return mCarrierEnabled;
+    }
+
+    /** @hide */
+    @StringDef({
+            MVNO_TYPE_SPN,
+            MVNO_TYPE_IMSI,
+            MVNO_TYPE_GID,
+            MVNO_TYPE_ICCID,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MvnoType {}
+
+    /**
+     * Returns the MVNO match type for this APN.
+     *
+     * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
+     *
+     * @return the MVNO match type
+     */
+    @MvnoType
+    public String getMvnoType() {
+        return mMvnoType;
+    }
+
+    private ApnSetting(Builder builder) {
+        this.mEntryName = builder.mEntryName;
+        this.mApnName = builder.mApnName;
+        this.mProxy = builder.mProxy;
+        this.mPort = builder.mPort;
+        this.mMmsc = builder.mMmsc;
+        this.mMmsProxy = builder.mMmsProxy;
+        this.mMmsPort = builder.mMmsPort;
+        this.mUser = builder.mUser;
+        this.mPassword = builder.mPassword;
+        this.mAuthType = builder.mAuthType;
+        this.mTypes = (builder.mTypes == null ? new ArrayList<String>() : builder.mTypes);
+        this.mTypesBitmap = builder.mTypesBitmap;
+        this.mId = builder.mId;
+        this.mOperatorNumeric = builder.mOperatorNumeric;
+        this.mProtocol = builder.mProtocol;
+        this.mRoamingProtocol = builder.mRoamingProtocol;
+        this.mMtu = builder.mMtu;
+        this.mCarrierEnabled = builder.mCarrierEnabled;
+        this.mBearer = builder.mBearer;
+        this.mBearerBitmask = builder.mBearerBitmask;
+        this.mProfileId = builder.mProfileId;
+        this.mModemCognitive = builder.mModemCognitive;
+        this.mMaxConns = builder.mMaxConns;
+        this.mWaitTime = builder.mWaitTime;
+        this.mMaxConnsTime = builder.mMaxConnsTime;
+        this.mMvnoType = builder.mMvnoType;
+        this.mMvnoMatchData = builder.mMvnoMatchData;
+    }
+
+    /** @hide */
+    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,
+            int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
+        return new Builder()
+                .setId(id)
+                .setOperatorNumeric(operatorNumeric)
+                .setEntryName(entryName)
+                .setApnName(apnName)
+                .setProxy(proxy)
+                .setPort(port)
+                .setMmsc(mmsc)
+                .setMmsProxy(mmsProxy)
+                .setMmsPort(mmsPort)
+                .setUser(user)
+                .setPassword(password)
+                .setAuthType(authType)
+                .setTypes(types)
+                .setProtocol(protocol)
+                .setRoamingProtocol(roamingProtocol)
+                .setCarrierEnabled(carrierEnabled)
+                .setBearer(bearer)
+                .setBearerBitmask(bearerBitmask)
+                .setProfileId(profileId)
+                .setModemCognitive(modemCognitive)
+                .setMaxConns(maxConns)
+                .setWaitTime(waitTime)
+                .setMaxConnsTime(maxConnsTime)
+                .setMtu(mtu)
+                .setMvnoType(mvnoType)
+                .setMvnoMatchData(mvnoMatchData)
+                .build();
+    }
+
+    /** @hide */
+    public static ApnSetting makeApnSetting(Cursor cursor) {
+        String[] types = parseTypes(
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
+
+        return makeApnSetting(
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
+                inetAddressFromString(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
+                portFromString(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))),
+                URLFromString(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
+                inetAddressFromString(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
+                portFromString(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT))),
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
+                Arrays.asList(types),
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
+                cursor.getString(cursor.getColumnIndexOrThrow(
+                        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)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MODEM_COGNITIVE)) == 1,
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MAX_CONNS_TIME)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
+                cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MVNO_TYPE)),
+                cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MVNO_MATCH_DATA)));
+    }
+
+    /** @hide */
+    public static ApnSetting makeApnSetting(ApnSetting apn) {
+        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.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
+                apn.mMvnoType, apn.mMvnoMatchData);
+    }
+
+    /** @hide */
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[ApnSettingV3] ")
+                .append(mEntryName)
+                .append(", ").append(mId)
+                .append(", ").append(mOperatorNumeric)
+                .append(", ").append(mApnName)
+                .append(", ").append(inetAddressToString(mProxy))
+                .append(", ").append(URLToString(mMmsc))
+                .append(", ").append(inetAddressToString(mMmsProxy))
+                .append(", ").append(portToString(mMmsPort))
+                .append(", ").append(portToString(mPort))
+                .append(", ").append(mAuthType).append(", ");
+        for (int i = 0; i < mTypes.size(); i++) {
+            sb.append(mTypes.get(i));
+            if (i < mTypes.size() - 1) {
+                sb.append(" | ");
+            }
+        }
+        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);
+        sb.append(", ").append(mWaitTime);
+        sb.append(", ").append(mMaxConnsTime);
+        sb.append(", ").append(mMtu);
+        sb.append(", ").append(mMvnoType);
+        sb.append(", ").append(mMvnoMatchData);
+        sb.append(", ").append(mPermanentFailed);
+        return sb.toString();
+    }
+
+    /**
+     * Returns true if there are MVNO params specified.
+     * @hide
+     */
+    public boolean hasMvnoParams() {
+        return !TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData);
+    }
+
+    /** @hide */
+    public boolean canHandleType(String type) {
+        if (!mCarrierEnabled) return false;
+        boolean wildcardable = true;
+        if (TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
+        for (String t : mTypes) {
+            // DEFAULT handles all, and HIPRI is handled by DEFAULT
+            if (t.equalsIgnoreCase(type)
+                    || (wildcardable && t.equalsIgnoreCase(TYPE_ALL))
+                    || (t.equalsIgnoreCase(TYPE_DEFAULT)
+                    && type.equalsIgnoreCase(TYPE_HIPRI))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // check whether the types of two APN same (even only one type of each APN is same)
+    private boolean typeSameAny(ApnSetting first, ApnSetting second) {
+        if (VDBG) {
+            StringBuilder apnType1 = new StringBuilder(first.mApnName + ": ");
+            for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
+                apnType1.append(first.mTypes.get(index1));
+                apnType1.append(",");
+            }
+
+            StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
+            for (int index1 = 0; index1 < second.mTypes.size(); index1++) {
+                apnType2.append(second.mTypes.get(index1));
+                apnType2.append(",");
+            }
+            Rlog.d(LOG_TAG, "APN1: is " + apnType1);
+            Rlog.d(LOG_TAG, "APN2: is " + apnType2);
+        }
+
+        for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
+            for (int index2 = 0; index2 < second.mTypes.size(); index2++) {
+                if (first.mTypes.get(index1).equals(ApnSetting.TYPE_ALL)
+                        || second.mTypes.get(index2).equals(ApnSetting.TYPE_ALL)
+                        || first.mTypes.get(index1).equals(second.mTypes.get(index2))) {
+                    if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
+                    return true;
+                }
+            }
+        }
+
+        if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
+        return false;
+    }
+
+    // TODO - if we have this function we should also have hashCode.
+    // Also should handle changes in type order and perhaps case-insensitivity
+    /** @hide */
+    public boolean equals(Object o) {
+        if (o instanceof ApnSetting == false) {
+            return false;
+        }
+
+        ApnSetting other = (ApnSetting) o;
+
+        return mEntryName.equals(other.mEntryName)
+                && Objects.equals(mId, other.mId)
+                && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
+                && Objects.equals(mApnName, other.mApnName)
+                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mMmsc, other.mMmsc)
+                && Objects.equals(mMmsProxy, other.mMmsProxy)
+                && Objects.equals(mMmsPort, other.mMmsPort)
+                && Objects.equals(mPort,other.mPort)
+                && Objects.equals(mUser, other.mUser)
+                && Objects.equals(mPassword, other.mPassword)
+                && Objects.equals(mAuthType, other.mAuthType)
+                && Objects.equals(mTypes, other.mTypes)
+                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && 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)
+                && Objects.equals(mWaitTime, other.mWaitTime)
+                && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
+                && Objects.equals(mMtu, other.mMtu)
+                && Objects.equals(mMvnoType, other.mMvnoType)
+                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData);
+    }
+
+    /**
+     * Compare two APN settings
+     *
+     * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. We only use this for
+     * determining if tearing a data call is needed when conditions change. See
+     * cleanUpConnectionsOnUpdatedApns in DcTracker.
+     *
+     * @param o the other object to compare
+     * @param isDataRoaming True if the device is on data roaming
+     * @return True if the two APN settings are same
+     * @hide
+     */
+    public boolean equals(Object o, boolean isDataRoaming) {
+        if (!(o instanceof ApnSetting)) {
+            return false;
+        }
+
+        ApnSetting other = (ApnSetting) o;
+
+        return mEntryName.equals(other.mEntryName)
+                && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
+                && Objects.equals(mApnName, other.mApnName)
+                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mMmsc, other.mMmsc)
+                && Objects.equals(mMmsProxy, other.mMmsProxy)
+                && Objects.equals(mMmsPort, other.mMmsPort)
+                && Objects.equals(mPort, other.mPort)
+                && Objects.equals(mUser, other.mUser)
+                && Objects.equals(mPassword, other.mPassword)
+                && Objects.equals(mAuthType, other.mAuthType)
+                && Objects.equals(mTypes, other.mTypes)
+                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
+                && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
+                && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
+                && Objects.equals(mProfileId, other.mProfileId)
+                && Objects.equals(mModemCognitive, other.mModemCognitive)
+                && Objects.equals(mMaxConns, other.mMaxConns)
+                && Objects.equals(mWaitTime, other.mWaitTime)
+                && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
+                && Objects.equals(mMtu, other.mMtu)
+                && Objects.equals(mMvnoType, other.mMvnoType)
+                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData);
+    }
+
+    /**
+     * Check if neither mention DUN and are substantially similar
+     *
+     * @param other The other APN settings to compare
+     * @return True if two APN settings are similar
+     * @hide
+     */
+    public boolean similar(ApnSetting other) {
+        return (!this.canHandleType(TYPE_DUN)
+                && !other.canHandleType(TYPE_DUN)
+                && Objects.equals(this.mApnName, other.mApnName)
+                && !typeSameAny(this, other)
+                && xorEqualsInetAddress(this.mProxy, other.mProxy)
+                && xorEqualsPort(this.mPort, other.mPort)
+                && 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));
+    }
+
+    // Equal or one is not specified.
+    private boolean xorEquals(String first, String second) {
+        return (Objects.equals(first, second)
+                || TextUtils.isEmpty(first)
+                || TextUtils.isEmpty(second));
+    }
+
+    // Equal or one is not specified.
+    private boolean xorEqualsInetAddress(InetAddress first, InetAddress second) {
+        return first == null || second == null || first.equals(second);
+    }
+
+    // Equal or one is not specified.
+    private boolean xorEqualsURL(URL first, URL second) {
+        return first == null || second == null || first.equals(second);
+    }
+
+    // Equal or one is not specified.
+    private boolean xorEqualsPort(int first, int second) {
+        return first == -1 || second == -1 || Objects.equals(first, second);
+    }
+
+    // Helper function to convert APN string into a 32-bit bitmask.
+    private static int getApnBitmask(String apn) {
+        switch (apn) {
+            case TYPE_DEFAULT: return ApnTypes.DEFAULT;
+            case TYPE_MMS: return ApnTypes.MMS;
+            case TYPE_SUPL: return ApnTypes.SUPL;
+            case TYPE_DUN: return ApnTypes.DUN;
+            case TYPE_HIPRI: return ApnTypes.HIPRI;
+            case TYPE_FOTA: return ApnTypes.FOTA;
+            case TYPE_IMS: return ApnTypes.IMS;
+            case TYPE_CBS: return ApnTypes.CBS;
+            case TYPE_IA: return ApnTypes.IA;
+            case TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
+            case TYPE_ALL: return ApnTypes.ALL;
+            default: return ApnTypes.NONE;
+        }
+    }
+
+    private String deParseTypes(List<String> types) {
+        if (types == null) {
+            return null;
+        }
+        return TextUtils.join(",", types);
+    }
+
+    /** @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.PORT, portToString(mPort));
+        if (mMmsc != null) {
+            apnValue.put(Telephony.Carriers.MMSC, 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.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.CARRIER_ENABLED, mCarrierEnabled);
+        // networkTypeBit.
+        apnValue.put(Telephony.Carriers.BEARER_BITMASK, mBearerBitmask);
+        if (mMvnoType != null) {
+            apnValue.put(Telephony.Carriers.MVNO_TYPE, mMvnoType);
+        }
+
+        return apnValue;
+    }
+
+    /**
+     * @param types comma delimited list of APN types
+     * @return array of APN types
+     * @hide
+     */
+    public static String[] parseTypes(String types) {
+        String[] result;
+        // If unset, set to DEFAULT.
+        if (TextUtils.isEmpty(types)) {
+            result = new String[1];
+            result[0] = TYPE_ALL;
+        } else {
+            result = types.split(",");
+        }
+        return result;
+    }
+
+    private static URL URLFromString(String url) {
+        try {
+            return TextUtils.isEmpty(url) ? null : new URL(url);
+        } catch (MalformedURLException e) {
+            Log.e(LOG_TAG, "Can't parse URL from string.");
+            return null;
+        }
+    }
+
+    private static String URLToString(URL url) {
+        return url == null ? "" : url.toString();
+    }
+
+    private static InetAddress inetAddressFromString(String inetAddress) {
+        if (TextUtils.isEmpty(inetAddress)) {
+            return null;
+        }
+        try {
+            return InetAddress.getByName(inetAddress);
+        } catch (UnknownHostException e) {
+            Log.e(LOG_TAG, "Can't parse InetAddress from string: unknown host.");
+            return null;
+        }
+    }
+
+    private static String inetAddressToString(InetAddress inetAddress) {
+        if (inetAddress == null) {
+            return null;
+        }
+        return TextUtils.isEmpty(inetAddress.getHostName())
+                ? inetAddress.getHostAddress() : inetAddress.getHostName();
+    }
+
+    private static int portFromString(String strPort) {
+        int port = -1;
+        if (!TextUtils.isEmpty(strPort)) {
+            try {
+                port = Integer.parseInt(strPort);
+            } catch (NumberFormatException e) {
+                Log.e(LOG_TAG, "Can't parse port from String");
+            }
+        }
+        return port;
+    }
+
+    private static String portToString(int port) {
+        return port == -1 ? "" : Integer.toString(port);
+    }
+
+    // Implement Parcelable.
+    @Override
+    /** @hide */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    /** @hide */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mId);
+        dest.writeString(mOperatorNumeric);
+        dest.writeString(mEntryName);
+        dest.writeString(mApnName);
+        dest.writeValue(mProxy);
+        dest.writeInt(mPort);
+        dest.writeValue(mMmsc);
+        dest.writeValue(mMmsProxy);
+        dest.writeInt(mMmsPort);
+        dest.writeString(mUser);
+        dest.writeString(mPassword);
+        dest.writeInt(mAuthType);
+        dest.writeStringArray(mTypes.toArray(new String[0]));
+        dest.writeString(mProtocol);
+        dest.writeString(mRoamingProtocol);
+        dest.writeInt(mCarrierEnabled ? 1: 0);
+        dest.writeString(mMvnoType);
+    }
+
+    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);
+    }
+
+    public static final Parcelable.Creator<ApnSetting> CREATOR =
+            new Parcelable.Creator<ApnSetting>() {
+                @Override
+                public ApnSetting createFromParcel(Parcel in) {
+                    return readFromParcel(in);
+                }
+
+                @Override
+                public ApnSetting[] newArray(int size) {
+                    return new ApnSetting[size];
+                }
+            };
+
+    /**
+     * APN types for data connections.  These are usage categories for an APN
+     * entry.  One APN entry may support multiple APN types, eg, a single APN
+     * may service regular internet traffic ("default") as well as MMS-specific
+     * connections.<br/>
+     * ALL is a special type to indicate that this APN entry can
+     * service all data connections.
+     */
+    public static final String TYPE_ALL = "*";
+    /** APN type for default data traffic */
+    public static final String TYPE_DEFAULT = "default";
+    /** APN type for MMS traffic */
+    public static final String TYPE_MMS = "mms";
+    /** APN type for SUPL assisted GPS */
+    public static final String TYPE_SUPL = "supl";
+    /** APN type for DUN traffic */
+    public static final String TYPE_DUN = "dun";
+    /** APN type for HiPri traffic */
+    public static final String TYPE_HIPRI = "hipri";
+    /** APN type for FOTA */
+    public static final String TYPE_FOTA = "fota";
+    /** APN type for IMS */
+    public static final String TYPE_IMS = "ims";
+    /** APN type for CBS */
+    public static final String TYPE_CBS = "cbs";
+    /** APN type for IA Initial Attach APN */
+    public static final String TYPE_IA = "ia";
+    /** APN type for Emergency PDN. This is not an IA apn, but is used
+     * for access to carrier services in an emergency call situation. */
+    public static final String TYPE_EMERGENCY = "emergency";
+    /**
+     * Array of all APN types
+     *
+     * @hide
+     */
+    public static final String[] ALL_TYPES = {
+            TYPE_DEFAULT,
+            TYPE_MMS,
+            TYPE_SUPL,
+            TYPE_DUN,
+            TYPE_HIPRI,
+            TYPE_FOTA,
+            TYPE_IMS,
+            TYPE_CBS,
+            TYPE_IA,
+            TYPE_EMERGENCY
+    };
+
+    // Possible values for authentication types.
+    public static final int AUTH_TYPE_NONE = 0;
+    public static final int AUTH_TYPE_PAP = 1;
+    public static final int AUTH_TYPE_CHAP = 2;
+    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
+
+    // Possible values for protocol.
+    public static final String PROTOCOL_IP = "IP";
+    public static final String PROTOCOL_IPV6 = "IPV6";
+    public static final String PROTOCOL_IPV4V6 = "IPV4V6";
+    public static final String PROTOCOL_PPP = "PPP";
+
+    // Possible values for MVNO type.
+    public static final String MVNO_TYPE_SPN = "spn";
+    public static final String MVNO_TYPE_IMSI = "imsi";
+    public static final String MVNO_TYPE_GID = "gid";
+    public static final String MVNO_TYPE_ICCID = "iccid";
+
+    public static class Builder{
+        private String mEntryName;
+        private String mApnName;
+        private InetAddress mProxy;
+        private int mPort = -1;
+        private URL mMmsc;
+        private InetAddress mMmsProxy;
+        private int mMmsPort = -1;
+        private String mUser;
+        private String mPassword;
+        private int mAuthType;
+        private List<String> mTypes;
+        private int mTypesBitmap;
+        private int mId;
+        private String mOperatorNumeric;
+        private String mProtocol;
+        private String mRoamingProtocol;
+        private int mMtu;
+        private boolean mCarrierEnabled;
+        private int mBearer;
+        private int mBearerBitmask;
+        private int mProfileId;
+        private boolean mModemCognitive;
+        private int mMaxConns;
+        private int mWaitTime;
+        private int mMaxConnsTime;
+        private String mMvnoType;
+        private String mMvnoMatchData;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {}
+
+        /**
+         * Set the MTU size of the mobile interface to which the APN connected.
+         *
+         * @param mtu the MTU size to set for the APN
+         * @hide
+         */
+        public Builder setMtu(int mtu) {
+            this.mMtu = mtu;
+            return this;
+        }
+
+        /**
+         * 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
+         * @hide
+         */
+        public Builder setProfileId(int profileId) {
+            this.mProfileId = profileId;
+            return this;
+        }
+
+        /**
+         * Sets if the APN setting is to be set in modem.
+         *
+         * @param modemCognitive if the APN setting is to be set in modem
+         * @hide
+         */
+        public Builder setModemCognitive(boolean modemCognitive) {
+            this.mModemCognitive = modemCognitive;
+            return this;
+        }
+
+        /**
+         * Sets the max connections of this APN.
+         *
+         * @param maxConns the max connections of this APN
+         * @hide
+         */
+        public Builder setMaxConns(int maxConns) {
+            this.mMaxConns = maxConns;
+            return this;
+        }
+
+        /**
+         * Sets the wait time for retry of the APN.
+         *
+         * @param waitTime the wait time for retry of the APN
+         * @hide
+         */
+        public Builder setWaitTime(int waitTime) {
+            this.mWaitTime = waitTime;
+            return this;
+        }
+
+        /**
+         * Sets the time to limit max connection for the APN.
+         *
+         * @param maxConnsTime the time to limit max connection for the APN
+         * @hide
+         */
+        public Builder setMaxConnsTime(int maxConnsTime) {
+            this.mMaxConnsTime = maxConnsTime;
+            return this;
+        }
+
+        /**
+         * Sets the MVNO match data for the APN.
+         *
+         * @param mvnoMatchData the MVNO match data for the APN
+         * @hide
+         */
+        public Builder setMvnoMatchData(String mvnoMatchData) {
+            this.mMvnoMatchData = mvnoMatchData;
+            return this;
+        }
+
+        /**
+         * Sets the entry name of the APN.
+         *
+         * @param entryName the entry name to set for the APN
+         */
+        public Builder setEntryName(String entryName) {
+            this.mEntryName = entryName;
+            return this;
+        }
+
+        /**
+         * Sets the name of the APN.
+         *
+         * @param apnName the name to set for the APN
+         */
+        public Builder setApnName(String apnName) {
+            this.mApnName = apnName;
+            return this;
+        }
+
+        /**
+         * Sets the proxy address of the APN.
+         *
+         * @param proxy the proxy address to set for the APN
+         */
+        public Builder setProxy(InetAddress proxy) {
+            this.mProxy = proxy;
+            return this;
+        }
+
+        /**
+         * Sets the proxy port of the APN.
+         *
+         * @param port the proxy port to set for the APN
+         */
+        public Builder setPort(int port) {
+            this.mPort = port;
+            return this;
+        }
+
+        /**
+         * Sets the MMSC URL of the APN.
+         *
+         * @param mmsc the MMSC URL to set for the APN
+         */
+        public Builder setMmsc(URL mmsc) {
+            this.mMmsc = mmsc;
+            return this;
+        }
+
+        /**
+         * Sets the MMS proxy address of the APN.
+         *
+         * @param mmsProxy the MMS proxy address to set for the APN
+         */
+        public Builder setMmsProxy(InetAddress mmsProxy) {
+            this.mMmsProxy = mmsProxy;
+            return this;
+        }
+
+        /**
+         * Sets the MMS proxy port of the APN.
+         *
+         * @param mmsPort the MMS proxy port to set for the APN
+         */
+        public Builder setMmsPort(int mmsPort) {
+            this.mMmsPort = mmsPort;
+            return this;
+        }
+
+        /**
+         * Sets the APN username of the APN.
+         *
+         * @param user the APN username to set for the APN
+         */
+        public Builder setUser(String user) {
+            this.mUser = user;
+            return this;
+        }
+
+        /**
+         * Sets the APN password of the APN.
+         *
+         * @see android.provider.Telephony.Carriers#PASSWORD
+         * @param password the APN password to set for the APN
+         */
+        public Builder setPassword(String password) {
+            this.mPassword = password;
+            return this;
+        }
+
+        /**
+         * Sets the authentication type of the APN.
+         *
+         * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
+         *
+         * @param authType the authentication type to set for the APN
+         */
+        public Builder setAuthType(@AuthType int authType) {
+            this.mAuthType = authType;
+            return this;
+        }
+
+        /**
+         * Sets the list of APN types of the APN.
+         *
+         * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+         *
+         * @param types the list of APN types to set for the APN
+         */
+        public Builder setTypes(@ApnType List<String> types) {
+            this.mTypes = types;
+            int apnBitmap = 0;
+            for (int i = 0; i < mTypes.size(); i++) {
+                mTypes.set(i, mTypes.get(i).toLowerCase());
+                apnBitmap |= getApnBitmask(mTypes.get(i));
+            }
+            this.mTypesBitmap = apnBitmap;
+            return this;
+        }
+
+        /**
+         * 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
+         */
+        public Builder setOperatorNumeric(String operatorNumeric) {
+            this.mOperatorNumeric = operatorNumeric;
+            return this;
+        }
+
+        /**
+         * Sets the protocol to use to connect to this APN.
+         *
+         * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
+         * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+         *
+         * @param protocol the protocol to set to use to connect to this APN
+         */
+        public Builder setProtocol(@ProtocolType String protocol) {
+            this.mProtocol = protocol;
+            return this;
+        }
+
+        /**
+         * Sets the protocol to use to connect to this APN when roaming.
+         *
+         * @param roamingProtocol the protocol to set to use to connect to this APN when roaming
+         */
+        public Builder setRoamingProtocol(String roamingProtocol) {
+            this.mRoamingProtocol = roamingProtocol;
+            return this;
+        }
+
+        /**
+         * Sets the current status of APN.
+         *
+         * @param carrierEnabled the current status to set for this APN
+         */
+        public Builder setCarrierEnabled(boolean carrierEnabled) {
+            this.mCarrierEnabled = carrierEnabled;
+            return this;
+        }
+
+        /**
+         * Sets the MVNO match type for this APN.
+         *
+         * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
+         *
+         * @param mvnoType the MVNO match type to set for this APN
+         */
+        public Builder setMvnoType(@MvnoType String mvnoType) {
+            this.mMvnoType = mvnoType;
+            return this;
+        }
+
+        public ApnSetting build() {
+            return new ApnSetting(this);
+        }
+    }
+}
+
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/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
new file mode 100644
index 0000000..29849c1
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -0,0 +1,88 @@
+/*
+ * 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.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.IEuiccCardController;
+import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+
+/**
+ * EuiccCardManager is the application interface to an eSIM card.
+ *
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+public class EuiccCardManager {
+    private static final String TAG = "EuiccCardManager";
+
+    /** Result code of execution with no error. */
+    public static final int RESULT_OK = 0;
+
+    /**
+     * Callback to receive the result of an eUICC card API.
+     *
+     * @param <T> Type of the result.
+     */
+    public interface ResultCallback<T> {
+        /**
+         * This method will be called when an eUICC card API call is completed.
+         *
+         * @param resultCode This can be {@link #RESULT_OK} or other positive values returned by the
+         *     eUICC.
+         * @param result The result object. It can be null if the {@code resultCode} is not
+         *     {@link #RESULT_OK}.
+         */
+        void onComplete(int resultCode, T result);
+    }
+
+    private final Context mContext;
+
+    /** @hide */
+    public EuiccCardManager(Context context) {
+        mContext = context;
+    }
+
+    private IEuiccCardController getIEuiccCardController() {
+        return IEuiccCardController.Stub.asInterface(
+                ServiceManager.getService("euicc_card_controller"));
+    }
+
+    /**
+     * Gets all the profiles on eUicc.
+     *
+     * @param callback the callback to get the result code and all the profiles.
+     */
+    public void getAllProfiles(ResultCallback<EuiccProfileInfo[]> callback) {
+        try {
+            getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(),
+                    new IGetAllProfilesCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
+                            callback.onComplete(resultCode, profiles);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getAllProfiles", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index f777649..3dfadf5 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -60,6 +60,20 @@
     public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
             "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
 
+
+    /**
+     * Broadcast Action: The eUICC OTA status is changed.
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     * 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";
+
     /**
      * Intent action to provision an embedded subscription.
      *
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/telephony/java/android/telephony/euicc/EuiccRat.java b/telephony/java/android/telephony/euicc/EuiccRat.java
new file mode 100644
index 0000000..6a56503a
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccRat.java
@@ -0,0 +1,259 @@
+/*
+ * 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 EuiccRat 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 EuiccRat} 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 EuiccRat build() {
+            if (mPosition != mPolicyRules.length) {
+                throw new IllegalStateException(
+                        "Not enough rules are added, expected: "
+                                + mPolicyRules.length
+                                + ", added: "
+                                + mPosition);
+            }
+            return new EuiccRat(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 EuiccRat(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;
+        }
+
+        EuiccRat that = (EuiccRat) 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 EuiccRat(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<EuiccRat> CREATOR =
+            new Creator<EuiccRat>() {
+                @Override
+                public EuiccRat createFromParcel(Parcel source) {
+                    return new EuiccRat(source);
+                }
+
+                @Override
+                public EuiccRat[] newArray(int size) {
+                    return new EuiccRat[size];
+                }
+            };
+}
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/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/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/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
similarity index 65%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
index d750363..2846a1a 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.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,11 @@
  * limitations under the License.
  */
 
-/** @hide */
-package android.telephony.data;
+package com.android.internal.telephony.euicc;
 
-parcelable InterfaceAddress;
+import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+
+/** @hide */
+interface IEuiccCardController {
+    oneway void getAllProfiles(String callingPackage, in IGetAllProfilesCallback callback);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl
similarity index 68%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl
index d750363..97b0768 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.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 IGetAllProfilesCallback {
+    void onComplete(int resultCode, in EuiccProfileInfo[] profiles);
+}
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/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/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/core/tests/coretests/src/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/NetworkUtilsTest.java
rename to tests/net/java/android/net/NetworkUtilsTest.java
diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/tests/net/java/android/net/RouteInfoTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/RouteInfoTest.java
rename to tests/net/java/android/net/RouteInfoTest.java
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 725ddb9..9b75a50 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -35,6 +35,7 @@
 import android.net.ip.IpManager;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.RaEvent;
+import android.net.util.InterfaceParams;
 import android.os.ConditionVariable;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -62,7 +63,6 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.InetAddress;
-import java.net.NetworkInterface;
 import java.nio.ByteBuffer;
 import java.util.List;
 import java.util.Random;
@@ -635,7 +635,7 @@
 
         public TestApfFilter(ApfConfiguration config, IpManager.Callback ipManagerCallback,
                 IpConnectivityLog log) throws Exception {
-            super(config, NetworkInterface.getByName("lo"), ipManagerCallback, log);
+            super(config, InterfaceParams.getByName("lo"), ipManagerCallback, log);
         }
 
         // Pretend an RA packet has been received and show it to ApfFilter.
diff --git a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
index 54776db..e65585f 100644
--- a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
+++ b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.when;
 
+import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.os.Looper;
@@ -54,8 +55,8 @@
     }
 
     IpReachabilityMonitor makeMonitor() {
-        return new IpReachabilityMonitor(
-                "fake0", 1, mHandler, mLog, mCallback, null, mDependencies);
+        final InterfaceParams ifParams = new InterfaceParams("fake0", 1, null);
+        return new IpReachabilityMonitor(ifParams, mHandler, mLog, mCallback, null, mDependencies);
     }
 
     @Test
diff --git a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
index 38d3d74..f9b7ec8 100644
--- a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
+++ b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.net.MacAddress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.filters.SmallTest;
 
@@ -36,9 +37,7 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ConnectivityPacketSummaryTest {
-    private static final byte[] MYHWADDR = {
-        asByte(0x80), asByte(0x7a), asByte(0xbf), asByte(0x6f), asByte(0x48), asByte(0xf3)
-    };
+    private static final MacAddress MYHWADDR = MacAddress.fromString("80:7a:bf:6f:48:f3");
 
     private String getSummary(String hexBytes) {
         hexBytes = hexBytes.replaceAll("\\s+", "");
diff --git a/tests/net/java/android/net/util/InterfaceParamsTest.java b/tests/net/java/android/net/util/InterfaceParamsTest.java
new file mode 100644
index 0000000..21728af
--- /dev/null
+++ b/tests/net/java/android/net/util/InterfaceParamsTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package android.net.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InterfaceParamsTest {
+    @Test
+    public void testNullInterfaceReturnsNull() {
+        assertNull(InterfaceParams.getByName(null));
+    }
+
+    @Test
+    public void testNonExistentInterfaceReturnsNull() {
+        assertNull(InterfaceParams.getByName("doesnotexist0"));
+    }
+
+    @Test
+    public void testLoopback() {
+        final InterfaceParams ifParams = InterfaceParams.getByName("lo");
+        assertNotNull(ifParams);
+        assertEquals("lo", ifParams.name);
+        assertTrue(ifParams.index > 0);
+        assertNotNull(ifParams.macAddr);
+        assertTrue(ifParams.defaultMtu >= NetworkConstants.ETHER_MTU);
+    }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 113cd37..b8e37f3 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -55,14 +55,20 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -116,6 +122,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.WakeupMessage;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
@@ -132,6 +139,7 @@
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
@@ -174,8 +182,11 @@
 
     @Mock IpConnectivityMetrics.Logger mMetricsService;
     @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
+    @Mock INetworkManagementService mNetworkManagementService;
     @Mock INetworkStatsService mStatsService;
 
+    private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
+
     // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods
     // do not go through ConnectivityService but talk to netd directly, so they don't automatically
     // reflect the state of our test ConnectivityService.
@@ -872,7 +883,7 @@
         LocalServices.addService(
                 NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
         mService = new WrappedConnectivityService(mServiceContext,
-                mock(INetworkManagementService.class),
+                mNetworkManagementService,
                 mStatsService,
                 mock(INetworkPolicyManager.class),
                 mock(IpConnectivityLog.class));
@@ -1381,39 +1392,75 @@
             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 assertNoCallback() {
@@ -1450,8 +1497,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);
@@ -1465,8 +1512,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);
@@ -1489,8 +1536,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);
 
@@ -1502,10 +1549,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);
@@ -1541,32 +1588,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;
@@ -1583,7 +1630,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());
@@ -1603,7 +1650,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);
 
@@ -1619,22 +1666,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.
@@ -1642,33 +1689,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);
@@ -1676,12 +1723,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);
@@ -1700,7 +1747,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
@@ -1709,8 +1756,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();
@@ -1726,14 +1773,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);
@@ -1760,13 +1807,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());
@@ -1789,7 +1836,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.
@@ -1800,7 +1847,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());
@@ -1809,7 +1856,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();
 
@@ -1982,7 +2029,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
@@ -2011,7 +2058,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
@@ -2038,7 +2085,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.
@@ -2051,7 +2098,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.
@@ -2061,9 +2108,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.
@@ -2087,7 +2132,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.
@@ -2098,7 +2143,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.
@@ -2111,7 +2156,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);
@@ -2154,7 +2199,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();
     }
@@ -2192,14 +2237,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);
         }
@@ -2208,7 +2253,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);
         }
@@ -2337,14 +2382,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();
@@ -2354,7 +2399,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
@@ -2362,7 +2407,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);
@@ -2383,7 +2428,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.
@@ -2404,7 +2449,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);
@@ -2444,18 +2489,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));
@@ -2479,8 +2524,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);
@@ -2500,7 +2545,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);
@@ -2640,7 +2685,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.
 
@@ -2731,16 +2776,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.
@@ -2761,18 +2805,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(
@@ -2783,9 +2827,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.
@@ -2799,7 +2842,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(
@@ -2810,17 +2853,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);
@@ -2862,7 +2905,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();
@@ -2883,7 +2926,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);
 
@@ -3370,7 +3413,7 @@
 
         // Bring up wifi aware network.
         wifiAware.connect(false, false);
-        callback.expectAvailableCallbacks(wifiAware);
+        callback.expectAvailableCallbacksUnvalidated(wifiAware);
 
         assertNull(mCm.getActiveNetworkInfo());
         assertNull(mCm.getActiveNetwork());
@@ -3489,6 +3532,44 @@
         reset(mStatsService);
     }
 
+    @Test
+    public void testBasicDnsConfigurationPushed() throws Exception {
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        waitForIdle();
+        verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
+                anyInt(), any(), any(), any(), anyBoolean(), anyString());
+
+        final LinkProperties cellLp = new LinkProperties();
+        cellLp.setInterfaceName("test_rmnet_data0");
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        mCellNetworkAgent.connect(false);
+        waitForIdle();
+        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+                anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+        // CS tells netd about the empty DNS config for this network.
+        assertEmpty(mStringArrayCaptor.getValue());
+        reset(mNetworkManagementService);
+
+        cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        waitForIdle();
+        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+                anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+        assertEquals(1, mStringArrayCaptor.getValue().length);
+        assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1"));
+        reset(mNetworkManagementService);
+
+        cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        waitForIdle();
+        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+                anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+        assertEquals(2, mStringArrayCaptor.getValue().length);
+        assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
+                new String[]{"2001:db8::1", "192.0.2.1"}));
+        reset(mNetworkManagementService);
+    }
+
     private void checkDirectlyConnectedRoutes(Object callbackObj,
             Collection<LinkAddress> linkAddresses, Collection<RouteInfo> otherRoutes) {
         assertTrue(callbackObj instanceof LinkProperties);