Merge "Used LinkAddress insteaad of InterfaceAddress"
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..2d74249 100644
--- a/Android.mk
+++ b/Android.mk
@@ -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 6702b94..392f4db 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -40931,6 +40931,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 d8f32d9..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 {
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..fb27f742 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..7bda37e 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);
@@ -1971,13 +1971,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 +1990,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 +2004,7 @@
                             + mBoundApplication.processName
                             + "/" + mBoundApplication.appInfo.uid);
                 }
-                return packageInfo;
+                return loadedApk;
             }
         }
 
@@ -2019,13 +2019,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 +2047,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 +2068,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 +2083,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 +2645,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 +2683,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 +2809,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 +2817,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 =
@@ -3145,7 +3145,7 @@
 
         String component = data.intent.getComponent().getClassName();
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(
+        LoadedApk loadedApk = getLoadedApkNoCheck(
                 data.info.applicationInfo, data.compatInfo);
 
         IActivityManager mgr = ActivityManager.getService();
@@ -3154,7 +3154,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 +3178,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 +3225,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 +3252,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 +3292,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 +3313,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 +3330,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 +3943,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 +4238,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 +4739,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 +5209,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 +5220,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 +5247,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 +5275,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 +5290,7 @@
                 break;
             }
         }
-        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo);
+        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk);
     }
 
     final void handleLowMemory() {
@@ -5494,7 +5494,7 @@
             applyCompatConfiguration(mCurDefaultDisplayDpi);
         }
 
-        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+        data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
 
         /**
          * Switch this process to density compatibility mode if needed.
@@ -5562,7 +5562,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 +5581,7 @@
                 }
 
             } else {
-                Slog.w(TAG, "Application " + data.info.getPackageName()
+                Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
                       + " can be debugged on port 8100...");
             }
         }
@@ -5629,14 +5629,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 +5666,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 +5712,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 +5766,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 +6361,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/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 ab70f0e7..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/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/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/euicc/EuiccProfileInfo.aidl b/core/java/android/service/euicc/EuiccProfileInfo.aidl
new file mode 100644
index 0000000..321021b
--- /dev/null
+++ b/core/java/android/service/euicc/EuiccProfileInfo.aidl
@@ -0,0 +1,18 @@
+/*
+ * 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.service.euicc;
+
+parcelable EuiccProfileInfo;
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/core/java/android/service/euicc/IOtaStatusChangedCallback.aidl b/core/java/android/service/euicc/IOtaStatusChangedCallback.aidl
new file mode 100644
index 0000000..caec75f
--- /dev/null
+++ b/core/java/android/service/euicc/IOtaStatusChangedCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/** @hide */
+oneway interface IOtaStatusChangedCallback {
+    void onOtaStatusChanged(int status);
+}
\ No newline at end of file
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/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/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/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5228498..f34730c 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;
@@ -232,8 +233,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 +406,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 +857,8 @@
         mMultinetworkPolicyTracker = createMultinetworkPolicyTracker(
                 mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
         mMultinetworkPolicyTracker.start();
+
+        mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
     }
 
     private Tethering makeTethering() {
@@ -1803,24 +1805,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(
@@ -4558,41 +4542,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) {
@@ -4865,7 +4825,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) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8a15ded..40e6d26 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);
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dce0c38..6cdf071 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;
@@ -12497,7 +12482,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 +12636,9 @@
                 Binder.restoreCallingIdentity(ident);
             }
         }
-        sendNotifyVrManagerOfKeyguardState(showing);
+
+        mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, showing ? 1 : 0, 0)
+                .sendToTarget();
     }
 
     @Override
@@ -24258,6 +24246,11 @@
                 }
             }
         }
+
+        @Override
+        public void registerScreenObserver(ScreenObserver observer) {
+            mScreenObservers.add(observer);
+        }
     }
 
     /**
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/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/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 ed78175b..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/data/ApnSetting.aidl b/telephony/java/android/telephony/data/ApnSetting.aidl
new file mode 100644
index 0000000..381e5e8
--- /dev/null
+++ b/telephony/java/android/telephony/data/ApnSetting.aidl
@@ -0,0 +1,18 @@
+/*
+ * 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;
+
+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/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/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
new file mode 100644
index 0000000..2846a1a
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.internal.telephony.euicc;
+
+import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+
+/** @hide */
+interface IEuiccCardController {
+    oneway void getAllProfiles(String callingPackage, in IGetAllProfilesCallback callback);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl
new file mode 100644
index 0000000..97b0768
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
+
+/** @hide */
+oneway interface IGetAllProfilesCallback {
+    void onComplete(int resultCode, in EuiccProfileInfo[] profiles);
+}
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..2b0349c 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));
@@ -3489,6 +3500,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);