Merge "Overload TileUtils.getCategories() with an additional parameteri."
diff --git a/api/current.txt b/api/current.txt
index 89573ac..395d7aa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12733,7 +12733,6 @@
     field public static final int RGBA_8888 = 1; // 0x1
     field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
-    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
diff --git a/api/system-current.txt b/api/system-current.txt
index 870070b..f38b02e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -13226,7 +13226,6 @@
     field public static final int RGBA_8888 = 1; // 0x1
     field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
-    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
@@ -26057,7 +26056,7 @@
     field public static final java.lang.String EXTRA_SEQUENCE = "android.net.extra.SEQUENCE";
   }
 
-  public static final class NetworkRecommendationProvider.ResultCallback {
+  public static class NetworkRecommendationProvider.ResultCallback {
     method public void onResult(android.net.RecommendationResult);
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 10554ff..e43d1b5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -12764,7 +12764,6 @@
     field public static final int RGBA_8888 = 1; // 0x1
     field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
-    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 9c17780..72ccf72 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -185,6 +185,11 @@
         mWindow = w;
         w.setCallback(this);
         w.setOnWindowDismissedCallback(this);
+        w.setOnWindowSwipeDismissedCallback(() -> {
+            if (mCancelable) {
+                cancel();
+            }
+        });
         w.setWindowManager(mWindowManager, null, null);
         w.setGravity(Gravity.CENTER);
 
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 94d24e4..db1162a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -51,6 +51,7 @@
 import android.view.Display;
 import android.view.DisplayAdjustments;
 
+import dalvik.system.BaseDexClassLoader;
 import dalvik.system.VMRuntime;
 
 import java.io.File;
@@ -600,6 +601,40 @@
 
         VMRuntime.registerAppInfo(profileFile.getPath(), mApplicationInfo.dataDir,
                 codePaths.toArray(new String[codePaths.size()]), foreignDexProfilesFile.getPath());
+
+        // Setup the reporter to notify package manager of any relevant dex loads.
+        // At this point the primary apk is loaded and will not be reported.
+        // Anything loaded from now on will be tracked as a potential secondary
+        // or foreign dex file. The goal is to enable:
+        //    1) monitoring and compilation of secondary dex file
+        //    2) track foreign dex file usage (used to determined the
+        //       compilation filter of apks).
+        if (BaseDexClassLoader.getReporter() != DexLoadReporter.INSTANCE) {
+            // Set the dex load reporter if not already set.
+            // Note that during the app's life cycle different LoadedApks may be
+            // created and loaded (e.g. if two different apps share the same runtime).
+            BaseDexClassLoader.setReporter(DexLoadReporter.INSTANCE);
+        }
+    }
+
+    private static class DexLoadReporter implements BaseDexClassLoader.Reporter {
+        private static final DexLoadReporter INSTANCE = new DexLoadReporter();
+
+        private DexLoadReporter() {}
+
+        @Override
+        public void report(List<String> dexPaths) {
+            if (dexPaths.isEmpty()) {
+                return;
+            }
+            String packageName = ActivityThread.currentPackageName();
+            try {
+                ActivityThread.getPackageManager().notifyDexLoad(
+                        packageName, dexPaths, VMRuntime.getRuntime().vmInstructionSet());
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b641e63..0e1407f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -385,7 +385,7 @@
             "com.android.server.action.BUGREPORT_SHARING_DECLINED";
 
     /**
-     * Action: Bugreport has been collected and is dispatched to {@link DevicePolicyManagerService}.
+     * Action: Bugreport has been collected and is dispatched to {@code DevicePolicyManagerService}.
      *
      * @hide
      */
@@ -1165,7 +1165,7 @@
     public @interface UserProvisioningState {}
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
      * {@link #ACTION_PROVISION_MANAGED_PROFILE}, {@link #ACTION_PROVISION_MANAGED_USER} and
@@ -1176,7 +1176,7 @@
     public static final int CODE_OK = 0;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
      * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the device already has a device
@@ -1187,7 +1187,7 @@
     public static final int CODE_HAS_DEVICE_OWNER = 1;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
      * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the user has a profile owner and for
@@ -1198,7 +1198,7 @@
     public static final int CODE_USER_HAS_PROFILE_OWNER = 2;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
      * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the user isn't running.
@@ -1208,7 +1208,7 @@
     public static final int CODE_USER_NOT_RUNNING = 3;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
      * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} if the device has already been setup and
@@ -1233,7 +1233,7 @@
     public static final int CODE_ACCOUNTS_NOT_EMPTY = 6;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
      * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} if the user is not a system user.
@@ -1243,7 +1243,7 @@
     public static final int CODE_NOT_SYSTEM_USER = 7;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
      * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} and {@link #ACTION_PROVISION_MANAGED_USER}
@@ -1254,7 +1254,7 @@
     public static final int CODE_HAS_PAIRED = 8;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} and
      * {@link #ACTION_PROVISION_MANAGED_USER} on devices which do not support managed users.
@@ -1265,7 +1265,7 @@
     public static final int CODE_MANAGED_USERS_NOT_SUPPORTED = 9;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_USER} if the user is a system user.
      *
@@ -1274,7 +1274,7 @@
     public static final int CODE_SYSTEM_USER = 10;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the user cannot have more
      * managed profiles.
@@ -1284,7 +1284,7 @@
     public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_USER} and
      * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} on devices not running with split system
@@ -1295,7 +1295,7 @@
     public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
      * {@link #ACTION_PROVISION_MANAGED_PROFILE}, {@link #ACTION_PROVISION_MANAGED_USER} and
@@ -1307,7 +1307,7 @@
     public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13;
 
     /**
-     * Result code for {@link checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the device the user is a
      * system user on a split system user device.
@@ -1317,7 +1317,17 @@
     public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14;
 
     /**
-     * Result codes for {@link checkProvisioningPreCondition} indicating all the provisioning pre
+     * Result code for {@link #checkProvisioningPreCondition}.
+     *
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when adding a managed profile is
+     * disallowed by {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}.
+     *
+     * @hide
+     */
+    public static final int CODE_ADD_MANAGED_PROFILE_DISALLOWED = 15;
+
+    /**
+     * Result codes for {@link #checkProvisioningPreCondition} indicating all the provisioning pre
      * conditions.
      *
      * @hide
@@ -1327,7 +1337,7 @@
             CODE_USER_SETUP_COMPLETED, CODE_NOT_SYSTEM_USER, CODE_HAS_PAIRED,
             CODE_MANAGED_USERS_NOT_SUPPORTED, CODE_SYSTEM_USER, CODE_CANNOT_ADD_MANAGED_PROFILE,
             CODE_NOT_SYSTEM_USER_SPLIT, CODE_DEVICE_ADMIN_NOT_SUPPORTED,
-            CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER})
+            CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER, CODE_ADD_MANAGED_PROFILE_DISALLOWED})
     public @interface ProvisioningPreCondition {}
 
     /**
@@ -2294,7 +2304,7 @@
      * Determine whether the current password the user has set is sufficient to meet the policy
      * requirements (e.g. quality, minimum length) that have been requested by the admins of this
      * user and its participating profiles. Restrictions on profiles that have a separate challenge
-     * are not taken into account.
+     * are not taken into account. The user must be unlocked in order to perform the check.
      * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
@@ -2307,6 +2317,7 @@
      * @return Returns true if the password meets the current requirements, else false.
      * @throws SecurityException if the calling application does not own an active administrator
      *             that uses {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+     * @throws InvalidStateException if the user is not unlocked.
      */
     public boolean isActivePasswordSufficient() {
         if (mService != null) {
@@ -3817,6 +3828,19 @@
     /**
      * @hide
      */
+    public void reportPasswordChanged(@UserIdInt int userId) {
+        if (mService != null) {
+            try {
+                mService.reportPasswordChanged(userId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
     public void reportFailedPasswordAttempt(int userHandle) {
         if (mService != null) {
             try {
@@ -6184,34 +6208,40 @@
     }
 
     /**
-     * Returns if provisioning a managed profile or device is possible or not.
+     * Returns whether it is possible for the caller to initiate provisioning of a managed profile
+     * or device, setting itself as the device or profile owner.
+     *
      * @param action One of {@link #ACTION_PROVISION_MANAGED_DEVICE},
      * {@link #ACTION_PROVISION_MANAGED_PROFILE}.
-     * @return if provisioning a managed profile or device is possible or not.
+     * @return whether provisioning a managed profile or device is possible.
      * @throws IllegalArgumentException if the supplied action is not valid.
      */
-    public boolean isProvisioningAllowed(String action) {
+    public boolean isProvisioningAllowed(@NonNull String action) {
         throwIfParentInstance("isProvisioningAllowed");
         try {
-            return mService.isProvisioningAllowed(action);
+            return mService.isProvisioningAllowed(action, mContext.getPackageName());
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Checks if provisioning a managed profile or device is possible and returns one of the
-     * {@link ProvisioningPreCondition}.
+     * Checks whether it is possible to initiate provisioning a managed device,
+     * profile or user, setting the given package as owner.
      *
      * @param action One of {@link #ACTION_PROVISION_MANAGED_DEVICE},
      *        {@link #ACTION_PROVISION_MANAGED_PROFILE},
      *        {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE},
      *        {@link #ACTION_PROVISION_MANAGED_USER}
+     * @param packageName The package of the component that would be set as device, user, or profile
+     *        owner.
+     * @return A {@link ProvisioningPreCondition} value indicating whether provisioning is allowed.
      * @hide
      */
-    public @ProvisioningPreCondition int checkProvisioningPreCondition(String action) {
+    public @ProvisioningPreCondition int checkProvisioningPreCondition(
+            String action, @NonNull String packageName) {
         try {
-            return mService.checkProvisioningPreCondition(action);
+            return mService.checkProvisioningPreCondition(action, packageName);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -6940,8 +6970,8 @@
      * @hide
      * Force update user setup completed status. This API has no effect on user build.
      * @throws {@link SecurityException} if the caller has no
-     *         {@link android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS} or the caller is
-     *         not {@link UserHandle.SYSTEM_USER}
+     *         {@code android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS} or the caller is
+     *         not {@link UserHandle#SYSTEM_USER}
      */
     public void forceUpdateUserSetupComplete() {
         try {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 3e22825..9be694e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -123,6 +123,7 @@
     boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle);
 
     void setActivePasswordState(in PasswordMetrics metrics, int userHandle);
+    void reportPasswordChanged(int userId);
     void reportFailedPasswordAttempt(int userHandle);
     void reportSuccessfulPasswordAttempt(int userHandle);
     void reportFailedFingerprintAttempt(int userHandle);
@@ -269,8 +270,8 @@
     boolean setPermissionGrantState(in ComponentName admin, String packageName,
             String permission, int grantState);
     int getPermissionGrantState(in ComponentName admin, String packageName, String permission);
-    boolean isProvisioningAllowed(String action);
-    int checkProvisioningPreCondition(String action);
+    boolean isProvisioningAllowed(String action, String packageName);
+    int checkProvisioningPreCondition(String action, String packageName);
     void setKeepUninstalledPackages(in ComponentName admin,in List<String> packageList);
     List<String> getKeepUninstalledPackages(in ComponentName admin);
     boolean isManagedProfile(in ComponentName admin);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 11da8dd..f0f1d99b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2499,9 +2499,8 @@
      * for high frequency calls.
      * </p>
      *
-     * @param service Identifies the service to be started.  The Intent must be either
-     *      fully explicit (supplying a component name) or specify a specific package
-     *      name it is targetted to.  Additional values
+     * @param service Identifies the service to be started.  The Intent must be
+     *      fully explicit (supplying a component name).  Additional values
      *      may be included in the Intent extras to supply arguments along with
      *      this specific start call.
      *
@@ -2579,10 +2578,8 @@
      * {@link #registerReceiver}, since the lifetime of this BroadcastReceiver
      * is tied to another object (the one that registered it).</p>
      *
-     * @param service Identifies the service to connect to.  The Intent may
-     *      specify either an explicit component name, or a logical
-     *      description (action, category, etc) to match an
-     *      {@link IntentFilter} published by a service.
+     * @param service Identifies the service to connect to.  The Intent must
+     *      specify an explicit component name.
      * @param conn Receives information as the service is started and stopped.
      *      This must be a valid ServiceConnection object; it must not be null.
      * @param flags Operation options for the binding.  May be 0,
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d753a6e..b9b61db 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -458,6 +458,15 @@
     void notifyPackageUse(String packageName, int reason);
 
     /**
+     * Notify the package manager that a list of dex files have been loaded.
+     *
+     * @param loadingPackageName the name of the package who performs the load
+     * @param dexPats the list of the dex files paths that have been loaded
+     * @param loaderIsa the ISA of the loader process
+     */
+    void notifyDexLoad(String loadingPackageName, in List<String> dexPaths, String loaderIsa);
+
+    /**
      * Ask the package manager to perform dex-opt (if needed) on the given
      * package if it already hasn't done so.
      *
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 74aded6..2236291 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -24,7 +24,10 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -58,7 +61,6 @@
 import android.view.Gravity;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -85,7 +87,6 @@
 import libcore.io.IoUtils;
 
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
 import static android.content.pm.ActivityInfo.FLAG_ON_TOP_LAUNCHER;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
@@ -2103,63 +2104,22 @@
 
                     sa.recycle();
 
-                    if (minCode != null) {
-                        boolean allowedCodename = false;
-                        for (String codename : SDK_CODENAMES) {
-                            if (minCode.equals(codename)) {
-                                allowedCodename = true;
-                                break;
-                            }
-                        }
-                        if (!allowedCodename) {
-                            if (SDK_CODENAMES.length > 0) {
-                                outError[0] = "Requires development platform " + minCode
-                                        + " (current platform is any of "
-                                        + Arrays.toString(SDK_CODENAMES) + ")";
-                            } else {
-                                outError[0] = "Requires development platform " + minCode
-                                        + " but this is a release platform.";
-                            }
-                            mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
-                            return null;
-                        }
-                        pkg.applicationInfo.minSdkVersion =
-                                android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
-                    } else if (minVers > SDK_VERSION) {
-                        outError[0] = "Requires newer sdk version #" + minVers
-                                + " (current version is #" + SDK_VERSION + ")";
+                    final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, minCode,
+                            SDK_VERSION, SDK_CODENAMES, outError);
+                    if (minSdkVersion < 0) {
                         mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                         return null;
-                    } else {
-                        pkg.applicationInfo.minSdkVersion = minVers;
                     }
 
-                    if (targetCode != null) {
-                        boolean allowedCodename = false;
-                        for (String codename : SDK_CODENAMES) {
-                            if (targetCode.equals(codename)) {
-                                allowedCodename = true;
-                                break;
-                            }
-                        }
-                        if (!allowedCodename) {
-                            if (SDK_CODENAMES.length > 0) {
-                                outError[0] = "Requires development platform " + targetCode
-                                        + " (current platform is any of "
-                                        + Arrays.toString(SDK_CODENAMES) + ")";
-                            } else {
-                                outError[0] = "Requires development platform " + targetCode
-                                        + " but this is a release platform.";
-                            }
-                            mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
-                            return null;
-                        }
-                        // If the code matches, it definitely targets this SDK.
-                        pkg.applicationInfo.targetSdkVersion
-                                = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
-                    } else {
-                        pkg.applicationInfo.targetSdkVersion = targetVers;
+                    final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers,
+                            targetCode, SDK_VERSION, SDK_CODENAMES, outError);
+                    if (targetSdkVersion < 0) {
+                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
+                        return null;
                     }
+
+                    pkg.applicationInfo.minSdkVersion = minSdkVersion;
+                    pkg.applicationInfo.targetSdkVersion = targetSdkVersion;
                 }
 
                 XmlUtils.skipCurrentTag(parser);
@@ -2407,6 +2367,137 @@
         return pkg;
     }
 
+    /**
+     * Computes the targetSdkVersion to use at runtime. If the package is not
+     * compatible with this platform, populates {@code outError[0]} with an
+     * error message.
+     * <p>
+     * If {@code targetCode} is not specified, e.g. the value is {@code null},
+     * then the {@code targetVers} will be returned unmodified.
+     * <p>
+     * Otherwise, the behavior varies based on whether the current platform
+     * is a pre-release version, e.g. the {@code platformSdkCodenames} array
+     * has length > 0:
+     * <ul>
+     * <li>If this is a pre-release platform and the value specified by
+     * {@code targetCode} is contained within the array of allowed pre-release
+     * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
+     * <li>If this is a released platform, this method will return -1 to
+     * indicate that the package is not compatible with this platform.
+     * </ul>
+     *
+     * @param targetVers targetSdkVersion number, if specified in the
+     *                   application manifest, or 0 otherwise
+     * @param targetCode targetSdkVersion code, if specified in the application
+     *                   manifest, or {@code null} otherwise
+     * @param platformSdkVersion platform SDK version number, typically
+     *                           Build.VERSION.SDK_INT
+     * @param platformSdkCodenames array of allowed pre-release SDK codenames
+     *                             for this platform
+     * @param outError output array to populate with error, if applicable
+     * @return the targetSdkVersion to use at runtime, or -1 if the package is
+     *         not compatible with this platform
+     * @hide Exposed for unit testing only.
+     */
+    @TestApi
+    public static int computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
+            @Nullable String targetCode, @IntRange(from = 1) int platformSdkVersion,
+            @NonNull String[] platformSdkCodenames, @NonNull String[] outError) {
+        // If it's a release SDK, return the version number unmodified.
+        if (targetCode == null) {
+            return targetVers;
+        }
+
+        // If it's a pre-release SDK and the codename matches this platform, it
+        // definitely targets this SDK.
+        if (ArrayUtils.contains(platformSdkCodenames, targetCode)) {
+            return Build.VERSION_CODES.CUR_DEVELOPMENT;
+        }
+
+        // Otherwise, we're looking at an incompatible pre-release SDK.
+        if (platformSdkCodenames.length > 0) {
+            outError[0] = "Requires development platform " + targetCode
+                    + " (current platform is any of "
+                    + Arrays.toString(platformSdkCodenames) + ")";
+        } else {
+            outError[0] = "Requires development platform " + targetCode
+                    + " but this is a release platform.";
+        }
+        return -1;
+    }
+
+    /**
+     * Computes the minSdkVersion to use at runtime. If the package is not
+     * compatible with this platform, populates {@code outError[0]} with an
+     * error message.
+     * <p>
+     * If {@code minCode} is not specified, e.g. the value is {@code null},
+     * then behavior varies based on the {@code platformSdkVersion}:
+     * <ul>
+     * <li>If the platform SDK version is greater than or equal to the
+     * {@code minVers}, returns the {@code mniVers} unmodified.
+     * <li>Otherwise, returns -1 to indicate that the package is not
+     * compatible with this platform.
+     * </ul>
+     * <p>
+     * Otherwise, the behavior varies based on whether the current platform
+     * is a pre-release version, e.g. the {@code platformSdkCodenames} array
+     * has length > 0:
+     * <ul>
+     * <li>If this is a pre-release platform and the value specified by
+     * {@code targetCode} is contained within the array of allowed pre-release
+     * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
+     * <li>If this is a released platform, this method will return -1 to
+     * indicate that the package is not compatible with this platform.
+     * </ul>
+     *
+     * @param minVers minSdkVersion number, if specified in the application
+     *                manifest, or 1 otherwise
+     * @param minCode minSdkVersion code, if specified in the application
+     *                manifest, or {@code null} otherwise
+     * @param platformSdkVersion platform SDK version number, typically
+     *                           Build.VERSION.SDK_INT
+     * @param platformSdkCodenames array of allowed prerelease SDK codenames
+     *                             for this platform
+     * @param outError output array to populate with error, if applicable
+     * @return the minSdkVersion to use at runtime, or -1 if the package is not
+     *         compatible with this platform
+     * @hide Exposed for unit testing only.
+     */
+    @TestApi
+    public static int computeMinSdkVersion(@IntRange(from = 1) int minVers,
+            @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion,
+            @NonNull String[] platformSdkCodenames, @NonNull String[] outError) {
+        // If it's a release SDK, make sure we meet the minimum SDK requirement.
+        if (minCode == null) {
+            if (minVers <= platformSdkVersion) {
+                return minVers;
+            }
+
+            // We don't meet the minimum SDK requirement.
+            outError[0] = "Requires newer sdk version #" + minVers
+                    + " (current version is #" + platformSdkVersion + ")";
+            return -1;
+        }
+
+        // If it's a pre-release SDK and the codename matches this platform, we
+        // definitely meet the minimum SDK requirement.
+        if (ArrayUtils.contains(platformSdkCodenames, minCode)) {
+            return Build.VERSION_CODES.CUR_DEVELOPMENT;
+        }
+
+        // Otherwise, we're looking at an incompatible pre-release SDK.
+        if (platformSdkCodenames.length > 0) {
+            outError[0] = "Requires development platform " + minCode
+                    + " (current platform is any of "
+                    + Arrays.toString(platformSdkCodenames) + ")";
+        } else {
+            outError[0] = "Requires development platform " + minCode
+                    + " but this is a release platform.";
+        }
+        return -1;
+    }
+
     private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs) {
         FeatureInfo fi = new FeatureInfo();
         TypedArray sa = res.obtainAttributes(attrs,
@@ -3789,6 +3880,8 @@
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
         }
 
+        final boolean hasVisibleToEphemeral =
+                sa.hasValue(R.styleable.AndroidManifestActivity_visibleToEphemeral);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToEphemeral, false);
@@ -3827,7 +3920,7 @@
                     return null;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
                 if (intent.countActions() == 0) {
                     Slog.w(TAG, "No actions in intent filter at "
                             + mArchiveSourcePath + " "
@@ -3835,6 +3928,10 @@
                 } else {
                     a.intents.add(intent);
                 }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
             } else if (!receiver && parser.getName().equals("preferred")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
                 if (!parseIntent(res, parser, false /*allowGlobs*/, false /*allowAutoVerify*/,
@@ -3842,7 +3939,7 @@
                     return null;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
                 if (intent.countActions() == 0) {
                     Slog.w(TAG, "No actions in preferred at "
                             + mArchiveSourcePath + " "
@@ -3853,6 +3950,10 @@
                     }
                     owner.preferredActivityFilters.add(intent);
                 }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
             } else if (parser.getName().equals("meta-data")) {
                 if ((a.metaData = parseMetaData(res, parser, a.metaData,
                         outError)) == null) {
@@ -4093,6 +4194,7 @@
             }
         }
 
+        // TODO add visibleToInstantApp attribute to activity alias
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || ((a.info.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0);
@@ -4124,9 +4226,14 @@
                             + parser.getPositionDescription());
                 } else {
                     intent.setEphemeral(isEphemeral);
-                    intent.setVisibleToEphemeral(visibleToEphemeral);
+                    intent.setVisibleToEphemeral(visibleToEphemeral
+                            || isWebBrowsableIntent(intent));
                     a.intents.add(intent);
                 }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (intent.isVisibleToEphemeral()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
             } else if (parser.getName().equals("meta-data")) {
                 if ((a.metaData=parseMetaData(res, parser, a.metaData,
                         outError)) == null) {
@@ -4262,6 +4369,8 @@
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
         }
 
+        final boolean hasVisibleToEphemeral =
+                sa.hasValue(R.styleable.AndroidManifestProvider_visibleToEphemeral);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToEphemeral, false);
@@ -4291,7 +4400,8 @@
         }
         p.info.authority = cpname.intern();
 
-        if (!parseProviderTags(res, parser, isEphemeral, visibleToEphemeral, p, outError)) {
+        if (!parseProviderTags(
+                res, parser, isEphemeral, hasVisibleToEphemeral, visibleToEphemeral, p, outError)) {
             return null;
         }
 
@@ -4299,8 +4409,9 @@
     }
 
     private boolean parseProviderTags(Resources res, XmlResourceParser parser,
-            boolean isEphemeral, boolean visibleToEphemeral, Provider outInfo, String[] outError)
-            throws XmlPullParserException, IOException {
+            boolean isEphemeral, boolean hasVisibleToEphemeral, boolean visibleToEphemeral,
+            Provider outInfo, String[] outError)
+                    throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4317,8 +4428,12 @@
                     return false;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
                 outInfo.intents.add(intent);
+                // adjust provider flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
 
             } else if (parser.getName().equals("meta-data")) {
                 if ((outInfo.metaData=parseMetaData(res, parser,
@@ -4565,6 +4680,8 @@
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
         }
 
+        final boolean hasVisibleToEphemeral =
+                sa.hasValue(R.styleable.AndroidManifestService_visibleToEphemeral);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || sa.getBoolean(R.styleable.AndroidManifestService_visibleToEphemeral, false);
@@ -4600,8 +4717,11 @@
                     return null;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
-
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
                 s.intents.add(intent);
             } else if (parser.getName().equals("meta-data")) {
                 if ((s.metaData=parseMetaData(res, parser, s.metaData,
@@ -4629,6 +4749,12 @@
         return s;
     }
 
+    private boolean isWebBrowsableIntent(IntentInfo intent) {
+        return intent.hasAction(Intent.ACTION_VIEW)
+                && intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+                && (intent.hasDataScheme("http") || intent.hasDataScheme("https"));
+    }
+
     private boolean parseAllMetaData(Resources res, XmlResourceParser parser, String tag,
             Component<?> outInfo, String[] outError) throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 28e392e..d1d5f40 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -230,6 +230,13 @@
     public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
 
     /**
+     * Key for passing a user agent string to the captive portal login activity.
+     * {@hide}
+     */
+    public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT =
+            "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
+
+    /**
      * Broadcast action to indicate the change of data activity status
      * (idle or active) on a network in a recent period.
      * The network becomes active when data transmission is started, or
diff --git a/core/java/android/net/NetworkRecommendationProvider.java b/core/java/android/net/NetworkRecommendationProvider.java
index af5a052c..16ae867 100644
--- a/core/java/android/net/NetworkRecommendationProvider.java
+++ b/core/java/android/net/NetworkRecommendationProvider.java
@@ -75,7 +75,7 @@
      * A callback implementing applications should invoke when a {@link RecommendationResult}
      * is available.
      */
-    public static final class ResultCallback {
+    public static class ResultCallback {
         private final IRemoteCallback mCallback;
         private final int mSequence;
         private final AtomicBoolean mCallbackRun;
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 865b8dd..4e606ef 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -183,7 +183,7 @@
         if (app == null) {
             return null;
         }
-        return app.mPackageName;
+        return app.packageName;
     }
 
     /**
@@ -272,19 +272,11 @@
      * @hide
      */
     public boolean requestScores(NetworkKey[] networks) throws SecurityException {
-        String activeScorer = getActiveScorerPackage();
-        if (activeScorer == null) {
-            return false;
+        try {
+            return mService.requestScores(networks);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        Intent intent = new Intent(ACTION_SCORE_NETWORKS);
-        intent.setPackage(activeScorer);
-        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        intent.putExtra(EXTRA_NETWORKS_TO_SCORE, networks);
-        // A scorer should never become active if its package doesn't hold SCORE_NETWORKS, but
-        // ensure the package still holds it to be extra safe.
-        // TODO: http://b/23422763
-        mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, Manifest.permission.SCORE_NETWORKS);
-        return true;
     }
 
     /**
@@ -344,6 +336,8 @@
     /**
      * Request a recommendation for which network to connect to.
      *
+     * <p>It is not safe to call this method from the main thread.
+     *
      * @param request a {@link RecommendationRequest} instance containing additional
      *                request details
      * @return a {@link RecommendationResult} instance containing the recommended network
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index ebb31c9..4282ca7 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -19,160 +19,176 @@
 import android.Manifest;
 import android.Manifest.permission;
 import android.annotation.Nullable;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
-
+import com.android.internal.R;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
 /**
- * Internal class for managing the primary network scorer application.
- *
- * TODO: Rename this to something more generic.
+ * Internal class for discovering and managing the network scorer/recommendation application.
  *
  * @hide
  */
 public class NetworkScorerAppManager {
     private static final String TAG = "NetworkScorerAppManager";
-
-    private static final Intent SCORE_INTENT =
-            new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
-
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private final Context mContext;
 
     public NetworkScorerAppManager(Context context) {
       mContext = context;
     }
 
+    /**
+     * Holds metadata about a discovered network scorer/recommendation application.
+     */
     public static class NetworkScorerAppData {
         /** Package name of this scorer app. */
-        public final String mPackageName;
+        public final String packageName;
 
         /** UID of the scorer app. */
-        public final int mPackageUid;
-
-        /** Name of this scorer app for display. */
-        public final CharSequence mScorerName;
+        public final int packageUid;
 
         /**
-         * Optional class name of a configuration activity. Null if none is set.
-         *
-         * @see NetworkScoreManager#ACTION_CUSTOM_ENABLE
+         * Name of the recommendation service we can bind to.
          */
-        public final String mConfigurationActivityClassName;
+        public final String recommendationServiceClassName;
 
-        /**
-         * Optional class name of the scoring service we can bind to. Null if none is set.
-         */
-        public final String mScoringServiceClassName;
-
-        public NetworkScorerAppData(String packageName, int packageUid, CharSequence scorerName,
-                @Nullable String configurationActivityClassName,
-                @Nullable String scoringServiceClassName) {
-            mScorerName = scorerName;
-            mPackageName = packageName;
-            mPackageUid = packageUid;
-            mConfigurationActivityClassName = configurationActivityClassName;
-            mScoringServiceClassName = scoringServiceClassName;
+        public NetworkScorerAppData(String packageName, int packageUid,
+                String recommendationServiceClassName) {
+            this.packageName = packageName;
+            this.packageUid = packageUid;
+            this.recommendationServiceClassName = recommendationServiceClassName;
         }
 
         @Override
         public String toString() {
             final StringBuilder sb = new StringBuilder("NetworkScorerAppData{");
-            sb.append("mPackageName='").append(mPackageName).append('\'');
-            sb.append(", mPackageUid=").append(mPackageUid);
-            sb.append(", mScorerName=").append(mScorerName);
-            sb.append(", mConfigurationActivityClassName='").append(mConfigurationActivityClassName)
-                    .append('\'');
-            sb.append(", mScoringServiceClassName='").append(mScoringServiceClassName).append('\'');
+            sb.append("mPackageName='").append(packageName).append('\'');
+            sb.append(", packageUid=").append(packageUid);
+            sb.append(", recommendationServiceClassName='")
+                    .append(recommendationServiceClassName).append('\'');
             sb.append('}');
             return sb.toString();
         }
     }
 
     /**
-     * Returns the list of available scorer apps.
+     * @return A {@link NetworkScorerAppData} instance containing information about the
+     *         best configured network recommendation provider installed or {@code null}
+     *         if none of the configured packages can recommend networks.
      *
-     * <p>A network scorer is any application which:
+     * <p>A network recommendation provider is any application which:
      * <ul>
+     * <li>Is listed in the <code>config_networkRecommendationPackageNames</code> config.
      * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
-     * <li>Includes a receiver for {@link NetworkScoreManager#ACTION_SCORE_NETWORKS} guarded by the
-     *     {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
+     * <li>Includes a Service for {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS}.
      * </ul>
-     *
-     * @return the list of scorers, or the empty list if there are no valid scorers.
      */
-    public Collection<NetworkScorerAppData> getAllValidScorers() {
-        // Network scorer apps can only run as the primary user so exit early if we're not the
-        // primary user.
+    public NetworkScorerAppData getNetworkRecommendationProviderData() {
+        // Network recommendation apps can only run as the primary user right now.
+        // http://b/23422763
         if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
+            return null;
+        }
+
+        final List<String> potentialPkgs = getPotentialRecommendationProviderPackages();
+        if (potentialPkgs.isEmpty()) {
+            if (DEBUG) {
+                Log.d(TAG, "No Network Recommendation Providers specified.");
+            }
+            return null;
+        }
+
+        final PackageManager pm = mContext.getPackageManager();
+        for (int i = 0; i < potentialPkgs.size(); i++) {
+            final String potentialPkg = potentialPkgs.get(i);
+
+            // Look for the recommendation service class and required receiver.
+            final ResolveInfo resolveServiceInfo = findRecommendationService(potentialPkg);
+            if (resolveServiceInfo != null) {
+                return new NetworkScorerAppData(potentialPkg,
+                    resolveServiceInfo.serviceInfo.applicationInfo.uid,
+                    resolveServiceInfo.serviceInfo.name);
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, potentialPkg + " does not have the required components, skipping.");
+                }
+            }
+        }
+
+        // None of the configured packages are valid.
+        return null;
+    }
+
+    /**
+     * @return A priority order list of package names that have been granted the
+     *         permission needed for them to act as a network recommendation provider.
+     *         The packages in the returned list may not contain the other required
+     *         network recommendation provider components so additional checks are required
+     *         before making a package the network recommendation provider.
+     */
+    public List<String> getPotentialRecommendationProviderPackages() {
+        final String[] packageArray = mContext.getResources().getStringArray(
+                R.array.config_networkRecommendationPackageNames);
+        if (packageArray == null || packageArray.length == 0) {
+            if (DEBUG) {
+                Log.d(TAG, "No Network Recommendation Providers specified.");
+            }
             return Collections.emptyList();
         }
 
-        List<NetworkScorerAppData> scorers = new ArrayList<>();
-        PackageManager pm = mContext.getPackageManager();
-        // Only apps installed under the primary user of the device can be scorers.
-        // TODO: http://b/23422763
-        List<ResolveInfo> receivers =
-                pm.queryBroadcastReceiversAsUser(SCORE_INTENT, 0 /* flags */, UserHandle.USER_SYSTEM);
-        for (ResolveInfo receiver : receivers) {
-            // This field is a misnomer, see android.content.pm.ResolveInfo#activityInfo
-            final ActivityInfo receiverInfo = receiver.activityInfo;
-            if (receiverInfo == null) {
-                // Should never happen with queryBroadcastReceivers, but invalid nonetheless.
-                continue;
-            }
-            if (!permission.BROADCAST_NETWORK_PRIVILEGED.equals(receiverInfo.permission)) {
-                // Receiver doesn't require the BROADCAST_NETWORK_PRIVILEGED permission, which
-                // means anyone could trigger network scoring and flood the framework with score
-                // requests.
-                continue;
-            }
-            if (pm.checkPermission(permission.SCORE_NETWORKS, receiverInfo.packageName) !=
-                    PackageManager.PERMISSION_GRANTED) {
-                // Application doesn't hold the SCORE_NETWORKS permission, so the user never
-                // approved it as a network scorer.
-                continue;
-            }
-
-            // Optionally, this package may specify a configuration activity.
-            String configurationActivityClassName = null;
-            Intent intent = new Intent(NetworkScoreManager.ACTION_CUSTOM_ENABLE);
-            intent.setPackage(receiverInfo.packageName);
-            List<ResolveInfo> configActivities = pm.queryIntentActivities(intent, 0 /* flags */);
-            if (configActivities != null && !configActivities.isEmpty()) {
-                ActivityInfo activityInfo = configActivities.get(0).activityInfo;
-                if (activityInfo != null) {
-                    configurationActivityClassName = activityInfo.name;
-                }
-            }
-
-            // Find the scoring service class we can bind to, if any.
-            String scoringServiceClassName = null;
-            Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
-            serviceIntent.setPackage(receiverInfo.packageName);
-            ResolveInfo resolveServiceInfo = pm.resolveService(serviceIntent, 0 /* flags */);
-            if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) {
-                scoringServiceClassName = resolveServiceInfo.serviceInfo.name;
-            }
-
-            // NOTE: loadLabel will attempt to load the receiver's label and fall back to the
-            // app label if none is present.
-            scorers.add(new NetworkScorerAppData(receiverInfo.packageName,
-                    receiverInfo.applicationInfo.uid, receiverInfo.loadLabel(pm),
-                    configurationActivityClassName, scoringServiceClassName));
+        if (VERBOSE) {
+            Log.d(TAG, "Configured packages: " + TextUtils.join(", ", packageArray));
         }
 
-        return scorers;
+        List<String> packages = new ArrayList<>();
+        final PackageManager pm = mContext.getPackageManager();
+        for (String potentialPkg : packageArray) {
+            if (pm.checkPermission(permission.SCORE_NETWORKS, potentialPkg)
+                    == PackageManager.PERMISSION_GRANTED) {
+                packages.add(potentialPkg);
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, potentialPkg + " has not been granted " + permission.SCORE_NETWORKS
+                            + ", skipping.");
+                }
+            }
+        }
+
+        return packages;
+    }
+
+    private ResolveInfo findRecommendationService(String packageName) {
+        final PackageManager pm = mContext.getPackageManager();
+        final int resolveFlags = 0;
+
+        final Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS);
+        serviceIntent.setPackage(packageName);
+        final ResolveInfo resolveServiceInfo =
+                pm.resolveService(serviceIntent, resolveFlags);
+
+        if (VERBOSE) {
+            Log.d(TAG, "Resolved " + serviceIntent + " to " + resolveServiceInfo);
+        }
+
+        if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) {
+            return resolveServiceInfo;
+        }
+
+        if (VERBOSE) {
+            Log.v(TAG, packageName + " does not have a service for " + serviceIntent);
+        }
+        return null;
     }
 
     /**
@@ -182,10 +198,15 @@
      *     selected) or if the previously-set scorer is no longer a valid scorer app (e.g. because
      *     it was disabled or uninstalled).
      */
+    @Nullable
     public NetworkScorerAppData getActiveScorer() {
-        String scorerPackage = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.NETWORK_SCORER_APP);
-        return getScorer(scorerPackage);
+        if (isNetworkRecommendationsDisabled()) {
+            // If recommendations are disabled then there can't be an active scorer.
+            return null;
+        }
+
+        // Otherwise return the recommendation provider (which may be null).
+        return getNetworkRecommendationProviderData();
     }
 
     /**
@@ -195,33 +216,13 @@
      *
      * @param packageName the packageName of the new scorer to use. If null, scoring will be
      *     disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
-     * @return true if the scorer was changed, or false if the package is not a valid scorer.
+     * @return true if the scorer was changed, or false if the package is not a valid scorer or
+     *         a valid network recommendation provider exists.
+     * @deprecated Scorers are now selected from a configured list.
      */
+    @Deprecated
     public boolean setActiveScorer(String packageName) {
-        String oldPackageName = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.NETWORK_SCORER_APP);
-        if (TextUtils.equals(oldPackageName, packageName)) {
-            // No change.
-            return true;
-        }
-
-        Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
-
-        if (packageName == null) {
-            Settings.Global.putString(mContext.getContentResolver(),
-                    Settings.Global.NETWORK_SCORER_APP, null);
-            return true;
-        } else {
-            // We only make the change if the new package is valid.
-            if (getScorer(packageName) != null) {
-                Settings.Global.putString(mContext.getContentResolver(),
-                        Settings.Global.NETWORK_SCORER_APP, packageName);
-                return true;
-            } else {
-                Log.w(TAG, "Requested network scorer is not valid: " + packageName);
-                return false;
-            }
-        }
+        return false;
     }
 
     /** Determine whether the application with the given UID is the enabled scorer. */
@@ -230,7 +231,7 @@
         if (defaultApp == null) {
             return false;
         }
-        if (callingUid != defaultApp.mPackageUid) {
+        if (callingUid != defaultApp.packageUid) {
             return false;
         }
         // To be extra safe, ensure the caller holds the SCORE_NETWORKS permission. It always
@@ -239,17 +240,9 @@
                 PackageManager.PERMISSION_GRANTED;
     }
 
-    /** Returns the {@link NetworkScorerAppData} for the given app, or null if it's not a scorer. */
-    public NetworkScorerAppData getScorer(String packageName) {
-        if (TextUtils.isEmpty(packageName)) {
-            return null;
-        }
-        Collection<NetworkScorerAppData> applications = getAllValidScorers();
-        for (NetworkScorerAppData app : applications) {
-            if (packageName.equals(app.mPackageName)) {
-                return app;
-            }
-        }
-        return null;
+    private boolean isNetworkRecommendationsDisabled() {
+        final ContentResolver cr = mContext.getContentResolver();
+        // A value of 1 indicates enabled.
+        return Settings.Global.getInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) != 1;
     }
 }
diff --git a/core/java/android/net/RecommendationRequest.java b/core/java/android/net/RecommendationRequest.java
index 05ca1aa..a96f90d 100644
--- a/core/java/android/net/RecommendationRequest.java
+++ b/core/java/android/net/RecommendationRequest.java
@@ -105,7 +105,16 @@
     }
 
     protected RecommendationRequest(Parcel in) {
-        mScanResults = (ScanResult[]) in.readParcelableArray(ScanResult.class.getClassLoader());
+        final int resultCount = in.readInt();
+        if (resultCount > 0) {
+            mScanResults = new ScanResult[resultCount];
+            for (int i = 0; i < resultCount; i++) {
+                mScanResults[i] = in.readParcelable(ScanResult.class.getClassLoader());
+            }
+        } else {
+            mScanResults = null;
+        }
+
         mCurrentSelectedConfig = in.readParcelable(WifiConfiguration.class.getClassLoader());
         mRequiredCapabilities = in.readParcelable(NetworkCapabilities.class.getClassLoader());
     }
@@ -117,7 +126,14 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelableArray(mScanResults, flags);
+        if (mScanResults != null) {
+            dest.writeInt(mScanResults.length);
+            for (int i = 0; i < mScanResults.length; i++) {
+                dest.writeParcelable(mScanResults[i], flags);
+            }
+        } else {
+            dest.writeInt(0);
+        }
         dest.writeParcelable(mCurrentSelectedConfig, flags);
         dest.writeParcelable(mRequiredCapabilities, flags);
     }
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index b0b2065..6276af3 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -246,7 +246,7 @@
     }
 
     /**
-     * Returns a userHandle for the instance of the app that posted this notification.
+     * Returns a userid for whom this notification is intended.
      *
      * @deprecated Use {@link #getUser()} instead.
      */
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 6196a97..99f6c2a9 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -56,6 +56,7 @@
     /** A previously logged event read from the logs. Instances are thread safe. */
     public static final class Event {
         private final ByteBuffer mBuffer;
+        private Exception mLastWtf;
 
         // Layout of event log entry received from Android logger.
         //  see system/core/include/log/logger.h
@@ -116,13 +117,19 @@
                     offset = V1_PAYLOAD_START;
                 }
                 mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET));
+                if ((offset + DATA_OFFSET) >= mBuffer.limit()) {
+                    // no payload
+                    return null;
+                }
                 mBuffer.position(offset + DATA_OFFSET); // Just after the tag.
                 return decodeObject();
             } catch (IllegalArgumentException e) {
                 Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
+                mLastWtf = e;
                 return null;
             } catch (BufferUnderflowException e) {
                 Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e);
+                mLastWtf = e;
                 return null;
             }
         }
@@ -148,6 +155,7 @@
                     return new String(mBuffer.array(), start, length, "UTF-8");
                 } catch (UnsupportedEncodingException e) {
                     Log.wtf(TAG, "UTF-8 is not supported", e);
+                    mLastWtf = e;
                     return null;
                 }
 
@@ -173,6 +181,24 @@
             byte[] bytes = mBuffer.array();
             return Arrays.copyOf(bytes, bytes.length);
         }
+
+        /**
+         * Retreive the last WTF error generated by this object.
+         * @hide
+         */
+        //VisibleForTesting
+        public Exception getLastError() {
+            return mLastWtf;
+        }
+
+        /**
+         * Clear the error state for this object.
+         * @hide
+         */
+        //VisibleForTesting
+        public void clearError() {
+            mLastWtf = null;
+        }
     }
 
     // We assume that the native methods deal with any concurrency issues.
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 7c1bcee..8bc988d 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -274,6 +274,7 @@
     private TypedArray mWindowStyle;
     private Callback mCallback;
     private OnWindowDismissedCallback mOnWindowDismissedCallback;
+    private OnWindowSwipeDismissedCallback mOnWindowSwipeDismissedCallback;
     private WindowControllerCallback mWindowControllerCallback;
     private OnRestrictedCaptionAreaChangedListener mOnRestrictedCaptionAreaChangedListener;
     private Rect mRestrictedCaptionAreaRect;
@@ -587,6 +588,18 @@
     }
 
     /** @hide */
+    public interface OnWindowSwipeDismissedCallback {
+        /**
+         * Called when a window is swipe dismissed. This informs the callback that the
+         * window is gone, and it should finish itself.
+         * @param finishTask True if the task should also be finished.
+         * @param suppressWindowTransition True if the resulting exit and enter window transition
+         * animations should be suppressed.
+         */
+        void onWindowSwipeDismissed();
+    }
+
+    /** @hide */
     public interface WindowControllerCallback {
         /**
          * Moves the activity from
@@ -880,6 +893,18 @@
     }
 
     /** @hide */
+    public final void setOnWindowSwipeDismissedCallback(OnWindowSwipeDismissedCallback sdcb) {
+        mOnWindowSwipeDismissedCallback = sdcb;
+    }
+
+    /** @hide */
+    public final void dispatchOnWindowSwipeDismissed() {
+        if (mOnWindowSwipeDismissedCallback != null) {
+            mOnWindowSwipeDismissedCallback.onWindowSwipeDismissed();
+        }
+    }
+
+    /** @hide */
     public final void setWindowControllerCallback(WindowControllerCallback wccb) {
         mWindowControllerCallback = wccb;
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 99dbb1c..6a5bbcc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2897,8 +2897,22 @@
             mHistoryLastWritten.setTo(mHistoryLastLastWritten);
         }
 
+        boolean recordResetDueToOverflow = false;
         final int dataSize = mHistoryBuffer.dataSize();
-        if (dataSize >= MAX_HISTORY_BUFFER) {
+        if (dataSize >= MAX_MAX_HISTORY_BUFFER*3) {
+            // Clients can't deal with history buffers this large. This only
+            // really happens when the device is on charger and interacted with
+            // for long periods of time, like in retail mode. Since the device is
+            // most likely charged, when unplugged, stats would have reset anyways.
+            // Reset the stats and mark that we overflowed.
+            // b/32540341
+            resetAllStatsLocked();
+
+            // Mark that we want to set *OVERFLOW* event and the RESET:START
+            // events.
+            recordResetDueToOverflow = true;
+
+        } else if (dataSize >= MAX_HISTORY_BUFFER) {
             if (!mHistoryOverflow) {
                 mHistoryOverflow = true;
                 addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
@@ -2944,9 +2958,12 @@
             return;
         }
 
-        if (dataSize == 0) {
+        if (dataSize == 0 || recordResetDueToOverflow) {
             // The history is currently empty; we need it to start with a time stamp.
             cur.currentTime = System.currentTimeMillis();
+            if (recordResetDueToOverflow) {
+                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
+            }
             addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_RESET, cur);
         }
         addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index d968e3c..a8a5549 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -62,6 +62,9 @@
             ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
                     packagePath, libsPath);
 
+            // Add the APK to the Zygote's list of allowed files for children.
+            Zygote.nativeAllowFileAcrossFork(packagePath);
+
             // Once we have the classloader, look up the WebViewFactoryProvider implementation and
             // call preloadInZygote() on it to give it the opportunity to preload the native library
             // and perform any other initialisation work that should be shared among the children.
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fc0ccb7..293de3d 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -153,6 +153,11 @@
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
 
     /**
+     * Lets children of the zygote inherit open file descriptors to this path.
+     */
+    native protected static void nativeAllowFileAcrossFork(String path);
+
+    /**
      * Zygote unmount storage space on initializing.
      * This method is called once.
      */
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index ec068a3..e68ebc4 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2997,6 +2997,7 @@
         swipeDismiss.setOnDismissedListener(new SwipeDismissLayout.OnDismissedListener() {
             @Override
             public void onDismissed(SwipeDismissLayout layout) {
+                dispatchOnWindowSwipeDismissed();
                 dispatchOnWindowDismissed(false /*finishTask*/, true /*suppressWindowTransition*/);
             }
         });
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 63b700b..2a8077c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -590,8 +590,6 @@
             setCredentialRequiredToDecrypt(false);
         }
 
-        getDevicePolicyManager().setActivePasswordState(new PasswordMetrics(), userHandle);
-
         onAfterChangingPassword(userHandle);
     }
 
@@ -644,6 +642,7 @@
                         + MIN_LOCK_PATTERN_SIZE + " dots long.");
             }
 
+            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
             getLockSettings().setLockPattern(patternToString(pattern), savedPattern, userId);
             DevicePolicyManager dpm = getDevicePolicyManager();
 
@@ -659,10 +658,6 @@
             }
 
             setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
-
-            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
-            dpm.setActivePasswordState(new PasswordMetrics(
-                    DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern.size()), userId);
             onAfterChangingPassword(userId);
         } catch (RemoteException re) {
             Log.e(TAG, "Couldn't save lock pattern " + re);
@@ -775,10 +770,9 @@
                         + "of length " + MIN_LOCK_PASSWORD_SIZE);
             }
 
+            final int computedQuality = PasswordMetrics.computeForPassword(password).quality;
+            setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
             getLockSettings().setLockPassword(password, savedPassword, userHandle);
-            getLockSettings().setSeparateProfileChallengeEnabled(userHandle, true, null);
-            final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
-            final int computedQuality = metrics.quality;
 
             // Update the device encryption password.
             if (userHandle == UserHandle.USER_SYSTEM
@@ -796,15 +790,6 @@
                 }
             }
 
-            setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
-            if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
-                metrics.quality = Math.max(quality, metrics.quality);
-                dpm.setActivePasswordState(metrics, userHandle);
-            } else {
-                // The password is not anything.
-                dpm.setActivePasswordState(new PasswordMetrics(), userHandle);
-            }
-
             // Add the password to the password history. We assume all
             // password hashes have the same length for simplicity of implementation.
             String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index b656bb0..8f74bf8 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -628,6 +628,14 @@
                            jint dstConfigHandle, jboolean isMutable) {
     SkBitmap src;
     reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src);
+    if (dstConfigHandle == GraphicsJNI::hardwareLegacyBitmapConfig()) {
+        sk_sp<Bitmap> bitmap(Bitmap::allocateHardwareBitmap(src));
+        if (!bitmap.get()) {
+            return NULL;
+        }
+        return createBitmap(env, bitmap.release(), kBitmapCreateFlag_None);
+    }
+
     SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
     SkBitmap result;
     HeapAllocator allocator;
@@ -1190,8 +1198,7 @@
     }
 }
 
-static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
-                              jlong bm1Handle) {
+static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, jlong bm1Handle) {
     SkBitmap bm0;
     SkBitmap bm1;
 
@@ -1207,11 +1214,11 @@
 
     bitmap0->bitmap().getSkBitmap(&bm0);
     bitmap1->bitmap().getSkBitmap(&bm1);
-    if (bm0.width() != bm1.width() ||
-        bm0.height() != bm1.height() ||
-        bm0.colorType() != bm1.colorType() ||
-        bm0.alphaType() != bm1.alphaType() ||
-        bm0.colorSpace() != bm1.colorSpace()) {
+    if (bm0.width() != bm1.width()
+            || bm0.height() != bm1.height()
+            || bm0.colorType() != bm1.colorType()
+            || bm0.alphaType() != bm1.alphaType()
+            || !SkColorSpace::Equals(bm0.colorSpace(), bm1.colorSpace())) {
         return JNI_FALSE;
     }
 
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 69c7054..724fccc 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -395,7 +395,7 @@
     SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied);
 
     const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(),
-            decodeColorType, alphaType, codec->computeOutputColorSpace(decodeColorType));
+            decodeColorType, alphaType);
 
     // We always decode to sRGB, but only mark the bitmap with a color space if linear
     // blending is enabled.
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index bb69e27..f18837f 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -144,8 +144,6 @@
             return kN32_SkColorType;
         case PIXEL_FORMAT_RGBA_FP16:
             return kRGBA_F16_SkColorType;
-        case PIXEL_FORMAT_RGBX_FP16:
-            return kRGBA_F16_SkColorType;
         case PIXEL_FORMAT_RGB_565:
             return kRGB_565_SkColorType;
         default:
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index a5b7671..96e6f81 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -169,7 +169,6 @@
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_RGBX_8888:
         case HAL_PIXEL_FORMAT_RGBA_FP16:
-        case HAL_PIXEL_FORMAT_RGBX_FP16:
         case HAL_PIXEL_FORMAT_RGB_888:
         case HAL_PIXEL_FORMAT_RGB_565:
         case HAL_PIXEL_FORMAT_Y8:
@@ -285,7 +284,6 @@
     switch (format) {
     case PIXEL_FORMAT_RGBX_8888:    return kN32_SkColorType;
     case PIXEL_FORMAT_RGBA_8888:    return kN32_SkColorType;
-    case PIXEL_FORMAT_RGBX_FP16:    return kRGBA_F16_SkColorType;
     case PIXEL_FORMAT_RGBA_FP16:    return kRGBA_F16_SkColorType;
     case PIXEL_FORMAT_RGB_565:      return kRGB_565_SkColorType;
     default:                        return kUnknown_SkColorType;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 62c3d04..ed071cd 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -164,11 +164,6 @@
             alphaType = kPremul_SkAlphaType;
             break;
         }
-        case PIXEL_FORMAT_RGBX_FP16: {
-            colorType = kRGBA_F16_SkColorType;
-            alphaType = kOpaque_SkAlphaType;
-            break;
-        }
         case PIXEL_FORMAT_RGBA_FP16: {
             colorType = kRGBA_F16_SkColorType;
             alphaType = kPremul_SkAlphaType;
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index d3c0202..613e040 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -87,10 +87,6 @@
             colorType = kRGBA_F16_SkColorType;
             alphaType = kPremul_SkAlphaType;
             break;
-        case WINDOW_FORMAT_RGBX_FP16:
-            colorType = kRGBA_F16_SkColorType;
-            alphaType = kOpaque_SkAlphaType;
-            break;
         case WINDOW_FORMAT_RGB_565:
             colorType = kRGB_565_SkColorType;
             alphaType = kOpaque_SkAlphaType;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7eb1150..cc7b958 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -709,6 +709,16 @@
   return pid;
 }
 
+static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
+        JNIEnv* env, jclass, jstring path) {
+    ScopedUtfChars path_native(env, path);
+    const char* path_cstr = path_native.c_str();
+    if (!path_cstr) {
+        RuntimeAbort(env, __LINE__, "path_cstr == NULL");
+    }
+    FileDescriptorWhitelist::Get()->Allow(path_cstr);
+}
+
 static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) {
     // Zygote process unmount root storage space initially before every child processes are forked.
     // Every forked child processes (include SystemServer) only mount their own root storage space
@@ -753,6 +763,8 @@
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
     { "nativeForkSystemServer", "(II[II[[IJJ)I",
       (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
+    { "nativeAllowFileAcrossFork", "(Ljava/lang/String;)V",
+      (void *) com_android_internal_os_Zygote_nativeAllowFileAcrossFork },
     { "nativeUnmountStorageOnInit", "()V",
       (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }
 };
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index ecd37a8..969d336f3 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -29,17 +29,7 @@
 #include <android-base/strings.h>
 #include <cutils/log.h>
 
-// Whitelist of open paths that the zygote is allowed to keep open.
-//
-// In addition to the paths listed here, all files ending with
-// ".jar" under /system/framework" are whitelisted. See
-// FileDescriptorInfo::IsWhitelisted for the canonical definition.
-//
-// If the whitelisted path is associated with a regular file or a
-// character device, the file is reopened after a fork with the same
-// offset and mode. If the whilelisted  path is associated with a
-// AF_UNIX socket, the socket will refer to /dev/null after each
-// fork, and all operations on it will fail.
+// Static whitelist of open paths that the zygote is allowed to keep open.
 static const char* kPathWhitelist[] = {
   "/dev/null",
   "/dev/socket/zygote",
@@ -55,6 +45,93 @@
 static const char kFdPath[] = "/proc/self/fd";
 
 // static
+FileDescriptorWhitelist* FileDescriptorWhitelist::Get() {
+  if (instance_ == nullptr) {
+    instance_ = new FileDescriptorWhitelist();
+  }
+  return instance_;
+}
+
+bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
+  // Check the static whitelist path.
+  for (const auto& whitelist_path : kPathWhitelist) {
+    if (path == whitelist_path)
+      return true;
+  }
+
+  // Check any paths added to the dynamic whitelist.
+  for (const auto& whitelist_path : whitelist_) {
+    if (path == whitelist_path)
+      return true;
+  }
+
+  static const std::string kFrameworksPrefix = "/system/framework/";
+  static const std::string kJarSuffix = ".jar";
+  if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
+    return true;
+  }
+
+  // Whitelist files needed for Runtime Resource Overlay, like these:
+  // /system/vendor/overlay/framework-res.apk
+  // /system/vendor/overlay-subdir/pg/framework-res.apk
+  // /vendor/overlay/framework-res.apk
+  // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
+  // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
+  // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
+  // See AssetManager.cpp for more details on overlay-subdir.
+  static const std::string kOverlayDir = "/system/vendor/overlay/";
+  static const std::string kVendorOverlayDir = "/vendor/overlay";
+  static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
+  static const std::string kApkSuffix = ".apk";
+
+  if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
+       || StartsWith(path, kVendorOverlayDir))
+      && EndsWith(path, kApkSuffix)
+      && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
+  static const std::string kOverlayIdmapSuffix = ".apk@idmap";
+  if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
+      && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  // All regular files that are placed under this path are whitelisted automatically.
+  static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
+  if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  return false;
+}
+
+FileDescriptorWhitelist::FileDescriptorWhitelist()
+    : whitelist_() {
+}
+
+// TODO: Call android::base::StartsWith instead of copying the code here.
+// static
+bool FileDescriptorWhitelist::StartsWith(const std::string& str,
+                                         const std::string& prefix) {
+  return str.compare(0, prefix.size(), prefix) == 0;
+}
+
+// TODO: Call android::base::EndsWith instead of copying the code here.
+// static
+bool FileDescriptorWhitelist::EndsWith(const std::string& str,
+                                       const std::string& suffix) {
+  if (suffix.size() > str.size()) {
+    return false;
+  }
+
+  return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
+
+// static
 FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
   struct stat f_stat;
   // This should never happen; the zygote should always have the right set
@@ -64,13 +141,15 @@
     return NULL;
   }
 
+  const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
+
   if (S_ISSOCK(f_stat.st_mode)) {
     std::string socket_name;
     if (!GetSocketName(fd, &socket_name)) {
       return NULL;
     }
 
-    if (!IsWhitelisted(socket_name)) {
+    if (!whitelist->IsAllowed(socket_name)) {
       ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
       return NULL;
     }
@@ -98,7 +177,7 @@
     return NULL;
   }
 
-  if (!IsWhitelisted(file_path)) {
+  if (!whitelist->IsAllowed(file_path)) {
     ALOGE("Not whitelisted : %s", file_path.c_str());
     return NULL;
   }
@@ -218,72 +297,6 @@
   is_sock(false) {
 }
 
-// TODO: Call android::base::StartsWith instead of copying the code here.
-// static
-bool FileDescriptorInfo::StartsWith(const std::string& str, const std::string& prefix) {
-  return str.compare(0, prefix.size(), prefix) == 0;
-}
-
-// TODO: Call android::base::EndsWith instead of copying the code here.
-// static
-bool FileDescriptorInfo::EndsWith(const std::string& str, const std::string& suffix) {
-  if (suffix.size() > str.size()) {
-    return false;
-  }
-
-  return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
-}
-
-// static
-bool FileDescriptorInfo::IsWhitelisted(const std::string& path) {
-  for (size_t i = 0; i < (sizeof(kPathWhitelist) / sizeof(kPathWhitelist[0])); ++i) {
-    if (kPathWhitelist[i] == path) {
-      return true;
-    }
-  }
-
-  static const std::string kFrameworksPrefix = "/system/framework/";
-  static const std::string kJarSuffix = ".jar";
-  if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
-    return true;
-  }
-
-  // Whitelist files needed for Runtime Resource Overlay, like these:
-  // /system/vendor/overlay/framework-res.apk
-  // /system/vendor/overlay-subdir/pg/framework-res.apk
-  // /vendor/overlay/framework-res.apk
-  // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
-  // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
-  // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
-  // See AssetManager.cpp for more details on overlay-subdir.
-  static const std::string kOverlayDir = "/system/vendor/overlay/";
-  static const std::string kVendorOverlayDir = "/vendor/overlay";
-  static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
-  static const std::string kApkSuffix = ".apk";
-
-  if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
-       || StartsWith(path, kVendorOverlayDir))
-      && EndsWith(path, kApkSuffix)
-      && path.find("/../") == std::string::npos) {
-    return true;
-  }
-
-  static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
-  static const std::string kOverlayIdmapSuffix = ".apk@idmap";
-  if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
-      && path.find("/../") == std::string::npos) {
-    return true;
-  }
-
-  // All regular files that are placed under this path are whitelisted automatically.
-  static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
-  if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
-    return true;
-  }
-
-  return false;
-}
-
 // TODO: Call android::base::Readlink instead of copying the code here.
 // static
 bool FileDescriptorInfo::Readlink(const int fd, std::string* result) {
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index 66fd718..9e3afd9 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -20,6 +20,7 @@
 #include <set>
 #include <string>
 #include <unordered_map>
+#include <vector>
 
 #include <dirent.h>
 #include <inttypes.h>
@@ -27,6 +28,48 @@
 
 #include <android-base/macros.h>
 
+// Whitelist of open paths that the zygote is allowed to keep open.
+//
+// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
+// paths dynamically added with Allow(), all files ending with ".jar"
+// under /system/framework" are whitelisted. See IsAllowed() for the canonical
+// definition.
+//
+// If the whitelisted path is associated with a regular file or a
+// character device, the file is reopened after a fork with the same
+// offset and mode. If the whilelisted  path is associated with a
+// AF_UNIX socket, the socket will refer to /dev/null after each
+// fork, and all operations on it will fail.
+class FileDescriptorWhitelist {
+ public:
+  // Lazily creates the global whitelist.
+  static FileDescriptorWhitelist* Get();
+
+  // Adds a path to the whitelist.
+  void Allow(const std::string& path) {
+    whitelist_.push_back(path);
+  }
+
+  // Returns true iff. a given path is whitelisted. A path is whitelisted
+  // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
+  // under /system/framework that ends with ".jar" or if it is a system
+  // framework overlay.
+  bool IsAllowed(const std::string& path) const;
+
+ private:
+  FileDescriptorWhitelist();
+
+  static bool StartsWith(const std::string& str, const std::string& prefix);
+
+  static bool EndsWith(const std::string& str, const std::string& suffix);
+
+  static FileDescriptorWhitelist* instance_;
+
+  std::vector<std::string> whitelist_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist);
+};
+
 // Keeps track of all relevant information (flags, offset etc.) of an
 // open zygote file descriptor.
 class FileDescriptorInfo {
@@ -56,16 +99,6 @@
   FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
                      int fd_flags, int fs_flags, off_t offset);
 
-  static bool StartsWith(const std::string& str, const std::string& prefix);
-
-  static bool EndsWith(const std::string& str, const std::string& suffix);
-
-  // Returns true iff. a given path is whitelisted. A path is whitelisted
-  // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
-  // under /system/framework that ends with ".jar" or if it is a system
-  // framework overlay.
-  static bool IsWhitelisted(const std::string& path);
-
   static bool Readlink(const int fd, std::string* result);
 
   // Returns the locally-bound name of the socket |fd|. Returns true
diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h
index b1e552a..1bc8521 100644
--- a/core/jni/include/android_runtime/android_view_Surface.h
+++ b/core/jni/include/android_runtime/android_view_Surface.h
@@ -42,6 +42,7 @@
     NV16              = 0x10,
     NV21              = 0x11,
     YUY2              = 0x14,
+    RGBA_FP16         = 0x16,
     RAW_SENSOR        = 0x20,
     PRIVATE           = 0x22,
     YUV_420_888       = 0x23,
diff --git a/core/tests/coretests/README b/core/tests/coretests/README
new file mode 100644
index 0000000..4a69843
--- /dev/null
+++ b/core/tests/coretests/README
@@ -0,0 +1,50 @@
+* Copyright (C) 2016 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+
+INTRODUCTION
+
+The Android platform core tests (APCT) consist of unit tests for core platform
+functionality. These differ from CTS in that they are not necessarily testing
+public APIs and are not guaranteed to work outside of AOSP builds.
+
+
+INSTRUCTIONS
+
+To run a test or set of tests, first build the FrameworksCoreTests package:
+
+  make FrameworksCoreTests
+
+Next, install the resulting APK and run tests as you would normal JUnit tests:
+
+  adb install out/target/product/.../data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+  adb shell am instrument -w \
+    com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+
+To run a tests within a specific package, add the following argument AFTER -w:
+
+    -e package android.content.pm
+
+To run a specific test or method within a test:
+
+    -e class android.content.pm.PackageParserTest
+    -e class android.content.pm.PackageParserTest#testComputeMinSdkVersion
+
+To run tests in debug mode:
+
+    -e debug true
+
+For more arguments, see the guide to command=line testing:
+
+  https://developer.android.com/studio/test/command-line.html
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
new file mode 100644
index 0000000..2a3c22c
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.os.Build;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PackageParserTest {
+    private static final String RELEASED = null;
+    private static final String OLDER_PRE_RELEASE = "A";
+    private static final String PRE_RELEASE = "B";
+    private static final String NEWER_PRE_RELEASE = "C";
+
+    private static final String[] CODENAMES_RELEASED = { /* empty */ };
+    private static final String[] CODENAMES_PRE_RELEASE = { PRE_RELEASE };
+
+    private static final int OLDER_VERSION = 10;
+    private static final int PLATFORM_VERSION = 20;
+    private static final int NEWER_VERSION = 30;
+
+    private void verifyComputeMinSdkVersion(int minSdkVersion, String minSdkCodename,
+            boolean isPlatformReleased, int expectedMinSdk) {
+        final String[] outError = new String[1];
+        final int result = PackageParser.computeMinSdkVersion(
+                minSdkVersion,
+                minSdkCodename,
+                PLATFORM_VERSION,
+                isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
+                outError);
+
+        assertEquals(result, expectedMinSdk);
+
+        if (expectedMinSdk == -1) {
+            assertNotNull(outError[0]);
+        } else {
+            assertNull(outError[0]);
+        }
+    }
+
+    @Test
+    public void testComputeMinSdkVersion_preReleasePlatform() {
+        // Do allow older release minSdkVersion on pre-release platform.
+        // APP: Released API 10
+        // DEV: Pre-release API 20
+        verifyComputeMinSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION);
+
+        // Do allow same release minSdkVersion on pre-release platform.
+        // APP: Released API 20
+        // DEV: Pre-release API 20
+        verifyComputeMinSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION);
+
+        // Don't allow newer release minSdkVersion on pre-release platform.
+        // APP: Released API 30
+        // DEV: Pre-release API 20
+        verifyComputeMinSdkVersion(NEWER_VERSION, RELEASED, false, -1);
+
+        // Don't allow older pre-release minSdkVersion on pre-release platform.
+        // APP: Pre-release API 10
+        // DEV: Pre-release API 20
+        verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1);
+
+        // Do allow same pre-release minSdkVersion on pre-release platform,
+        // but overwrite the specified version with CUR_DEVELOPMENT.
+        // APP: Pre-release API 20
+        // DEV: Pre-release API 20
+        verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT);
+
+        // Don't allow newer pre-release minSdkVersion on pre-release platform.
+        // APP: Pre-release API 30
+        // DEV: Pre-release API 20
+        verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1);
+    }
+
+    @Test
+    public void testComputeMinSdkVersion_releasedPlatform() {
+        // Do allow older release minSdkVersion on released platform.
+        // APP: Released API 10
+        // DEV: Released API 20
+        verifyComputeMinSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION);
+
+        // Do allow same release minSdkVersion on released platform.
+        // APP: Released API 20
+        // DEV: Released API 20
+        verifyComputeMinSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION);
+
+        // Don't allow newer release minSdkVersion on released platform.
+        // APP: Released API 30
+        // DEV: Released API 20
+        verifyComputeMinSdkVersion(NEWER_VERSION, RELEASED, true, -1);
+
+        // Don't allow older pre-release minSdkVersion on released platform.
+        // APP: Pre-release API 10
+        // DEV: Released API 20
+        verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1);
+
+        // Don't allow same pre-release minSdkVersion on released platform.
+        // APP: Pre-release API 20
+        // DEV: Released API 20
+        verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1);
+
+        // Don't allow newer pre-release minSdkVersion on released platform.
+        // APP: Pre-release API 30
+        // DEV: Released API 20
+        verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1);
+    }
+
+    private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename,
+            boolean isPlatformReleased, int expectedTargetSdk) {
+        final String[] outError = new String[1];
+        final int result = PackageParser.computeTargetSdkVersion(
+                targetSdkVersion,
+                targetSdkCodename,
+                PLATFORM_VERSION,
+                isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
+                outError);
+
+        assertEquals(result, expectedTargetSdk);
+
+        if (expectedTargetSdk == -1) {
+            assertNotNull(outError[0]);
+        } else {
+            assertNull(outError[0]);
+        }
+    }
+
+    @Test
+    public void testComputeTargetSdkVersion_preReleasePlatform() {
+        // Do allow older release targetSdkVersion on pre-release platform.
+        // APP: Released API 10
+        // DEV: Pre-release API 20
+        verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION);
+
+        // Do allow same release targetSdkVersion on pre-release platform.
+        // APP: Released API 20
+        // DEV: Pre-release API 20
+        verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION);
+
+        // Do allow newer release targetSdkVersion on pre-release platform.
+        // APP: Released API 30
+        // DEV: Pre-release API 20
+        verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, false, NEWER_VERSION);
+
+        // Don't allow older pre-release targetSdkVersion on pre-release platform.
+        // APP: Pre-release API 10
+        // DEV: Pre-release API 20
+        verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1);
+
+        // Do allow same pre-release targetSdkVersion on pre-release platform,
+        // but overwrite the specified version with CUR_DEVELOPMENT.
+        // APP: Pre-release API 20
+        // DEV: Pre-release API 20
+        verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT);
+
+        // Don't allow newer pre-release targetSdkVersion on pre-release platform.
+        // APP: Pre-release API 30
+        // DEV: Pre-release API 20
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1);
+    }
+
+    @Test
+    public void testComputeTargetSdkVersion_releasedPlatform() {
+        // Do allow older release targetSdkVersion on released platform.
+        // APP: Released API 10
+        // DEV: Released API 20
+        verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION);
+
+        // Do allow same release targetSdkVersion on released platform.
+        // APP: Released API 20
+        // DEV: Released API 20
+        verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION);
+
+        // Do allow newer release targetSdkVersion on released platform.
+        // APP: Released API 30
+        // DEV: Released API 20
+        verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, true, NEWER_VERSION);
+
+        // Don't allow older pre-release targetSdkVersion on released platform.
+        // APP: Pre-release API 10
+        // DEV: Released API 20
+        verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1);
+
+        // Don't allow same pre-release targetSdkVersion on released platform.
+        // APP: Pre-release API 20
+        // DEV: Released API 20
+        verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1);
+
+        // Don't allow newer pre-release targetSdkVersion on released platform.
+        // APP: Pre-release API 30
+        // DEV: Released API 20
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1);
+    }
+}
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 02c2517..5bfff26 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -16,32 +16,33 @@
 
 package android.net;
 
+import static org.mockito.Mockito.when;
+
 import android.Manifest.permission;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
-import android.os.UserHandle;
+import android.provider.Settings;
 import android.test.InstrumentationTestCase;
-
+import com.android.internal.R;
+import java.util.List;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
 public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
     @Mock private Context mMockContext;
     @Mock private PackageManager mMockPm;
-
+    @Mock private Resources mResources;
+    @Mock private ContentResolver mContentResolver;
+    private Context mTargetContext;
     private NetworkScorerAppManager mNetworkScorerAppManager;
 
     @Override
@@ -49,154 +50,161 @@
         super.setUp();
 
         // Configuration needed to make mockito/dexcache work.
-        System.setProperty("dexmaker.dexcache",
-                getInstrumentation().getTargetContext().getCacheDir().getPath());
+        mTargetContext = getInstrumentation().getTargetContext();
+        System.setProperty("dexmaker.dexcache", mTargetContext.getCacheDir().getPath());
         ClassLoader newClassLoader = getInstrumentation().getClass().getClassLoader();
         Thread.currentThread().setContextClassLoader(newClassLoader);
 
         MockitoAnnotations.initMocks(this);
-        Mockito.when(mMockContext.getPackageManager()).thenReturn(mMockPm);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPm);
+        when(mMockContext.getResources()).thenReturn(mResources);
+        when(mMockContext.getContentResolver()).thenReturn(mTargetContext.getContentResolver());
         mNetworkScorerAppManager = new NetworkScorerAppManager(mMockContext);
     }
 
-    public void testGetAllValidScorers() throws Exception {
-        // Package 1 - Valid scorer.
-        ResolveInfoHolder package1 = buildResolveInfo("package1", 1, true, true, false, false);
-
-        // Package 2 - Receiver does not have BROADCAST_NETWORK_PRIVILEGED permission.
-        ResolveInfoHolder package2 = buildResolveInfo("package2", 2, false, true, false, false);
-
-        // Package 3 - App does not have SCORE_NETWORKS permission.
-        ResolveInfoHolder package3 = buildResolveInfo("package3", 3, true, false, false, false);
-
-        // Package 4 - Valid scorer w/ optional config activity.
-        ResolveInfoHolder package4 = buildResolveInfo("package4", 4, true, true, true, false);
-
-        // Package 5 - Valid scorer w/ optional service to bind to.
-        ResolveInfoHolder package5 = buildResolveInfo("package5", 5, true, true, false, true);
-
-        List<ResolveInfoHolder> scorers = new ArrayList<>();
-        scorers.add(package1);
-        scorers.add(package2);
-        scorers.add(package3);
-        scorers.add(package4);
-        scorers.add(package5);
-        setScorers(scorers);
-
-        Iterator<NetworkScorerAppData> result =
-                mNetworkScorerAppManager.getAllValidScorers().iterator();
-
-        assertTrue(result.hasNext());
-        NetworkScorerAppData next = result.next();
-        assertEquals("package1", next.mPackageName);
-        assertEquals(1, next.mPackageUid);
-        assertNull(next.mConfigurationActivityClassName);
-
-        assertTrue(result.hasNext());
-        next = result.next();
-        assertEquals("package4", next.mPackageName);
-        assertEquals(4, next.mPackageUid);
-        assertEquals(".ConfigActivity", next.mConfigurationActivityClassName);
-
-        assertTrue(result.hasNext());
-        next = result.next();
-        assertEquals("package5", next.mPackageName);
-        assertEquals(5, next.mPackageUid);
-        assertEquals(".ScoringService", next.mScoringServiceClassName);
-
-        assertFalse(result.hasNext());
+    public void testGetPotentialRecommendationProviderPackages_emptyConfig() throws Exception {
+        setNetworkRecommendationPackageNames(/*no configured packages*/);
+        assertTrue(mNetworkScorerAppManager.getPotentialRecommendationProviderPackages().isEmpty());
     }
 
-    private void setScorers(List<ResolveInfoHolder> scorers) {
-        List<ResolveInfo> receivers = new ArrayList<>();
-        for (final ResolveInfoHolder scorer : scorers) {
-            receivers.add(scorer.scorerResolveInfo);
-            if (scorer.configActivityResolveInfo != null) {
-                // This scorer has a config activity.
-                Mockito.when(mMockPm.queryIntentActivities(
-                        Mockito.argThat(new ArgumentMatcher<Intent>() {
-                            @Override
-                            public boolean matches(Object object) {
-                                Intent intent = (Intent) object;
-                                return NetworkScoreManager.ACTION_CUSTOM_ENABLE.equals(
-                                        intent.getAction())
-                                        && scorer.scorerResolveInfo.activityInfo.packageName.equals(
-                                                intent.getPackage());
-                            }
-                        }), Mockito.eq(0))).thenReturn(
-                                Collections.singletonList(scorer.configActivityResolveInfo));
-            }
+    public void testGetPotentialRecommendationProviderPackages_permissionNotGranted()
+            throws Exception {
+        setNetworkRecommendationPackageNames("package1");
+        mockScoreNetworksDenied("package1");
 
-            if (scorer.serviceResolveInfo != null) {
-                // This scorer has a service to bind to
-                Mockito.when(mMockPm.resolveService(
-                        Mockito.argThat(new ArgumentMatcher<Intent>() {
-                            @Override
-                            public boolean matches(Object object) {
-                                Intent intent = (Intent) object;
-                                return NetworkScoreManager.ACTION_SCORE_NETWORKS.equals(
-                                        intent.getAction())
-                                        && scorer.scorerResolveInfo.activityInfo.packageName.equals(
-                                        intent.getPackage());
-                            }
-                        }), Mockito.eq(0))).thenReturn(scorer.serviceResolveInfo);
-            }
+        assertTrue(mNetworkScorerAppManager.getPotentialRecommendationProviderPackages().isEmpty());
+    }
+
+    public void testGetPotentialRecommendationProviderPackages_permissionGranted()
+            throws Exception {
+        setNetworkRecommendationPackageNames("package1");
+        mockScoreNetworksGranted("package1");
+
+        List<String> potentialProviderPackages =
+                mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
+
+        assertFalse(potentialProviderPackages.isEmpty());
+        assertEquals("package1", potentialProviderPackages.get(0));
+    }
+
+    public void testGetPotentialRecommendationProviderPackages_multipleConfigured()
+            throws Exception {
+        setNetworkRecommendationPackageNames("package1", "package2");
+        mockScoreNetworksDenied("package1");
+        mockScoreNetworksGranted("package2");
+
+        List<String> potentialProviderPackages =
+                mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
+
+        assertEquals(1, potentialProviderPackages.size());
+        assertEquals("package2", potentialProviderPackages.get(0));
+    }
+
+    public void testGetNetworkRecommendationProviderData_noPotentialPackages() throws Exception {
+        setNetworkRecommendationPackageNames(/*no configured packages*/);
+        assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
+    }
+
+    public void testGetNetworkRecommendationProviderData_serviceMissing() throws Exception {
+        setNetworkRecommendationPackageNames("package1");
+        mockScoreNetworksGranted("package1");
+
+        assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
+    }
+
+    public void testGetNetworkRecommendationProviderData_scoreNetworksNotGranted()
+            throws Exception {
+        setNetworkRecommendationPackageNames("package1");
+        mockScoreNetworksDenied("package1");
+        mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+
+        assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
+    }
+
+    public void testGetNetworkRecommendationProviderData_available() throws Exception {
+        setNetworkRecommendationPackageNames("package1");
+        mockScoreNetworksGranted("package1");
+        mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+
+        NetworkScorerAppData appData =
+                mNetworkScorerAppManager.getNetworkRecommendationProviderData();
+        assertNotNull(appData);
+        assertEquals("package1", appData.packageName);
+        assertEquals(924, appData.packageUid);
+        assertEquals(".RecommendationService", appData.recommendationServiceClassName);
+    }
+
+    public void testGetActiveScorer_providerAvailable() throws Exception {
+        setNetworkRecommendationPackageNames("package1");
+        mockScoreNetworksGranted("package1");
+        mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+
+        ContentResolver cr = mTargetContext.getContentResolver();
+        Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
+
+        final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+        assertNotNull(activeScorer);
+        assertEquals("package1", activeScorer.packageName);
+        assertEquals(924, activeScorer.packageUid);
+        assertEquals(".RecommendationService", activeScorer.recommendationServiceClassName);
+    }
+
+    public void testGetActiveScorer_providerNotAvailable()
+            throws Exception {
+        ContentResolver cr = mTargetContext.getContentResolver();
+        Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
+
+        final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+        assertNull(activeScorer);
+    }
+
+    public void testGetActiveScorer_recommendationsDisabled() throws Exception {
+        setNetworkRecommendationPackageNames("package1");
+        mockScoreNetworksGranted("package1");
+        mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+        ContentResolver cr = mTargetContext.getContentResolver();
+        Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0);
+
+        final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+        assertNull(activeScorer);
+    }
+
+    private void setNetworkRecommendationPackageNames(String... names) {
+        if (names == null) {
+            names = new String[0];
         }
+        when(mResources.getStringArray(R.array.config_networkRecommendationPackageNames))
+                .thenReturn(names);
+    }
 
-        Mockito.when(mMockPm.queryBroadcastReceiversAsUser(
+    private void mockScoreNetworksGranted(String packageName) {
+        when(mMockPm.checkPermission(permission.SCORE_NETWORKS, packageName))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+    }
+
+    private void mockScoreNetworksDenied(String packageName) {
+        when(mMockPm.checkPermission(permission.SCORE_NETWORKS, packageName))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+    }
+
+    private void mockRecommendationServiceAvailable(final String packageName, int packageUid) {
+        final ResolveInfo serviceInfo = new ResolveInfo();
+        serviceInfo.serviceInfo = new ServiceInfo();
+        serviceInfo.serviceInfo.name = ".RecommendationService";
+        serviceInfo.serviceInfo.packageName = packageName;
+        serviceInfo.serviceInfo.applicationInfo = new ApplicationInfo();
+        serviceInfo.serviceInfo.applicationInfo.uid = packageUid;
+
+        final int flags = 0;
+        when(mMockPm.resolveService(
                 Mockito.argThat(new ArgumentMatcher<Intent>() {
                     @Override
                     public boolean matches(Object object) {
                         Intent intent = (Intent) object;
-                        return NetworkScoreManager.ACTION_SCORE_NETWORKS.equals(intent.getAction());
+                        return NetworkScoreManager.ACTION_RECOMMEND_NETWORKS
+                                .equals(intent.getAction())
+                                && packageName.equals(intent.getPackage());
                     }
-                }), Mockito.eq(0), Mockito.eq(UserHandle.USER_SYSTEM)))
-                .thenReturn(receivers);
-    }
-
-    private ResolveInfoHolder buildResolveInfo(String packageName, int packageUid,
-            boolean hasReceiverPermission, boolean hasScorePermission, boolean hasConfigActivity,
-            boolean hasServiceInfo) throws Exception {
-        Mockito.when(mMockPm.checkPermission(permission.SCORE_NETWORKS, packageName))
-                .thenReturn(hasScorePermission ?
-                        PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
-
-        ResolveInfo resolveInfo = new ResolveInfo();
-        resolveInfo.activityInfo = new ActivityInfo();
-        resolveInfo.activityInfo.packageName = packageName;
-        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
-        resolveInfo.activityInfo.applicationInfo.uid = packageUid;
-        if (hasReceiverPermission) {
-            resolveInfo.activityInfo.permission = permission.BROADCAST_NETWORK_PRIVILEGED;
-        }
-
-        ResolveInfo configActivityInfo = null;
-        if (hasConfigActivity) {
-            configActivityInfo = new ResolveInfo();
-            configActivityInfo.activityInfo = new ActivityInfo();
-            configActivityInfo.activityInfo.name = ".ConfigActivity";
-        }
-
-        ResolveInfo serviceInfo = null;
-        if (hasServiceInfo) {
-            serviceInfo = new ResolveInfo();
-            serviceInfo.serviceInfo = new ServiceInfo();
-            serviceInfo.serviceInfo.name = ".ScoringService";
-        }
-
-        return new ResolveInfoHolder(resolveInfo, configActivityInfo, serviceInfo);
-    }
-
-    private static class ResolveInfoHolder {
-        final ResolveInfo scorerResolveInfo;
-        final ResolveInfo configActivityResolveInfo;
-        final ResolveInfo serviceResolveInfo;
-
-        public ResolveInfoHolder(ResolveInfo scorerResolveInfo,
-                ResolveInfo configActivityResolveInfo, ResolveInfo serviceResolveInfo) {
-            this.scorerResolveInfo = scorerResolveInfo;
-            this.configActivityResolveInfo = configActivityResolveInfo;
-            this.serviceResolveInfo = serviceResolveInfo;
-        }
+                }), Mockito.eq(flags))).thenReturn(serviceInfo);
     }
 }
diff --git a/core/tests/coretests/src/android/net/RecommendationRequestTest.java b/core/tests/coretests/src/android/net/RecommendationRequestTest.java
new file mode 100644
index 0000000..31560b0
--- /dev/null
+++ b/core/tests/coretests/src/android/net/RecommendationRequestTest.java
@@ -0,0 +1,84 @@
+package android.net;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+public class RecommendationRequestTest extends AndroidTestCase {
+    private ScanResult[] mScanResults;
+    private WifiConfiguration mConfiguration;
+    private NetworkCapabilities mCapabilities;
+
+    @Override
+    public void setUp() throws Exception {
+        mScanResults = new ScanResult[2];
+        mScanResults[0] = new ScanResult();
+        mScanResults[1] = new ScanResult(
+                "ssid",
+                "bssid",
+                0L /*hessid*/,
+                1 /*anqpDominId*/,
+                "caps",
+                2 /*level*/,
+                3 /*frequency*/,
+                4L /*tsf*/,
+                5 /*distCm*/,
+                6 /*distSdCm*/,
+                7 /*channelWidth*/,
+                8 /*centerFreq0*/,
+                9 /*centerFreq1*/,
+                false /*is80211McRTTResponder*/);
+        mConfiguration = new WifiConfiguration();
+        mConfiguration.SSID = "RecommendationRequestTest";
+        mCapabilities = new NetworkCapabilities()
+                .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
+    }
+
+    public void testParceling() throws Exception {
+        RecommendationRequest request = new RecommendationRequest.Builder()
+                .setCurrentRecommendedWifiConfig(mConfiguration)
+                .setScanResults(mScanResults)
+                .setNetworkCapabilities(mCapabilities)
+                .build();
+
+        RecommendationRequest parceled = passThroughParcel(request);
+        assertEquals(request.getCurrentSelectedConfig().SSID,
+                parceled.getCurrentSelectedConfig().SSID);
+        assertEquals(request.getRequiredCapabilities(), parceled.getRequiredCapabilities());
+        ScanResult[] parceledScanResults = parceled.getScanResults();
+        assertNotNull(parceledScanResults);
+        assertEquals(mScanResults.length, parceledScanResults.length);
+        for (int i = 0; i < mScanResults.length; i++) {
+            assertEquals(mScanResults[i].SSID, parceledScanResults[i].SSID);
+        }
+    }
+
+    public void testParceling_nullScanResults() throws Exception {
+        RecommendationRequest request = new RecommendationRequest.Builder()
+                .setCurrentRecommendedWifiConfig(mConfiguration)
+                .setNetworkCapabilities(mCapabilities)
+                .build();
+
+        RecommendationRequest parceled = passThroughParcel(request);
+        assertEquals(request.getCurrentSelectedConfig().SSID,
+                parceled.getCurrentSelectedConfig().SSID);
+        assertEquals(request.getRequiredCapabilities(), parceled.getRequiredCapabilities());
+        ScanResult[] parceledScanResults = parceled.getScanResults();
+        assertNull(parceledScanResults);
+    }
+
+    private RecommendationRequest passThroughParcel(RecommendationRequest request) {
+        Parcel p = Parcel.obtain();
+        RecommendationRequest output = null;
+        try {
+            request.writeToParcel(p, 0);
+            p.setDataPosition(0);
+            output = RecommendationRequest.CREATOR.createFromParcel(p);
+        } finally {
+            p.recycle();
+        }
+        assertNotNull(output);
+        return output;
+    }
+}
diff --git a/core/tests/coretests/src/android/text/method/WordIteratorTest.java b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
index 28a480d..66cf65f 100644
--- a/core/tests/coretests/src/android/text/method/WordIteratorTest.java
+++ b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
@@ -516,4 +516,34 @@
         assertFalse(wordIterator.isOnPunctuation(text.length()));
         assertFalse(wordIterator.isOnPunctuation(text.length() + 1));
     }
+
+    @SmallTest
+    public void testApostropheMiddleOfWord() {
+        // These tests confirm that the word "isn't" is treated like one word.
+        final String text = "isn't he";
+        WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+        wordIterator.setCharSequence(text, 0, text.length());
+
+        assertEquals(text.indexOf('i'), wordIterator.preceding(text.indexOf('h')));
+        assertEquals(text.indexOf('t') + 1, wordIterator.following(text.indexOf('i')));
+
+        assertTrue(wordIterator.isBoundary(text.indexOf('i')));
+        assertFalse(wordIterator.isBoundary(text.indexOf('\'')));
+        assertFalse(wordIterator.isBoundary(text.indexOf('t')));
+        assertTrue(wordIterator.isBoundary(text.indexOf('t') + 1));
+        assertTrue(wordIterator.isBoundary(text.indexOf('h')));
+
+        assertEquals(text.indexOf('i'), wordIterator.getBeginning(text.indexOf('i')));
+        assertEquals(text.indexOf('i'), wordIterator.getBeginning(text.indexOf('n')));
+        assertEquals(text.indexOf('i'), wordIterator.getBeginning(text.indexOf('\'')));
+        assertEquals(text.indexOf('i'), wordIterator.getBeginning(text.indexOf('t')));
+        assertEquals(text.indexOf('i'), wordIterator.getBeginning(text.indexOf('t') + 1));
+        assertEquals(text.indexOf('h'), wordIterator.getBeginning(text.indexOf('h')));
+
+        assertEquals(text.indexOf('t') + 1, wordIterator.getEnd(text.indexOf('i')));
+        assertEquals(text.indexOf('t') + 1, wordIterator.getEnd(text.indexOf('n')));
+        assertEquals(text.indexOf('t') + 1, wordIterator.getEnd(text.indexOf('\'')));
+        assertEquals(text.indexOf('t') + 1, wordIterator.getEnd(text.indexOf('t')));
+        assertEquals(text.indexOf('e') + 1, wordIterator.getEnd(text.indexOf('h')));
+    }
 }
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTest.java b/core/tests/coretests/src/android/view/ViewCaptureTest.java
index 15cfe23..1b4d4a2 100644
--- a/core/tests/coretests/src/android/view/ViewCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewCaptureTest.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -32,8 +34,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertTrue;
-
 @RunWith(AndroidJUnit4.class)
 public class ViewCaptureTest {
 
@@ -70,6 +70,7 @@
 
     private void testCreateSnapshot(boolean skipChildren, int goldenResId) {
         Bitmap result = mViewToCapture.createSnapshot(Bitmap.Config.ARGB_8888, 0, skipChildren);
+        result.setHasAlpha(false); // resource will have no alpha, since content is opaque
         Bitmap golden = BitmapFactory.decodeResource(mActivity.getResources(), goldenResId);
         assertTrue(golden.sameAs(result));
     }
diff --git a/docs/__DEPRECATED__DO_NOT_EDIT__.txt b/docs/__DEPRECATED__DO_NOT_EDIT__.txt
new file mode 100644
index 0000000..3f8fc80c
--- /dev/null
+++ b/docs/__DEPRECATED__DO_NOT_EDIT__.txt
@@ -0,0 +1,16 @@
+### DEPRECATED: DO NOT EDIT ###
+
+The source files for developer.android.com are NO LONGER MAINTAINED HERE, as
+of 12/2016. Migration of content was completed on 10/16/2016.
+
+All authoring of content has been moved to Piper (go/dac-source).
+
+Exceptions and Caveats:
+
+- Reference documentation is still maintained via building of .java source files,
+  so you may continue to update JavaDoc comments to update documentation.
+
+- Sample code documentation is not maintained in Piper, but is published from
+  a separate code repository.
+
+For answers to further questions, please email: android-writers@google.com
diff --git a/docs/html/__DEPRECATED__DO_NOT_EDIT__.txt b/docs/html/__DEPRECATED__DO_NOT_EDIT__.txt
new file mode 100644
index 0000000..3f8fc80c
--- /dev/null
+++ b/docs/html/__DEPRECATED__DO_NOT_EDIT__.txt
@@ -0,0 +1,16 @@
+### DEPRECATED: DO NOT EDIT ###
+
+The source files for developer.android.com are NO LONGER MAINTAINED HERE, as
+of 12/2016. Migration of content was completed on 10/16/2016.
+
+All authoring of content has been moved to Piper (go/dac-source).
+
+Exceptions and Caveats:
+
+- Reference documentation is still maintained via building of .java source files,
+  so you may continue to update JavaDoc comments to update documentation.
+
+- Sample code documentation is not maintained in Piper, but is published from
+  a separate code repository.
+
+For answers to further questions, please email: android-writers@google.com
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index a041a28..4b899f6 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -592,9 +592,13 @@
      * @param isMutable True if the resulting bitmap should be mutable (i.e.
      *                  its pixels can be modified)
      * @return the new bitmap, or null if the copy could not be made.
+     * @throws IllegalArgumentException if config is {@link Config#HARDWARE} and isMutable is true
      */
     public Bitmap copy(Config config, boolean isMutable) {
         checkRecycled("Can't copy a recycled bitmap");
+        if (config == Config.HARDWARE && isMutable) {
+            throw new IllegalArgumentException("Hardware bitmaps are always immutable");
+        }
         Bitmap b = nativeCopy(mNativePtr, config.nativeInt, isMutable);
         if (b != null) {
             b.setPremultiplied(mRequestPremultiplied);
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index a5517f0..64a726b 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -51,8 +51,8 @@
         /**
          * If set, decode methods that take the Options object will attempt to
          * reuse this bitmap when loading content. If the decode operation
-         * cannot use this bitmap, the decode method will return
-         * <code>null</code> and will throw an IllegalArgumentException. The
+         * cannot use this bitmap, the decode method will throw an
+         * {@link java.lang.IllegalArgumentException}. The
          * current implementation necessitates that the reused bitmap be
          * mutable, and the resulting reused bitmap will continue to remain
          * mutable even when decoding a resource which would normally result in
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index fc873c4..7815ae1 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1357,7 +1357,7 @@
     /**
      * Return the paint's text size.
      *
-     * @return the paint's text size.
+     * @return the paint's text size in pixel units.
      */
     public float getTextSize() {
         return nGetTextSize(mNativePaint);
@@ -1366,7 +1366,7 @@
     /**
      * Set the paint's text size. This value must be > 0
      *
-     * @param textSize set the paint's text size.
+     * @param textSize set the paint's text size in pixel units.
      */
     public void setTextSize(float textSize) {
         nSetTextSize(mNativePaint, textSize);
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 0fa52f8..c3a9443 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -29,7 +29,7 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGBX_F16, RGB_888, RGB_565})
+    @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGB_888, RGB_565})
     public @interface Format { };
 
     // NOTE: these constants must match the values from graphics/common/x.x/types.hal
@@ -88,7 +88,6 @@
     public static final int YCbCr_422_I = 0x14;
 
     public static final int RGBA_F16    = 0x16;
-    public static final int RGBX_F16    = 0x17;
 
     /**
      * @deprecated use {@link android.graphics.ImageFormat#JPEG
@@ -134,7 +133,6 @@
                 info.bytesPerPixel = 1;
                 break;
             case RGBA_F16:
-            case RGBX_F16:
                 info.bitsPerPixel = 64;
                 info.bytesPerPixel = 8;
                 break;
@@ -178,7 +176,6 @@
             case RGB_888:
             case RGB_565:
             case RGBA_F16:
-            case RGBX_F16:
                 return true;
         }
 
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index a9058b1..f6585d6 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -98,8 +98,6 @@
 // TODO: handle SRGB sanely
 static PixelFormat internalFormatToPixelFormat(GLint internalFormat) {
     switch (internalFormat) {
-    case GL_ALPHA:
-        return PIXEL_FORMAT_TRANSPARENT;
     case GL_LUMINANCE:
         return PIXEL_FORMAT_RGBA_8888;
     case GL_SRGB8_ALPHA8:
@@ -217,8 +215,8 @@
     }
 
     const SkImageInfo& info = skBitmap.info();
-    if (info.colorType() == kUnknown_SkColorType) {
-        ALOGW("unable to create hardware bitmap of configuration");
+    if (info.colorType() == kUnknown_SkColorType || info.colorType() == kAlpha_8_SkColorType) {
+        ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
         return nullptr;
     }
 
@@ -251,7 +249,7 @@
     if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) {
         return nullptr;
     }
-    return sk_sp<Bitmap>(new Bitmap(buffer.get(), info));
+    return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
 }
 
 sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
@@ -313,7 +311,8 @@
         return nullptr;
     }
     SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
-            kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+            kRGBA_8888_SkColorType, kPremul_SkAlphaType,
+            SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named));
     return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info));
 }
 
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 03f0b4d..23a8655 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -63,6 +63,7 @@
     private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
 
     private URL mUrl;
+    private String mUserAgent;
     private Network mNetwork;
     private CaptivePortal mCaptivePortal;
     private NetworkCallback mNetworkCallback;
@@ -76,6 +77,8 @@
         mCm = ConnectivityManager.from(this);
         mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
         mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
+        mUserAgent = getIntent().getParcelableExtra(
+                ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT);
         mUrl = getUrl();
         if (mUrl == null) {
             // getUrl() failed to parse the url provided in the intent: bail out in a way that
@@ -252,6 +255,7 @@
     }
 
     private void testForCaptivePortal() {
+        // TODO: reuse NetworkMonitor facilities for consistent captive portal detection.
         new Thread(new Runnable() {
             public void run() {
                 // Give time for captive portal to open.
@@ -262,11 +266,14 @@
                 HttpURLConnection urlConnection = null;
                 int httpResponseCode = 500;
                 try {
-                    urlConnection = (HttpURLConnection) mUrl.openConnection();
+                    urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl);
                     urlConnection.setInstanceFollowRedirects(false);
                     urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
                     urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
                     urlConnection.setUseCaches(false);
+                    if (mUserAgent != null) {
+                       urlConnection.setRequestProperty("User-Agent", mUserAgent);
+                    }
                     urlConnection.getInputStream();
                     httpResponseCode = urlConnection.getResponseCode();
                 } catch (IOException e) {
diff --git a/packages/PrintRecommendationService/AndroidManifest.xml b/packages/PrintRecommendationService/AndroidManifest.xml
index c6736d7..2e9342c 100644
--- a/packages/PrintRecommendationService/AndroidManifest.xml
+++ b/packages/PrintRecommendationService/AndroidManifest.xml
@@ -18,11 +18,11 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.printservice.recommendation"
-    android:versionCode="1"
-    android:versionName="1.0.0">
+    android:versionCode="2"
+    android:versionName="1.1.0">
 
     <uses-sdk android:minSdkVersion="24"
-        android:targetSdkVersion="24" />
+        android:targetSdkVersion="25" />
 
     <uses-permission android:name="android.permission.INTERNET" />
 
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index c1108f6..208fa6d 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -26,7 +26,7 @@
 LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_JAVA_LIBRARIES := \
-    junit4-target \
+    junit \
     platform-robolectric-prebuilt
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
@@ -55,7 +55,7 @@
     truth-prebuilt
 
 LOCAL_JAVA_LIBRARIES := \
-    junit4-target \
+    junit \
     platform-robolectric-prebuilt
 
 LOCAL_INSTRUMENTATION_FOR := SettingsLibShell
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d232e00..5c2787b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -287,22 +287,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".recents.grid.RecentsGridActivity"
-                  android:label="@string/accessibility_desc_recent_apps"
-                  android:exported="false"
-                  android:launchMode="singleInstance"
-                  android:excludeFromRecents="true"
-                  android:stateNotNeeded="true"
-                  android:resumeWhilePausing="true"
-                  android:screenOrientation="behind"
-                  android:resizeableActivity="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:theme="@style/RecentsTheme.Grid">
-            <intent-filter>
-                <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
-            </intent-filter>
-        </activity>
-
         <activity android:name=".recents.tv.RecentsTvActivity"
                   android:label="@string/accessibility_desc_recent_apps"
                   android:exported="false"
diff --git a/packages/SystemUI/res/drawable/header_dot.xml b/packages/SystemUI/res/drawable/header_dot.xml
index 568a9c2..dbc6b37 100644
--- a/packages/SystemUI/res/drawable/header_dot.xml
+++ b/packages/SystemUI/res/drawable/header_dot.xml
@@ -2,26 +2,27 @@
 <!--
 ** Copyright 2015, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** 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.
 -->
 <shape
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
+    android:shape="oval"
+    android:tint="?android:attr/colorControlNormal">
 
-    <solid 
+    <solid
         android:color="#FFFFFF"/>
 
-    <size 
+    <size
         android:width="3dp"
         android:height="3dp"/>
 </shape>
diff --git a/packages/SystemUI/res/drawable/ic_qs_network_logging.xml b/packages/SystemUI/res/drawable/ic_qs_network_logging.xml
index 1340ae1..87b5a14 100644
--- a/packages/SystemUI/res/drawable/ic_qs_network_logging.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_network_logging.xml
@@ -24,6 +24,6 @@
   android:tint="#4DFFFFFF" >
     <path
       android:fillColor="#FFFFFFFF"
-      android:pathData="M7,18v-2h6v2H7z M7,14v-2h10v2H7z M8.5,9 12,5.5 15.5,9 13,9 13,13 11,13 11,9z"/>
+      android:pathData="M2,24v-4h12v4H2z M2,16v-4h20v4H2z M5,7 12,0 19,7 14,7 14,15 10,15 10,7z"/>
 
 </vector>
diff --git a/packages/SystemUI/res/layout-sw600dp/recents_grid.xml b/packages/SystemUI/res/layout-sw600dp/recents_grid.xml
deleted file mode 100644
index 1a054a5..0000000
--- a/packages/SystemUI/res/layout-sw600dp/recents_grid.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:id="@+id/recents_view"
-    android:gravity="center"
-    android:background="#99000000">
-    <include layout="@layout/recents_stack_action_button" />
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/battery_detail.xml b/packages/SystemUI/res/layout/battery_detail.xml
index 8abfcf6..6162d65 100644
--- a/packages/SystemUI/res/layout/battery_detail.xml
+++ b/packages/SystemUI/res/layout/battery_detail.xml
@@ -41,7 +41,7 @@
         android:layout_marginEnd="24dp"
         systemui:sideLabels="@array/battery_labels"
         android:colorAccent="?android:attr/colorAccent"
-        systemui:textColor="#66FFFFFF" />
+        systemui:textColor="?android:attr/textColorSecondary" />
 
     <com.android.systemui.ResizingSpace
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
new file mode 100644
index 0000000..2ba04fd
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/scrollView"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="?android:attr/dialogPreferredPadding"
+        android:paddingRight="?android:attr/dialogPreferredPadding"
+        android:paddingLeft="?android:attr/dialogPreferredPadding"
+        android:paddingBottom="?android:attr/dialogPreferredPadding"
+        android:orientation="vertical">
+        <TextView
+            android:id="@+id/device_owner_warning"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="@android:style/TextAppearance.Material.Subhead"
+            android:textColor="?android:attr/textColorPrimaryInverse"
+        />
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+            <ImageView
+                android:id="@+id/vpn_icon"
+                android:layout_width="@dimen/qs_footer_dialog_icon_size"
+                android:layout_height="wrap_content"
+                android:paddingTop="?android:attr/dialogPreferredPadding"
+                android:layout_marginStart="@dimen/qs_footer_dialog_icon_margin"
+                android:layout_marginEnd="@dimen/qs_footer_dialog_icon_margin"
+                android:scaleType="fitCenter"
+                android:src="@drawable/ic_qs_vpn"
+                android:tint="?android:attr/textColorPrimaryInverse"
+                android:adjustViewBounds="true"/>
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+                <TextView
+                    android:id="@+id/vpn_subtitle"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:paddingTop="?android:attr/dialogPreferredPadding"
+                    android:text="@string/monitoring_subtitle_vpn"
+                    style="@android:style/TextAppearance.Material.Title"
+                    android:textColor="?android:attr/textColorPrimaryInverse"
+                />
+                <TextView
+                    android:id="@+id/vpn_warning"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@null"
+                    style="@android:style/TextAppearance.Material.Subhead"
+                    android:textColor="?android:attr/textColorPrimaryInverse"
+                />
+            </LinearLayout>
+        </LinearLayout>
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+            <ImageView
+                android:id="@+id/network_logging_icon"
+                android:layout_width="@dimen/qs_footer_dialog_icon_size"
+                android:layout_height="wrap_content"
+                android:paddingTop="?android:attr/dialogPreferredPadding"
+                android:layout_marginStart="@dimen/qs_footer_dialog_icon_margin"
+                android:layout_marginEnd="@dimen/qs_footer_dialog_icon_margin"
+                android:scaleType="fitCenter"
+                android:src="@drawable/ic_qs_network_logging"
+                android:tint="?android:attr/textColorPrimaryInverse"
+                android:alpha="@dimen/qs_footer_dialog_network_logging_icon_alpha"
+                android:adjustViewBounds="true"/>
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+                <TextView
+                    android:id="@+id/network_logging_subtitle"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:paddingTop="?android:attr/dialogPreferredPadding"
+                    android:text="@string/monitoring_subtitle_network_logging"
+                    style="@android:style/TextAppearance.Material.Title"
+                    android:textColor="?android:attr/textColorPrimaryInverse"
+                />
+                <TextView
+                    android:id="@+id/network_logging_warning"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/monitoring_description_network_logging"
+                    style="@android:style/TextAppearance.Material.Subhead"
+                    android:textColor="?android:attr/textColorPrimaryInverse"
+                />
+            </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+</ScrollView>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fe5606e..ff4ec5b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -258,6 +258,13 @@
     <!-- How far the expanded QS panel peeks from the header in collapsed state. -->
     <dimen name="qs_peek_height">0dp</dimen>
 
+    <!-- How large the icons in the quick settings footer dialog are -->
+    <dimen name="qs_footer_dialog_icon_size">24sp</dimen>
+    <!-- Left and right margin of the icons -->
+    <dimen name="qs_footer_dialog_icon_margin">8sp</dimen>
+    <!-- Alpha value of network logging icon -->
+    <dimen name="qs_footer_dialog_network_logging_icon_alpha">0.3</dimen>
+
     <!-- Zen mode panel: condition item button padding -->
     <dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
 
@@ -720,18 +727,4 @@
     <!-- The size of a PIP menu action icon. -->
     <dimen name="pip_menu_action_icon_size">32dp</dimen>
 
-    <!-- Values specific to grid-based recents. -->
-    <!-- Margins around recent tasks. -->
-    <dimen name="recents_grid_margin_left">15dp</dimen>
-    <dimen name="recents_grid_margin_top">70dp</dimen>
-    <dimen name="recents_grid_margin_right">15dp</dimen>
-    <dimen name="recents_grid_margin_bottom">90dp</dimen>
-    <!-- Margins around the "Clear all" button. -->
-    <dimen name="recents_grid_clear_all_margin_left">0dp</dimen>
-    <dimen name="recents_grid_clear_all_margin_top">30dp</dimen>
-    <dimen name="recents_grid_clear_all_margin_right">15dp</dimen>
-    <dimen name="recents_grid_clear_all_margin_bottom">0dp</dimen>
-    <!-- Padding in between task views. -->
-    <dimen name="recents_grid_inter_task_padding">15dp</dimen>
-
 </resources>
diff --git a/packages/SystemUI/res/values/dimens_grid.xml b/packages/SystemUI/res/values/dimens_grid.xml
index 43e152a..94ffdb1 100644
--- a/packages/SystemUI/res/values/dimens_grid.xml
+++ b/packages/SystemUI/res/values/dimens_grid.xml
@@ -17,7 +17,9 @@
 -->
 <resources>
   <dimen name="recents_grid_padding_left_right">32dp</dimen>
-  <dimen name="recents_grid_padding_top_bottom">84dp</dimen>
+  <dimen name="recents_grid_padding_top_bottom">150dp</dimen>
   <dimen name="recents_grid_padding_task_view">20dp</dimen>
+  <dimen name="recents_grid_task_view_header_height">44dp</dimen>
+  <dimen name="recents_grid_task_view_header_button_padding">8dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 31b83cc..d5638a8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1010,6 +1010,13 @@
     <!-- Monitoring dialog title for normal devices  [CHAR LIMIT=35]-->
     <string name="monitoring_title">Network monitoring</string>
 
+    <!-- STOPSHIP Monitoring strings still need to be finalized and approved -->
+    <!-- Monitoring dialog subtitle for the section describing VPN [CHAR LIMIT=TODO]-->
+    <string name="monitoring_subtitle_vpn">VPN</string>
+
+    <!-- Monitoring dialog subtitle for the section describing network logging [CHAR LIMIT=TODO]-->
+    <string name="monitoring_subtitle_network_logging">Network Logging</string>
+
     <!-- Monitoring dialog disable vpn button [CHAR LIMIT=30] -->
     <string name="disable_vpn">Disable VPN</string>
 
@@ -1025,15 +1032,25 @@
     <!-- Monitoring dialog: Part of text body explaining what a Device Owner app can do [CHAR LIMIT=130] -->
     <string name="monitoring_description_do_body">Your administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.</string>
 
-    <!-- Monitoring dialog: Part of text body explaining that a VPN is connected and what it can do, for devices managed by a Device Owner app [CHAR LIMIT=130] -->
-    <string name="monitoring_description_do_body_vpn">You\'re connected to <xliff:g id="vpn_app">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
-
     <!-- Monitoring dialog: Space that separates the body text and the "learn more" link that follows it. [CHAR LIMIT=5] -->
     <string name="monitoring_description_do_learn_more_separator">" "</string>
 
     <!-- Monitoring dialog: Link to learn more about what a Device Owner app can do [CHAR LIMIT=55] -->
     <string name="monitoring_description_do_learn_more">Learn more</string>
 
+    <!-- Monitoring dialog: Part of text body explaining that a VPN is connected and what it can do, for devices managed by a Device Owner app [CHAR LIMIT=130] -->
+    <string name="monitoring_description_do_body_vpn">You\'re connected to <xliff:g id="vpn_app">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
+
+    <!-- Monitoring dialog: Space that separates the VPN body text and the "Open VPN Settings" link that follows it. [CHAR LIMIT=5] -->
+    <string name="monitoring_description_vpn_settings_separator">" "</string>
+
+    <!-- Monitoring dialog: Link to open the VPN settings page [CHAR LIMIT=TODO] -->
+    <string name="monitoring_description_vpn_settings">Open VPN Settings</string>
+
+    <!-- Monitoring dialog: Network logging text [CHAR LIMIT=TODO] -->
+    <string name="monitoring_description_network_logging">Your admin has turned on network logging, which monitors traffic on your device.\n\nFor more information contact your admin.</string>
+
+
     <!-- Monitoring dialog VPN text [CHAR LIMIT=400] -->
     <string name="monitoring_description_vpn">You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps, and websites.</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 08b8988..93b6965 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -44,22 +44,6 @@
         <item name="android:layout_marginBottom">0dp</item>
     </style>
 
-    <!-- Grid-based Recents theme. -->
-    <style name="RecentsTheme.Grid">
-        <item name="android:windowBackground">@color/transparent</item>
-        <item name="android:colorBackgroundCacheHint">@null</item>
-        <item name="android:windowShowWallpaper">true</item>
-        <item name="android:windowDisablePreview">true</item>
-        <item name="clearAllStyle">@style/ClearAllButtonLargeMargins</item>
-    </style>
-
-    <style name="ClearAllButtonLargeMargins">
-        <item name="android:layout_marginStart">@dimen/recents_grid_clear_all_margin_left</item>
-        <item name="android:layout_marginTop">@dimen/recents_grid_clear_all_margin_top</item>
-        <item name="android:layout_marginEnd">@dimen/recents_grid_clear_all_margin_right</item>
-        <item name="android:layout_marginBottom">@dimen/recents_grid_clear_all_margin_bottom</item>
-    </style>
-
     <!-- Performance optimized Recents theme (no wallpaper) -->
     <style name="RecentsTheme.NoWallpaper">
         <item name="android:windowBackground">@android:color/black</item>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 13e047c..6a868d5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -218,7 +218,8 @@
                     Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE);
                     break;
                 case DOZE_PULSE_DONE:
-                    Preconditions.checkState(mState == State.DOZE_PULSING);
+                    Preconditions.checkState(
+                            mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING);
                     break;
                 default:
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index f3da47b..0e4799b0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -31,6 +31,7 @@
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.View.OnClickListener;
 import android.view.Window;
 import android.widget.ImageView;
@@ -175,6 +176,7 @@
     private void createDialog() {
         final String deviceOwnerPackage = mSecurityController.getDeviceOwnerName();
         final String profileOwnerPackage = mSecurityController.getProfileOwnerName();
+        final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
         final String primaryVpn = mSecurityController.getPrimaryVpnName();
         final String profileVpn = mSecurityController.getProfileVpnName();
         final CharSequence deviceOwnerOrganization =
@@ -186,24 +188,47 @@
         if (!isBranded) {
             mDialog.setTitle(getTitle(deviceOwnerPackage));
         }
-        mDialog.setMessage(getMessage(deviceOwnerPackage, profileOwnerPackage, primaryVpn,
-                profileVpn, deviceOwnerOrganization, hasProfileOwner, isBranded));
+        CharSequence msg = getMessage(deviceOwnerPackage, profileOwnerPackage, primaryVpn,
+                profileVpn, deviceOwnerOrganization, hasProfileOwner, isBranded);
+        if (deviceOwnerPackage == null) {
+            mDialog.setMessage(msg);
+        } else {
+            View dialogView = LayoutInflater.from(mContext)
+                   .inflate(R.layout.quick_settings_footer_dialog, null, false);
+            mDialog.setView(dialogView);
+            TextView deviceOwnerWarning =
+                    (TextView) dialogView.findViewById(R.id.device_owner_warning);
+            deviceOwnerWarning.setText(msg);
+            // Make the link "learn more" clickable.
+            deviceOwnerWarning.setMovementMethod(new LinkMovementMethod());
+            if (primaryVpn == null) {
+                dialogView.findViewById(R.id.vpn_icon).setVisibility(View.GONE);
+                dialogView.findViewById(R.id.vpn_subtitle).setVisibility(View.GONE);
+                dialogView.findViewById(R.id.vpn_warning).setVisibility(View.GONE);
+            } else {
+                final SpannableStringBuilder message = new SpannableStringBuilder();
+                message.append(mContext.getString(R.string.monitoring_description_do_body_vpn,
+                        primaryVpn));
+                message.append(mContext.getString(
+                        R.string.monitoring_description_vpn_settings_separator));
+                message.append(mContext.getString(R.string.monitoring_description_vpn_settings),
+                        new VpnSpan(), 0);
 
-        mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(isBranded), this);
-        if (mSecurityController.isVpnEnabled() && !mSecurityController.isVpnRestricted()) {
-            mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
+                TextView vpnWarning = (TextView) dialogView.findViewById(R.id.vpn_warning);
+                vpnWarning.setText(message);
+                // Make the link "Open VPN Settings" clickable.
+                vpnWarning.setMovementMethod(new LinkMovementMethod());
+            }
+            if (!isNetworkLoggingEnabled) {
+                dialogView.findViewById(R.id.network_logging_icon).setVisibility(View.GONE);
+                dialogView.findViewById(R.id.network_logging_subtitle).setVisibility(View.GONE);
+                dialogView.findViewById(R.id.network_logging_warning).setVisibility(View.GONE);
+            }
         }
 
-        // Make the link "learn more" clickable.
-        // TODO: Reaching into SystemUIDialog's internal View hierarchy is ugly and error-prone.
-        // This is a temporary solution. b/33126622 will introduce a custom View hierarchy for this
-        // dialog, allowing us to set the movement method on the appropriate View without any
-        // knowledge of SystemUIDialog's internals.
-        mDialog.create();
-        ((TextView) mDialog.getWindow().findViewById(com.android.internal.R.id.message))
-                .setMovementMethod(new LinkMovementMethod());
-
+        mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(isBranded), this);
         mDialog.show();
+        mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
     }
 
     private String getSettingsButton() {
@@ -229,11 +254,6 @@
             }
             message.append("\n\n");
             message.append(mContext.getString(R.string.monitoring_description_do_body));
-            if (primaryVpn != null) {
-                message.append("\n\n");
-                message.append(mContext.getString(R.string.monitoring_description_do_body_vpn,
-                        primaryVpn));
-            }
             message.append(mContext.getString(
                     R.string.monitoring_description_do_learn_more_separator));
             message.append(mContext.getString(R.string.monitoring_description_do_learn_more),
@@ -340,4 +360,14 @@
             return object instanceof EnterprisePrivacySpan;
         }
     }
+
+    protected class VpnSpan extends ClickableSpan {
+        @Override
+        public void onClick(View widget) {
+            final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            mDialog.dismiss();
+            mContext.startActivity(intent);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 9bbead4..0be53b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -60,8 +60,6 @@
 
     private final QSDetailClipper mClipper;
 
-    private PhoneStatusBar mPhoneStatusBar;
-
     private boolean isShown;
     private QSTileHost mHost;
     private RecyclerView mRecyclerView;
@@ -119,7 +117,6 @@
 
     public void setHost(QSTileHost host) {
         mHost = host;
-        mPhoneStatusBar = host.getPhoneStatusBar();
         mTileAdapter.setHost(host);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index d9fc2fc..0265c9e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -57,7 +57,6 @@
 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.grid.RecentsGridImpl;
 import com.android.systemui.recents.tv.RecentsTvImpl;
 import com.android.systemui.stackdivider.Divider;
 
@@ -84,7 +83,6 @@
     static {
         RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY);
         RECENTS_ACTIVITIES.add(RecentsTvImpl.RECENTS_TV_ACTIVITY);
-        RECENTS_ACTIVITIES.add(RecentsGridImpl.RECENTS_MOSAIC_ACTIVITY);
     }
 
     // Purely for experimentation
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index c9c4f2b..1371381 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -576,7 +576,8 @@
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height_tablet_land,
                 R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height_tablet_land);
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_grid_task_view_header_height);
 
         LayoutInflater inflater = LayoutInflater.from(mContext);
         mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
@@ -720,7 +721,7 @@
                 if (task.isFreeformTask()) {
                     mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
                             stackScroller.getStackScroll(), mTmpTransform, null,
-                            windowOverrideRect);
+                            windowOverrideRect, false /* useGridLayout */);
                     Bitmap thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform,
                             mThumbTransitionBitmapCache);
                     Rect toTaskRect = new Rect();
@@ -770,7 +771,8 @@
         stackView.updateLayoutAlgorithm(true /* boundScroll */);
         stackView.updateToInitialState();
         stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
-                stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect);
+                stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect,
+                Recents.getConfiguration().isGridEnabled);
         return mTmpTransform;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java b/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java
deleted file mode 100644
index 71c2148..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.grid;
-
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.util.DisplayMetrics;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.ScaleAnimation;
-import android.view.animation.TranslateAnimation;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
-import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
-import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.Utilities;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskView;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * The main grid recents activity started by the RecentsImpl.
- */
-public class RecentsGridActivity extends Activity {
-    public final static int MAX_VISIBLE_TASKS = 9;
-
-    private final static String TAG = "RecentsGridActivity";
-    private final static int TITLE_BAR_HEIGHT_DP = 64;
-
-    private ArrayList<Integer> mMargins = new ArrayList<>();
-
-    private TaskStack mTaskStack;
-    private ArrayList<Task> mTasks = new ArrayList<>();
-    private ArrayList<TaskView> mTaskViews = new ArrayList<>();
-    private ArrayList<Rect> mTaskViewRects;
-    private FrameLayout mRecentsView;
-    private TextView mEmptyView;
-    private View mClearAllButton;
-    private int mLastDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
-    private int mLastDisplayDensity;
-    private Rect mDisplayRect = new Rect();
-    private LayoutInflater mInflater;
-    private boolean mTouchExplorationEnabled;
-    private Point mScreenSize;
-    private int mTitleBarHeightPx;
-    private int mStatusBarHeightPx;
-    private int mNavigationBarHeightPx;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.recents_grid);
-        SystemServicesProxy ssp = Recents.getSystemServices();
-
-        Resources res = getResources();
-        Integer[] margins = {
-                res.getDimensionPixelSize(R.dimen.recents_grid_margin_left),
-                res.getDimensionPixelSize(R.dimen.recents_grid_margin_top),
-                res.getDimensionPixelSize(R.dimen.recents_grid_margin_right),
-                res.getDimensionPixelSize(R.dimen.recents_grid_margin_bottom),
-        };
-        mMargins.addAll(Arrays.asList(margins));
-
-        mInflater = LayoutInflater.from(this);
-        Configuration appConfiguration = Utilities.getAppConfiguration(this);
-        mDisplayRect = ssp.getDisplayRect();
-        mLastDisplayOrientation = appConfiguration.orientation;
-        mLastDisplayDensity = appConfiguration.densityDpi;
-        mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
-        mScreenSize = new Point();
-        getWindowManager().getDefaultDisplay().getRealSize(mScreenSize);
-        DisplayMetrics metrics = res.getDisplayMetrics();
-        mTitleBarHeightPx = (int) (TITLE_BAR_HEIGHT_DP *
-                ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT));
-        mStatusBarHeightPx = res.getDimensionPixelSize(R.dimen.status_bar_height);
-        mNavigationBarHeightPx = res.getDimensionPixelSize(R.dimen.navigation_bar_height);
-
-        mRecentsView = (FrameLayout) findViewById(R.id.recents_view);
-        mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
-                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
-                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-        getWindow().getAttributes().privateFlags |=
-                WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
-        mEmptyView = (TextView) mInflater.inflate(R.layout.recents_empty, mRecentsView, false);
-        mClearAllButton = findViewById(R.id.button);
-
-        FrameLayout.LayoutParams emptyViewLayoutParams = new FrameLayout.LayoutParams(
-            FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
-        emptyViewLayoutParams.gravity = Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL;
-        mEmptyView.setLayoutParams(emptyViewLayoutParams);
-        mRecentsView.addView(mEmptyView);
-
-        mClearAllButton.setVisibility(View.VISIBLE);
-        FrameLayout.LayoutParams lp =
-                (FrameLayout.LayoutParams) mClearAllButton.getLayoutParams();
-        lp.gravity = Gravity.END;
-
-        mClearAllButton.setOnClickListener(v -> {
-            EventBus.getDefault().send(new DismissAllTaskViewsEvent());
-        });
-
-        mRecentsView.setOnClickListener(v -> {
-            EventBus.getDefault().send(new HideRecentsEvent(
-                    false /* triggeredFromAltTab */, false /* triggeredFromHomeKey */));
-        });
-
-        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY);
-    }
-
-    private TaskView createView() {
-        return (TaskView) mInflater.inflate(R.layout.recents_task_view, mRecentsView, false);
-    }
-
-    private void removeTaskViews() {
-        for (View taskView : mTaskViews) {
-            ViewGroup parent = (ViewGroup) taskView.getParent();
-            if (parent != null) {
-                parent.removeView(taskView);
-            }
-        }
-    }
-
-    private void clearTaskViews() {
-        removeTaskViews();
-        mTaskViews.clear();
-    }
-
-    private TaskView getChildViewForTask(Task task) {
-        for (TaskView tv : mTaskViews) {
-            if (tv.getTask() == task) {
-                return tv;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Starts animations for each task view to either enlarge it to the size of the screen (when
-     * launching a task), or (if {@code reverse} is true, to reduce it from the size of the screen
-     * back to its place in the recents layout (when opening recents).
-     * @param animationListener An animation listener for executing code before or after the
-     *         animations run.
-     * @param reverse Whether the blow-up animations should be run in reverse.
-     */
-    private void startBlowUpAnimations(Animation.AnimationListener animationListener,
-            boolean reverse) {
-        if (mTaskViews.size() == 0) {
-            return;
-        }
-        int screenWidth = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
-                ? mScreenSize.x : mScreenSize.y;
-        int screenHeight = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
-                ? mScreenSize.y : mScreenSize.x;
-        screenHeight -= mStatusBarHeightPx + mNavigationBarHeightPx;
-        for (int i = 0; i < mTaskViews.size(); i++) {
-            View tv = mTaskViews.get(i);
-            AnimationSet animations = new AnimationSet(true /* shareInterpolator */);
-            animations.setInterpolator(new DecelerateInterpolator());
-            if (i == 0 && animationListener != null) {
-                animations.setAnimationListener(animationListener);
-            }
-            animations.setFillBefore(reverse);
-            animations.setFillAfter(!reverse);
-            Rect initialRect = mTaskViewRects.get(mTaskViewRects.size() - 1 - i);
-            int xDelta = - initialRect.left;
-            int yDelta = - initialRect.top - mTitleBarHeightPx + mStatusBarHeightPx;
-            TranslateAnimation translate = new TranslateAnimation(
-                    reverse ? xDelta : 0, reverse ? 0 : xDelta,
-                    reverse ? yDelta : 0, reverse ? 0 : yDelta);
-            translate.setDuration(250);
-            animations.addAnimation(translate);
-
-
-            float xScale = (float) screenWidth / (float) initialRect.width();
-            float yScale = (float) screenHeight /
-                    ((float) initialRect.height() - mTitleBarHeightPx);
-            ScaleAnimation scale = new ScaleAnimation(
-                    reverse ? xScale : 1, reverse ? 1 : xScale,
-                    reverse ? yScale : 1, reverse ? 1 : yScale,
-                    Animation.ABSOLUTE, 0, Animation.ABSOLUTE, mStatusBarHeightPx);
-            scale.setDuration(300);
-            animations.addAnimation(scale);
-
-            tv.startAnimation(animations);
-        }
-    }
-
-    private void updateControlVisibility() {
-        boolean empty = (mTasks.size() == 0);
-        mClearAllButton.setVisibility(empty ? View.INVISIBLE : View.VISIBLE);
-        mEmptyView.setVisibility(empty ? View.VISIBLE : View.INVISIBLE);
-        if (empty) {
-            mEmptyView.bringToFront();
-        }
-    }
-
-    private void updateModel() {
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
-        if (plan == null) {
-            plan = loader.createLoadPlan(this);
-        }
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-        if (!plan.hasTasks()) {
-            loader.preloadTasks(plan, -1, !launchState.launchedFromHome);
-        }
-        mTaskStack = plan.getTaskStack();
-        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
-        loadOpts.runningTaskId = launchState.launchedToTaskId;
-        loadOpts.numVisibleTasks = MAX_VISIBLE_TASKS;
-        loadOpts.numVisibleTaskThumbnails = MAX_VISIBLE_TASKS;
-        loader.loadTasks(this, plan, loadOpts);
-
-        mTasks = mTaskStack.getStackTasks();
-    }
-
-    private void updateViews() {
-        int screenWidth = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
-                ? mScreenSize.x : mScreenSize.y;
-        int screenHeight = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
-                ? mScreenSize.y : mScreenSize.x;
-        int paddingPixels = getResources().getDimensionPixelSize(
-                R.dimen.recents_grid_inter_task_padding);
-        mTaskViewRects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
-                mTasks.size(), screenWidth, screenHeight, getAppRectRatio(), paddingPixels,
-                mMargins, mTitleBarHeightPx);
-        boolean recycleViews = (mTaskViews.size() == mTasks.size());
-        if (!recycleViews) {
-            clearTaskViews();
-        }
-        for (int i = 0; i < mTasks.size(); i++) {
-            Task task = mTasks.get(i);
-            // We keep the same ordering in the model as other Recents flavors (older tasks are
-            // first in the stack) so that the logic can be similar, but we reverse the order
-            // when placing views on the screen so that most recent tasks are displayed first.
-            Rect rect = mTaskViewRects.get(mTaskViewRects.size() - 1 - i);
-            TaskView taskView;
-            if (recycleViews) {
-                taskView = mTaskViews.get(i);
-            } else {
-                taskView = createView();
-            }
-            taskView.onTaskBound(task, mTouchExplorationEnabled, mLastDisplayOrientation,
-                    mDisplayRect);
-            Recents.getTaskLoader().loadTaskData(task);
-            taskView.setTouchEnabled(true);
-            // Show dismiss button right away.
-            taskView.startNoUserInteractionAnimation();
-            taskView.setLayoutParams(new FrameLayout.LayoutParams(rect.width(), rect.height()));
-            taskView.setTranslationX(rect.left);
-            taskView.setTranslationY(rect.top);
-            if (!recycleViews) {
-                mRecentsView.addView(taskView);
-                mTaskViews.add(taskView);
-            }
-        }
-        updateControlVisibility();
-    }
-
-    private float getAppRectRatio() {
-        if (mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE) {
-            return (float) mScreenSize.x /
-                    (float) (mScreenSize.y - mStatusBarHeightPx - mNavigationBarHeightPx);
-        } else {
-            return (float) mScreenSize.y /
-                    (float) (mScreenSize.x - mStatusBarHeightPx - mNavigationBarHeightPx);
-        }
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
-        updateModel();
-        updateViews();
-        if (mTaskViews.size() > 0) {
-            mTaskViews.get(mTaskViews.size() - 1).bringToFront();
-        }
-        startBlowUpAnimations(new Animation.AnimationListener() {
-            @Override
-            public void onAnimationStart(Animation animation) { }
-
-            @Override
-            public void onAnimationEnd(Animation animation) {
-                updateViews();
-            }
-
-            @Override
-            public void onAnimationRepeat(Animation animation) { }
-        }, true /* reverse */);
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        EventBus.getDefault().unregister(this);
-    }
-
-    @Override
-    public void onBackPressed() {
-        // Back behaves like the recents button so just trigger a toggle event.
-        EventBus.getDefault().send(new ToggleRecentsEvent());
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        // Notify of the config change.
-        Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this);
-        mDisplayRect = Recents.getSystemServices().getDisplayRect();
-        int numStackTasks = mTaskStack.getStackTaskCount();
-        EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
-                mLastDisplayOrientation != newDeviceConfiguration.orientation,
-                mLastDisplayDensity != newDeviceConfiguration.densityDpi, numStackTasks > 0));
-        mLastDisplayOrientation = newDeviceConfiguration.orientation;
-        mLastDisplayDensity = newDeviceConfiguration.densityDpi;
-        updateViews();
-    }
-
-    void dismissRecentsToHome() {
-        Intent startMain = new Intent(Intent.ACTION_MAIN);
-        startMain.addCategory(Intent.CATEGORY_HOME);
-        startActivity(startMain);
-    }
-
-    /** Launches the task that recents was launched from if possible. */
-    boolean launchPreviousTask() {
-        if (mRecentsView != null) {
-            Task task = mTaskStack.getLaunchTarget();
-            if (task != null) {
-                TaskView taskView = getChildViewForTask(task);
-                EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
-                        INVALID_STACK_ID, false));
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /** Dismisses recents back to the launch target task. */
-    boolean dismissRecentsToLaunchTargetTaskOrHome() {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp.isRecentsActivityVisible()) {
-            // If we can launch the task that Recents was launched from, do that, otherwise go home.
-            if (launchPreviousTask()) return true;
-            dismissRecentsToHome();
-        }
-        return false;
-    }
-
-    /**** EventBus events ****/
-
-    public final void onBusEvent(HideRecentsEvent event) {
-        if (event.triggeredFromAltTab) {
-            dismissRecentsToLaunchTargetTaskOrHome();
-        } else if (event.triggeredFromHomeKey) {
-            dismissRecentsToHome();
-        } else {
-            // Fall through tap on the background view but not on any of the tasks.
-            dismissRecentsToHome();
-        }
-    }
-
-    public final void onBusEvent(ToggleRecentsEvent event) {
-        dismissRecentsToLaunchTargetTaskOrHome();
-    }
-
-    public final void onBusEvent(DismissTaskViewEvent event) {
-        int taskIndex = mTaskViews.indexOf(event.taskView);
-        if (taskIndex != -1) {
-            mTasks.remove(taskIndex);
-            ((ViewGroup) event.taskView.getParent()).removeView(event.taskView);
-            mTaskViews.remove(taskIndex);
-            EventBus.getDefault().send(
-                    new TaskViewDismissedEvent(event.taskView.getTask(), event.taskView, null));
-        }
-    }
-
-    public final void onBusEvent(TaskViewDismissedEvent event) {
-        mRecentsView.announceForAccessibility(this.getString(
-            R.string.accessibility_recents_item_dismissed, event.task.title));
-        updateControlVisibility();
-
-        EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
-
-        MetricsLogger.action(this, MetricsEvent.OVERVIEW_DISMISS,
-                event.task.key.getComponent().toString());
-    }
-
-    public final void onBusEvent(DeleteTaskDataEvent event) {
-        // Remove any stored data from the loader.
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        loader.deleteTaskData(event.task, false);
-
-        // Remove the task from activity manager.
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        ssp.removeTask(event.task.key.id);
-    }
-
-    public final void onBusEvent(final DismissAllTaskViewsEvent event) {
-        // Keep track of the tasks which will have their data removed.
-        ArrayList<Task> tasks = new ArrayList<>(mTaskStack.getStackTasks());
-        mRecentsView.announceForAccessibility(this.getString(
-                R.string.accessibility_recents_all_items_dismissed));
-        mTaskStack.removeAllTasks();
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));
-        }
-        mTasks = new ArrayList<>();
-        updateModel();
-        updateViews();
-
-        MetricsLogger.action(this, MetricsEvent.OVERVIEW_DISMISS_ALL);
-    }
-
-    public final void onBusEvent(AllTaskViewsDismissedEvent event) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        if (!ssp.hasDockedTask()) {
-            dismissRecentsToHome();
-        }
-    }
-
-    public final void onBusEvent(LaunchNextTaskRequestEvent event) {
-        if (mTaskStack.getTaskCount() > 0) {
-            Task launchTask = mTaskStack.getNextLaunchTarget();
-            TaskView launchTaskView = getChildViewForTask(launchTask);
-            if (launchTaskView != null) {
-                EventBus.getDefault().send(new LaunchTaskEvent(launchTaskView,
-                        launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */));
-                MetricsLogger.action(this, MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
-                        launchTask.key.getComponent().toString());
-                return;
-            }
-        }
-        // We couldn't find a matching task view, or there are no tasks. Just hide recents back
-        // to home.
-        EventBus.getDefault().send(new HideRecentsEvent(false, true));
-    }
-
-    public final void onBusEvent(LaunchTaskEvent event) {
-        event.taskView.bringToFront();
-        startActivity(event.task.key.baseIntent);
-        // Eventually we should start blow-up animations here, but we need to make sure it's done
-        // in parallel with starting the activity so that we don't introduce unneeded latency.
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridImpl.java b/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridImpl.java
deleted file mode 100644
index 41acaa0..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridImpl.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.grid;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
-
-import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-
-public class RecentsGridImpl extends RecentsImpl {
-    public static final String RECENTS_MOSAIC_ACTIVITY =
-            "com.android.systemui.recents.grid.RecentsGridActivity";
-
-    public RecentsGridImpl(Context context) {
-        super(context);
-    }
-
-    @Override
-    protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
-            boolean isHomeStackVisible, boolean animate, int growTarget) {
-        Intent intent = new Intent();
-        intent.setClassName(RECENTS_PACKAGE, RECENTS_MOSAIC_ACTIVITY);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-
-        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-        EventBus.getDefault().send(new RecentsActivityStartingEvent());
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithm.java
deleted file mode 100644
index 648f2f0..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithm.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.grid;
-
-import android.graphics.Rect;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-class TaskGridLayoutAlgorithm {
-
-    public enum VerticalGravity {
-        START, END, CENTER
-    }
-
-    public static final List<Integer> ZERO_MARGIN = new ArrayList<>();
-    static {
-        Integer[] zero = {0, 0, 0, 0};
-        ZERO_MARGIN.addAll(Arrays.asList(zero));
-    }
-    private static final String TAG = "TaskGridLayoutAlgorithm";
-
-    /**
-     * Calculates the adequate rectangles for the specified number of tasks to be layed out on
-     * the screen.
-     * @param count The number of task views to layout.
-     * @param containerWidth The width of the whole area containing those tasks.
-     * @param containerHeight The height of the whole area containing those tasks.
-     * @param screenRatio The ratio of the device's screen, so that tasks have the same aspect
-     *         ratio (ignoring the title bar).
-     * @param padding The amount of padding, in pixels, in between task views.
-     * @param margins The amount of space to be left blank around the area on the left, top, right
-     *         and bottom.
-     * @param titleBarHeight The height, in pixels, of the task views title bar.
-     * @return A list of rectangles to be used for layout.
-     */
-    static ArrayList<Rect> getRectsForTaskCount(int count, int containerWidth, int containerHeight,
-            float screenRatio, int padding, List<Integer> margins, int titleBarHeight) {
-        return getRectsForTaskCount(count, containerWidth, containerHeight,  screenRatio, padding,
-                margins, titleBarHeight, null, VerticalGravity.CENTER);
-    }
-
-    private static ArrayList<Rect> getRectsForTaskCount(int count, int containerWidth,
-            int containerHeight, float screenRatio, int padding, List<Integer> margins,
-            int titleBarHeight, Rect preCalculatedTile, VerticalGravity gravity) {
-        ArrayList<Rect> rects = new ArrayList<>(count);
-        boolean landscape = (containerWidth > containerHeight);
-        containerWidth -= margins.get(0) + margins.get(2);
-        containerHeight -= margins.get(1) + margins.get(3);
-
-        // We support at most 9 tasks in this layout.
-        count = Math.min(count, RecentsGridActivity.MAX_VISIBLE_TASKS);
-
-        if (count == 0) {
-            return rects;
-        }
-        if (count <= 3) {
-            // Base case: single line.
-            int taskWidth, taskHeight;
-            if (preCalculatedTile != null) {
-                taskWidth = preCalculatedTile.width();
-                taskHeight = preCalculatedTile.height();
-            } else {
-                // Divide available width in equal parts.
-                int maxTaskWidth = (containerWidth - (count - 1) * padding) / count;
-                int maxTaskHeight = containerHeight;
-                if (maxTaskHeight >= maxTaskWidth / screenRatio + titleBarHeight) {
-                    // Width bound.
-                    taskWidth = maxTaskWidth;
-                    taskHeight = (int) (maxTaskWidth / screenRatio + titleBarHeight);
-                } else {
-                    // Height bound.
-                    taskHeight = maxTaskHeight;
-                    taskWidth = (int) ((taskHeight - titleBarHeight) * screenRatio);
-                }
-            }
-            int emptySpaceX = containerWidth - (count * taskWidth) - (count - 1) * padding;
-            int emptySpaceY = containerHeight - taskHeight;
-            for (int i = 0; i < count; i++) {
-                int left = emptySpaceX / 2 + i * taskWidth + i * padding;
-                int top;
-                switch (gravity) {
-                    case CENTER:
-                        top = emptySpaceY / 2;
-                        break;
-                    case END:
-                        top = emptySpaceY;
-                        break;
-                    case START:
-                    default:
-                        top = 0;
-                        break;
-                }
-                Rect rect = new Rect(left, top, left + taskWidth, top + taskHeight);
-                rect.offset(margins.get(0), margins.get(1));
-                rects.add(rect);
-            }
-        } else if (count < 7) {
-            // Two lines.
-            int lineHeight = (containerHeight - padding) / 2;
-            int lineTaskCount = (int) Math.ceil((double) count / 2);
-            List<Rect> rectsA = getRectsForTaskCount(lineTaskCount, containerWidth, lineHeight,
-                    screenRatio, padding, ZERO_MARGIN, titleBarHeight, null, VerticalGravity.END);
-            List<Rect> rectsB = getRectsForTaskCount(count - lineTaskCount, containerWidth,
-                    lineHeight, screenRatio, padding, ZERO_MARGIN, titleBarHeight, rectsA.get(0),
-                    VerticalGravity.START);
-            for (int i = 0; i < rectsA.size(); i++) {
-                rectsA.get(i).offset(margins.get(0), margins.get(1));
-            }
-            for (int i = 0; i < rectsB.size(); i++) {
-                rectsB.get(i).offset(margins.get(0), margins.get(1) + lineHeight + padding);
-            }
-            rects.addAll(rectsA);
-            rects.addAll(rectsB);
-        } else {
-            // Three lines.
-            int lineHeight = (containerHeight - 2 * padding) / 3;
-            int lineTaskCount = (int) Math.ceil((double) count / 3);
-            List<Rect> rectsA = getRectsForTaskCount(lineTaskCount, containerWidth, lineHeight,
-                    screenRatio, padding, ZERO_MARGIN, titleBarHeight, null, VerticalGravity.END);
-            List<Rect> rectsB = getRectsForTaskCount(lineTaskCount, containerWidth, lineHeight,
-                    screenRatio,  padding, ZERO_MARGIN, titleBarHeight, rectsA.get(0),
-                    VerticalGravity.END);
-            List<Rect> rectsC = getRectsForTaskCount(count - (2 * lineTaskCount), containerWidth,
-                    lineHeight, screenRatio, padding, ZERO_MARGIN, titleBarHeight, rectsA.get(0),
-                    VerticalGravity.START);
-            for (int i = 0; i < rectsA.size(); i++) {
-                rectsA.get(i).offset(margins.get(0), margins.get(1));
-            }
-            for (int i = 0; i < rectsB.size(); i++) {
-                rectsB.get(i).offset(margins.get(0), margins.get(1) + lineHeight + padding);
-            }
-            for (int i = 0; i < rectsC.size(); i++) {
-                rectsC.get(i).offset(margins.get(0), margins.get(1) + 2 * (lineHeight + padding));
-            }
-            rects.addAll(rectsA);
-            rects.addAll(rectsB);
-            rects.addAll(rectsC);
-        }
-        return rects;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 3c97310..fddf165 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -370,6 +370,7 @@
                 R.dimen.recents_layout_initial_top_offset_tablet,
                 R.dimen.recents_layout_initial_top_offset_tablet,
                 R.dimen.recents_layout_initial_top_offset_tablet,
+                R.dimen.recents_layout_initial_top_offset_tablet,
                 R.dimen.recents_layout_initial_top_offset_tablet);
         mBaseInitialBottomOffset = getDimensionForDevice(context,
                 R.dimen.recents_layout_initial_bottom_offset_phone_port,
@@ -377,6 +378,7 @@
                 R.dimen.recents_layout_initial_bottom_offset_tablet,
                 R.dimen.recents_layout_initial_bottom_offset_tablet,
                 R.dimen.recents_layout_initial_bottom_offset_tablet,
+                R.dimen.recents_layout_initial_bottom_offset_tablet,
                 R.dimen.recents_layout_initial_bottom_offset_tablet);
         mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context);
         mTaskGridLayoutAlgorithm.reloadOnConfigurationChange(context);
@@ -384,11 +386,13 @@
         mBaseTopMargin = getDimensionForDevice(context,
                 R.dimen.recents_layout_top_margin_phone,
                 R.dimen.recents_layout_top_margin_tablet,
-                R.dimen.recents_layout_top_margin_tablet_xlarge);
+                R.dimen.recents_layout_top_margin_tablet_xlarge,
+                R.dimen.recents_layout_top_margin_tablet);
         mBaseSideMargin = getDimensionForDevice(context,
                 R.dimen.recents_layout_side_margin_phone,
                 R.dimen.recents_layout_side_margin_tablet,
-                R.dimen.recents_layout_side_margin_tablet_xlarge);
+                R.dimen.recents_layout_side_margin_tablet_xlarge,
+                R.dimen.recents_layout_side_margin_tablet);
         mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
         mFreeformStackGap =
                 res.getDimensionPixelSize(R.dimen.recents_freeform_layout_bottom_margin);
@@ -408,6 +412,7 @@
     public boolean setSystemInsets(Rect systemInsets) {
         boolean changed = !mSystemInsets.equals(systemInsets);
         mSystemInsets.set(systemInsets);
+        mTaskGridLayoutAlgorithm.setSystemInsets(systemInsets);
         return changed;
     }
 
@@ -876,10 +881,10 @@
      */
     public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
             TaskViewTransform transformOut, TaskViewTransform frontTransform,
-            Rect windowOverrideRect) {
+            Rect windowOverrideRect, boolean useGridLayout) {
         TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
                 transformOut, frontTransform, true /* forceUpdate */,
-                false /* ignoreTaskOverrides */, false /* useGridLayout */);
+                false /* ignoreTaskOverrides */, useGridLayout);
         return transformToScreenCoordinates(transform, windowOverrideRect);
     }
 
@@ -1100,9 +1105,9 @@
      * Retrieves resources that are constant regardless of the current configuration of the device.
      */
     public static int getDimensionForDevice(Context ctx, int phoneResId,
-            int tabletResId, int xlargeTabletResId) {
+            int tabletResId, int xlargeTabletResId, int gridLayoutResId) {
         return getDimensionForDevice(ctx, phoneResId, phoneResId, tabletResId, tabletResId,
-                xlargeTabletResId, xlargeTabletResId);
+                xlargeTabletResId, xlargeTabletResId, gridLayoutResId);
     }
 
     /**
@@ -1110,12 +1115,14 @@
      */
     public static int getDimensionForDevice(Context ctx, int phonePortResId, int phoneLandResId,
             int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId,
-            int xlargeTabletLandResId) {
+            int xlargeTabletLandResId, int gridLayoutResId) {
         RecentsConfiguration config = Recents.getConfiguration();
         Resources res = ctx.getResources();
         boolean isLandscape = Utilities.getAppConfiguration(ctx).orientation ==
                 Configuration.ORIENTATION_LANDSCAPE;
-        if (config.isXLargeScreen) {
+        if (config.isGridEnabled) {
+            return res.getDimensionPixelSize(gridLayoutResId);
+        } else if (config.isXLargeScreen) {
             return res.getDimensionPixelSize(isLandscape
                     ? xlargeTabletLandResId
                     : xlargeTabletPortResId);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 71f559b..33fa3b0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -168,7 +168,7 @@
     /** Touch preprocessing for handling below */
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         // Pass through to swipe helper if we are swiping
-        mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
+        mInterceptedBySwipeHelper = isSwipingEnabled() && mSwipeHelper.onInterceptTouchEvent(ev);
         if (mInterceptedBySwipeHelper) {
             return true;
         }
@@ -680,4 +680,11 @@
     public float getScaledDismissSize() {
         return 1.5f * Math.max(mSv.getWidth(), mSv.getHeight());
     }
+
+    /**
+     * Returns whether swiping is enabled.
+     */
+    private boolean isSwipingEnabled() {
+        return !mSv.useGridLayout();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 9b30515..2f4ad6a4d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -148,7 +148,7 @@
     private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
 
     @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
-    TaskViewThumbnail mThumbnailView;
+    protected TaskViewThumbnail mThumbnailView;
     @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
     TaskViewHeader mHeaderView;
     private View mActionButtonView;
@@ -239,7 +239,7 @@
     /**
      * Update the task view when the configuration changes.
      */
-    void onConfigurationChanged() {
+    protected void onConfigurationChanged() {
         mHeaderView.onConfigurationChanged();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 6be1691..c0cc83f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -290,14 +290,16 @@
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height_tablet_land,
                 R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height_tablet_land);
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_grid_task_view_header_height);
         int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
                 R.dimen.recents_task_view_header_button_padding,
                 R.dimen.recents_task_view_header_button_padding,
                 R.dimen.recents_task_view_header_button_padding,
                 R.dimen.recents_task_view_header_button_padding_tablet_land,
                 R.dimen.recents_task_view_header_button_padding,
-                R.dimen.recents_task_view_header_button_padding_tablet_land);
+                R.dimen.recents_task_view_header_button_padding_tablet_land,
+                R.dimen.recents_grid_task_view_header_button_padding);
         if (headerBarHeight != mHeaderBarHeight || headerButtonPadding != mHeaderButtonPadding) {
             mHeaderBarHeight = headerBarHeight;
             mHeaderButtonPadding = headerButtonPadding;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 780cbca..d109807 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -60,6 +60,8 @@
     @ViewDebug.ExportedProperty(category="recents")
     private float mThumbnailScale;
     private float mFullscreenThumbnailScale;
+    private boolean mSizeToFit = false;
+    private boolean mOverlayHeaderOnThumbnailActionBar = true;
     private ActivityManager.TaskThumbnailInfo mThumbnailInfo;
 
     private int mCornerRadius;
@@ -136,13 +138,14 @@
         int thumbnailHeight = Math.min(viewHeight,
                 (int) (mThumbnailRect.height() * mThumbnailScale));
 
-        if (mTask.isLocked) {
+        if (mTask != null && mTask.isLocked) {
             canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
                     mLockedPaint);
         } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
-            int topOffset = mTaskBar != null
-                    ? mTaskBar.getHeight() - mCornerRadius
-                    : 0;
+            int topOffset = 0;
+            if (mTaskBar != null && mOverlayHeaderOnThumbnailActionBar) {
+                topOffset = mTaskBar.getHeight() - mCornerRadius;
+            }
 
             // Draw the background, there will be some small overdraw with the thumbnail
             if (thumbnailWidth < viewWidth) {
@@ -238,7 +241,7 @@
                 // If we haven't measured or the thumbnail is invalid, skip the thumbnail drawing
                 // and only draw the background color
                 mThumbnailScale = 0f;
-            } else if (isStackTask) {
+            } else if (isStackTask && !mSizeToFit) {
                 float invThumbnailScale = 1f / mFullscreenThumbnailScale;
                 if (mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) {
                     if (mThumbnailInfo.screenOrientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -270,6 +273,19 @@
         }
     }
 
+    /** Sets whether the thumbnail should be resized to fit the task view in all orientations. */
+    public void setSizeToFit(boolean flag) {
+        mSizeToFit = flag;
+    }
+
+    /**
+     * Sets whether the header should overlap (and hide) the action bar in the thumbnail, or
+     * be stacked just above it.
+     */
+    public void setOverlayHeaderOnThumbnailActionBar(boolean flag) {
+        mOverlayHeaderOnThumbnailActionBar = flag;
+    }
+
     /** Updates the clip rect based on the given task bar. */
     void updateClipToTaskBar(View taskBar) {
         mTaskBar = taskBar;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
index da14e2b..6300400 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
@@ -23,6 +23,10 @@
 import com.android.systemui.recents.views.TaskView;
 
 public class GridTaskView extends TaskView {
+
+    /** The height, in pixels, of the header view. */
+    private int mHeaderHeight;
+
     public GridTaskView(Context context) {
         this(context, null);
     }
@@ -37,6 +41,18 @@
 
     public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        mHeaderHeight = context.getResources().getDimensionPixelSize(
+                R.dimen.recents_grid_task_view_header_height);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        // Show the full thumbnail and don't overlap with the header.
+        mThumbnailView.setSizeToFit(true);
+        mThumbnailView.setOverlayHeaderOnThumbnailActionBar(false);
+        mThumbnailView.updateThumbnailScale();
+        mThumbnailView.setTranslationY(mHeaderHeight);
     }
 
     @Override
@@ -44,4 +60,12 @@
         return new AnimateableGridViewBounds(this, mContext.getResources().getDimensionPixelSize(
             R.dimen.recents_task_view_shadow_rounded_corners_radius));
     }
+
+    @Override
+    protected void onConfigurationChanged() {
+        super.onConfigurationChanged();
+        mHeaderHeight = mContext.getResources().getDimensionPixelSize(
+                R.dimen.recents_grid_task_view_header_height);
+        mThumbnailView.setTranslationY(mHeaderHeight);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
index 8d1bec8..82ccae6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -18,7 +18,10 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.Rect;
+import android.view.WindowManager;
+
 import com.android.systemui.R;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
@@ -26,15 +29,29 @@
 
 public class TaskGridLayoutAlgorithm  {
 
+    private final String TAG = "TaskGridLayoutAlgorithm";
+    private final int MAX_LAYOUT_TASK_COUNT = 8;
+
+    /** The horizontal padding around the whole recents view. */
     private int mPaddingLeftRight;
+    /** The vertical padding around the whole recents view. */
     private int mPaddingTopBottom;
+    /** The padding between task views. */
     private int mPaddingTaskView;
 
     private Rect mDisplayRect;
     private Rect mWindowRect;
+    private Point mScreenSize = new Point();
 
     private Rect mTaskGridRect;
 
+    /** The height, in pixels, of each task view's title bar. */
+    private int mTitleBarHeight;
+
+    /** The aspect ratio of each task thumbnail, without the title bar. */
+    private float mAppAspectRatio;
+    private Rect mSystemInsets = new Rect();
+
     public TaskGridLayoutAlgorithm(Context context) {
         reloadOnConfigurationChange(context);
     }
@@ -46,38 +63,71 @@
         mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
 
         mTaskGridRect = new Rect();
+        mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_header_height);
+
+        WindowManager windowManager = (WindowManager) context
+                .getSystemService(Context.WINDOW_SERVICE);
+        windowManager.getDefaultDisplay().getRealSize(mScreenSize);
+
+        updateAppAspectRatio();
     }
 
-    public TaskViewTransform getTransform(int taskIndex, int taskAmount,
+    public TaskViewTransform getTransform(int taskIndex, int taskCount,
         TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
 
-        int taskPerLine = taskAmount < 2 ? 1 : (
-            taskAmount < 5 ? 2 : 3);
+        int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
 
-        int taskWidth = (mDisplayRect.width() - mPaddingLeftRight * 2
-            - mPaddingTaskView * (taskPerLine - 1)) / taskPerLine;
-        int taskHeight = taskWidth * mDisplayRect.height() / mDisplayRect.width();
+        // We also need to invert the index in order to display the most recent tasks first.
+        int taskLayoutIndex = taskCount - taskIndex - 1;
+
+        int tasksPerLine = layoutTaskCount < 2 ? 1 : (
+                layoutTaskCount < 5 ? 2 : (
+                        layoutTaskCount < 7 ? 3 : 4));
+        int lines = layoutTaskCount < 3 ? 1 : 2;
+
+        int taskWidth, taskHeight;
+        int maxTaskWidth = (mDisplayRect.width() - 2 * mPaddingLeftRight
+                - (tasksPerLine - 1) * mPaddingTaskView) / tasksPerLine;
+        int maxTaskHeight = (mDisplayRect.height() - 2 * mPaddingTopBottom
+                - (lines - 1) * mPaddingTaskView) / lines;
+
+        if (maxTaskHeight >= maxTaskWidth / mAppAspectRatio + mTitleBarHeight) {
+            // Width bound.
+            taskWidth = maxTaskWidth;
+            taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight);
+        } else {
+            // Height bound.
+            taskHeight = maxTaskHeight;
+            taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio);
+        }
+        int emptySpaceX = mDisplayRect.width() - 2 * mPaddingLeftRight
+                - (tasksPerLine * taskWidth) - (tasksPerLine - 1) * mPaddingTaskView;
+        int emptySpaceY = mDisplayRect.height() - 2 * mPaddingTopBottom
+                - (lines * taskHeight) - (lines - 1) * mPaddingTaskView;
+
         mTaskGridRect.set(0, 0, taskWidth, taskHeight);
 
-        int xIndex = taskIndex % taskPerLine;
-        int yIndex = taskIndex / taskPerLine;
-        int x = mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
-        int y = mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
+        int xIndex = taskLayoutIndex % tasksPerLine;
+        int yIndex = taskLayoutIndex / tasksPerLine;
+        int x = emptySpaceX / 2 + mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
+        int y = emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
         float z = stackLayout.mMaxTranslationZ;
 
         float dimAlpha = 0f;
         float viewOutlineAlpha = 0f;
+        boolean isTaskViewVisible = (taskLayoutIndex < MAX_LAYOUT_TASK_COUNT);
 
         // Fill out the transform
         transformOut.scale = 1f;
-        transformOut.alpha = 1f;
+        transformOut.alpha = isTaskViewVisible ? 1f : 0f;
         transformOut.translationZ = z;
         transformOut.dimAlpha = dimAlpha;
         transformOut.viewOutlineAlpha = viewOutlineAlpha;
         transformOut.rect.set(mTaskGridRect);
         transformOut.rect.offset(x, y);
         Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
-        transformOut.visible = true;
+        // We only show the 8 most recent tasks.
+        transformOut.visible = isTaskViewVisible;
         return transformOut;
     }
 
@@ -85,4 +135,15 @@
         mDisplayRect = displayRect;
         mWindowRect = windowRect;
     }
-}
\ No newline at end of file
+
+    public void setSystemInsets(Rect systemInsets) {
+        mSystemInsets = systemInsets;
+        updateAppAspectRatio();
+    }
+
+    private void updateAppAspectRatio() {
+        int usableWidth = mScreenSize.x - mSystemInsets.left - mSystemInsets.right;
+        int usableHeight = mScreenSize.y - mSystemInsets.top - mSystemInsets.bottom;
+        mAppAspectRatio = (float) usableWidth / (float) usableHeight;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 567ab3b..227ebdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -165,10 +165,6 @@
         mHeader = view;
     }
 
-    public PhoneStatusBar getPhoneStatusBar() {
-        return mStatusBar;
-    }
-
     public void destroy() {
         mHandlerThread.quitSafely();
         mTiles.values().forEach(tile -> tile.destroy());
diff --git a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java b/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
index f420921..5cfe677 100644
--- a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
@@ -81,11 +81,23 @@
      * @return Builder object post-modification.
      */
     public LayoutInflaterBuilder replace(@NonNull Class from, @NonNull Class to) {
+        return replace(from.getName(), to);
+    }
+
+    /**
+     * Instructs the Builder to configure the LayoutInflater such that all instances
+     * of one {@link View} will be replaced with instances of another during inflation.
+     *
+     * @param tag Instances of this tag will be replaced during inflation.
+     * @param to Instances of this class will be inflated as replacements.
+     * @return Builder object post-modification.
+     */
+    public LayoutInflaterBuilder replace(@NonNull String tag, @NonNull Class to) {
         assertIfAlreadyBuilt();
         if (mReplaceMap == null) {
             mReplaceMap = new ArrayMap<String, String>();
         }
-        mReplaceMap.put(from.getName(), to.getName());
+        mReplaceMap.put(tag, to.getName());
         return this;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 8b99d72..c3948258 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -214,6 +214,16 @@
 
     @Test
     @UiThreadTest
+    public void testSuppressingPulse_doesntCrash() {
+        mMachine.requestState(INITIALIZED);
+
+        mMachine.requestState(DOZE);
+        mMachine.requestState(DOZE_REQUEST_PULSE);
+        mMachine.requestState(DOZE_PULSE_DONE);
+    }
+
+    @Test
+    @UiThreadTest
     public void testScreen_offInDoze() {
         mMachine.requestState(INITIALIZED);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
index 4c25c62e..8acd6ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
@@ -14,37 +14,31 @@
 
 package com.android.systemui.qs;
 
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
-import android.content.res.Resources;
+import android.os.Handler;
 import android.os.Looper;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.SpannableStringBuilder;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.util.LayoutInflaterBuilder;
+import com.android.systemui.utils.TestableImageView;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static junit.framework.Assert.assertEquals;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class QSFooterTest extends SysuiTestCase {
@@ -53,28 +47,26 @@
     private final String DEVICE_OWNER_PACKAGE = "TestDPC";
     private final String VPN_PACKAGE = "TestVPN";
 
-    private ViewGroup mRootView = mock(ViewGroup.class);
-    private TextView mFooterText = mock(TextView.class);
-    private ImageView mFooterIcon = mock(ImageView.class);
-    private ImageView mFooterIcon2 = mock(ImageView.class);
+    private ViewGroup mRootView;
+    private TextView mFooterText;
+    private TestableImageView mFooterIcon;
+    private TestableImageView mFooterIcon2;
     private QSFooter mFooter;
-    private Resources mResources;
     private SecurityController mSecurityController = mock(SecurityController.class);
 
     @Before
     public void setUp() {
-        when(mRootView.findViewById(R.id.footer_text)).thenReturn(mFooterText);
-        when(mRootView.findViewById(R.id.footer_icon)).thenReturn(mFooterIcon);
-        when(mRootView.findViewById(R.id.footer_icon2)).thenReturn(mFooterIcon2);
-        final LayoutInflater layoutInflater = mock(LayoutInflater.class);
-        when(layoutInflater.inflate(eq(R.layout.quick_settings_footer), anyObject(), anyBoolean()))
-                .thenReturn(mRootView);
-        final Context context = mock(Context.class);
-        when(context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).thenReturn(layoutInflater);
-        mResources = mContext.getResources();
-        when(context.getResources()).thenReturn(mResources);
-        mFooter = new QSFooter(null, context);
-        reset(mRootView);
+        mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
+                new LayoutInflaterBuilder(mContext)
+                        .replace("ImageView", TestableImageView.class)
+                        .build());
+        Handler h = new Handler(Looper.getMainLooper());
+        h.post(() -> mFooter = new QSFooter(null, mContext));
+        waitForIdleSync(h);
+        mRootView = (ViewGroup) mFooter.getView();
+        mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
+        mFooterIcon = (TestableImageView) mRootView.findViewById(R.id.footer_icon);
+        mFooterIcon2 = (TestableImageView) mRootView.findViewById(R.id.footer_icon2);
         mFooter.setHostEnvironment(null, mSecurityController, Looper.getMainLooper());
     }
 
@@ -86,8 +78,7 @@
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
-        verify(mRootView).setVisibility(View.GONE);
-        verifyNoMoreInteractions(mRootView);
+        assertEquals(View.GONE, mRootView.getVisibility());
     }
 
     @Test
@@ -97,10 +88,8 @@
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
-        verify(mFooterText).setText(mResources.getString(R.string.do_disclosure_generic));
-        verifyNoMoreInteractions(mFooterText);
-        verify(mRootView).setVisibility(View.VISIBLE);
-        verifyNoMoreInteractions(mRootView);
+        assertEquals(mContext.getString(R.string.do_disclosure_generic), mFooterText.getText());
+        assertEquals(View.VISIBLE, mRootView.getVisibility());
     }
 
     @Test
@@ -111,11 +100,9 @@
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
-        verify(mFooterText).setText(mResources.getString(R.string.do_disclosure_with_name,
-                MANAGING_ORGANIZATION));
-        verifyNoMoreInteractions(mFooterText);
-        verify(mRootView).setVisibility(View.VISIBLE);
-        verifyNoMoreInteractions(mRootView);
+        assertEquals(mContext.getString(R.string.do_disclosure_with_name, MANAGING_ORGANIZATION),
+                mFooterText.getText());
+        assertEquals(View.VISIBLE, mRootView.getVisibility());
     }
 
     @Test
@@ -126,9 +113,9 @@
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
-        verify(mFooterIcon).setVisibility(View.VISIBLE);
-        verify(mFooterIcon).setImageResource(R.drawable.ic_qs_network_logging);
-        verify(mFooterIcon2).setVisibility(View.INVISIBLE);
+        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(R.drawable.ic_qs_network_logging, mFooterIcon.getLastImageResource());
+        assertEquals(View.INVISIBLE, mFooterIcon2.getVisibility());
     }
 
     @Test
@@ -140,9 +127,10 @@
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
-        verify(mFooterIcon).setVisibility(View.VISIBLE);
-        verify(mFooterIcon, never()).setImageResource(anyInt());
-        verify(mFooterIcon2).setVisibility(View.INVISIBLE);
+        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        // -1 == never set.
+        assertEquals(-1, mFooterIcon.getLastImageResource());
+        assertEquals(View.INVISIBLE, mFooterIcon2.getVisibility());
     }
 
     @Test
@@ -154,10 +142,11 @@
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
-        verify(mFooterIcon).setVisibility(View.VISIBLE);
-        verify(mFooterIcon, never()).setImageResource(anyInt());
-        verify(mFooterIcon2).setVisibility(View.VISIBLE);
-        verify(mFooterIcon2, never()).setImageResource(anyInt());
+        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.VISIBLE, mFooterIcon2.getVisibility());
+        // -1 == never set.
+        assertEquals(-1, mFooterIcon.getLastImageResource());
+        assertEquals(-1, mFooterIcon2.getLastImageResource());
     }
 
     @Test
@@ -211,20 +200,15 @@
     private CharSequence getExpectedMessage(boolean hasDeviceOwnerOrganization, boolean hasVPN) {
         final SpannableStringBuilder message = new SpannableStringBuilder();
         message.append(hasDeviceOwnerOrganization ?
-                mResources.getString(R.string.monitoring_description_do_header_with_name,
+                mContext.getString(R.string.monitoring_description_do_header_with_name,
                         MANAGING_ORGANIZATION, DEVICE_OWNER_PACKAGE) :
-                mResources.getString(R.string.monitoring_description_do_header_generic,
+                mContext.getString(R.string.monitoring_description_do_header_generic,
                         DEVICE_OWNER_PACKAGE));
         message.append("\n\n");
-        message.append(mResources.getString(R.string.monitoring_description_do_body));
-        if (hasVPN) {
-            message.append("\n\n");
-            message.append(mResources.getString(R.string.monitoring_description_do_body_vpn,
-                    VPN_PACKAGE));
-        }
-        message.append(mResources.getString(
+        message.append(mContext.getString(R.string.monitoring_description_do_body));
+        message.append(mContext.getString(
                 R.string.monitoring_description_do_learn_more_separator));
-        message.append(mResources.getString(R.string.monitoring_description_do_learn_more),
+        message.append(mContext.getString(R.string.monitoring_description_do_learn_more),
                 mFooter.new EnterprisePrivacySpan(), 0);
         return message;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 350a95f..c0d5bbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -63,7 +63,7 @@
         when(userSwitcher.getKeyguardMonitor()).thenReturn(keyguardMonitor);
         when(userSwitcher.getUsers()).thenReturn(new ArrayList<>());
         QSTileHost host = new QSTileHost(mContext,
-                mock(PhoneStatusBar.class),
+                null,
                 getLeakChecker(BluetoothController.class),
                 getLeakChecker(LocationController.class),
                 getLeakChecker(RotationLockController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 782a489..66ec7dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -15,8 +15,10 @@
  */
 package com.android.systemui.qs.external;
 
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
@@ -26,9 +28,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
-import android.content.ServiceConnection;
 import android.content.pm.PackageInfo;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
@@ -50,15 +50,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class TileLifecycleManagerTest extends SysuiTestCase {
     private static final int TEST_FAIL_TIMEOUT = 5000;
 
-    private final Context mMockContext = Mockito.mock(Context.class);
     private final PackageManagerAdapter mMockPackageManagerAdapter =
             Mockito.mock(PackageManagerAdapter.class);
     private final IQSTileService.Stub mMockTileService = Mockito.mock(IQSTileService.Stub.class);
@@ -77,19 +74,7 @@
         // Stub.asInterface will just return itself.
         when(mMockTileService.queryLocalInterface(anyString())).thenReturn(mMockTileService);
 
-        // Default behavior for bind is success and connects as mMockTileService.
-        when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any()))
-                .thenAnswer(
-                        new Answer<Boolean>() {
-                            @Override
-                            public Boolean answer(InvocationOnMock invocation) {
-                                ServiceConnection connection =
-                                        (ServiceConnection) invocation.getArguments()[1];
-                                connection.onServiceConnected(
-                                        mTileServiceComponentName, mMockTileService);
-                                return true;
-                            }
-                        });
+        mContext.addMockService(mTileServiceComponentName, mMockTileService);
 
 
         mTileServiceIntent = new Intent().setComponent(mTileServiceComponentName);
@@ -97,7 +82,7 @@
         mThread = new HandlerThread("TestThread");
         mThread.start();
         mHandler = new Handler(mThread.getLooper());
-        mStateManager = new TileLifecycleManager(mHandler, mMockContext,
+        mStateManager = new TileLifecycleManager(mHandler, mContext,
                 Mockito.mock(IQSService.class), new Tile(),
                 mTileServiceIntent,
                 mUser,
@@ -126,11 +111,7 @@
     }
 
     private void verifyBind(int times) {
-        verify(mMockContext, times(times)).bindServiceAsUser(
-                mTileServiceIntent,
-                mStateManager,
-                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
-                mUser);
+        assertEquals(times > 0, mContext.isBound(mTileServiceComponentName));
     }
 
     @Test
@@ -143,7 +124,7 @@
     public void testUnbind() {
         mStateManager.setBindService(true);
         mStateManager.setBindService(false);
-        verify(mMockContext).unbindService(mStateManager);
+        assertFalse(mContext.isBound(mTileServiceComponentName));
     }
 
     @Test
@@ -203,7 +184,7 @@
 
         verifyBind(1);
         mStateManager.setBindService(false);
-        verify(mMockContext).unbindService(mStateManager);
+        assertFalse(mContext.isBound(mTileServiceComponentName));
         verify(mMockTileService, never()).onStartListening();
     }
 
@@ -217,7 +198,7 @@
 
         verifyBind(1);
         mStateManager.setBindService(false);
-        verify(mMockContext).unbindService(mStateManager);
+        assertFalse(mContext.isBound(mTileServiceComponentName));
         verify(mMockTileService, never()).onClick(null);
     }
 
@@ -233,7 +214,7 @@
         // Package is re-enabled.
         setPackageEnabled(true);
         mStateManager.onReceive(
-                mMockContext,
+                mContext,
                 new Intent(
                         Intent.ACTION_PACKAGE_CHANGED,
                         Uri.fromParts(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithmTest.java
deleted file mode 100644
index a9f811b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithmTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.systemui.recents.grid;
-
-import android.graphics.Rect;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.systemui.SysuiTestCase;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import org.junit.Test;
-
-@SmallTest
-public class TaskGridLayoutAlgorithmTest extends SysuiTestCase {
-
-    private static final List<Integer> ZERO_MARGIN = TaskGridLayoutAlgorithm.ZERO_MARGIN;
-
-    @Test
-    public void testOneTile() {
-        List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
-                1, 1000, 1000, 1 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
-        assertEquals(1, rects.size());
-        Rect singleRect = rects.get(0);
-        assertEquals(1000, singleRect.width());
-    }
-
-    @Test
-    public void testTwoTilesLandscape() {
-        List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
-                2, 1200, 500, 1.2f /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
-        assertEquals(2, rects.size());
-        for (Rect rect : rects) {
-            assertEquals(600, rect.width());
-            assertEquals(499, rect.height());
-        }
-    }
-
-    @Test
-    public void testTwoTilesLandscapeWithPadding() {
-        List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
-                2, 1200, 500, 1.19f /* screenRatio */, 10 /* padding */, ZERO_MARGIN, 0);
-        assertEquals(2, rects.size());
-        Rect rectA = rects.get(0);
-        Rect rectB = rects.get(1);
-        assertEquals(595, rectA.width());
-        assertEquals(595, rectB.width());
-        assertEquals(605, rectB.left);
-    }
-
-    @Test
-    public void testTwoTilesPortrait() {
-        List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
-                2, 500, 1200, 1 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
-        assertEquals(2, rects.size());
-        for (Rect rect : rects) {
-            assertEquals(250, rect.width());
-            assertEquals(250, rect.height());
-        }
-    }
-
-    @Test
-    public void testThreeTiles() {
-        List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
-                3, 1200, 500, 2 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
-        assertEquals(3, rects.size());
-        for (Rect rect : rects) {
-            assertEquals(400, rect.width());
-            assertEquals(200, rect.height());
-        }
-    }
-
-    @Test
-    public void testFourTiles() {
-        List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
-                4, 1200, 500, 2.4f /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
-        assertEquals(4, rects.size());
-        for (Rect rect : rects) {
-            assertEquals(600, rect.width());
-            assertEquals(249, rect.height());
-        }
-        Rect rectD = rects.get(3);
-        assertEquals(600, rectD.left);
-        assertEquals(250, rectD.top);
-    }
-
-    @Test
-    public void testNineTiles() {
-        List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
-                9, 1200, 600, 2 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
-        assertEquals(9, rects.size());
-        for (Rect rect : rects) {
-            assertEquals(400, rect.width());
-            assertEquals(200, rect.height());
-        }
-        Rect rectE = rects.get(4);
-        assertEquals(400, rectE.left);
-        assertEquals(200, rectE.top);
-        Rect rectI = rects.get(8);
-        assertEquals(800, rectI.left);
-        assertEquals(400, rectI.top);
-    }}
-
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 639c8da..7335af3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -16,15 +16,18 @@
 
 package com.android.systemui.statusbar;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.Looper;
 import android.support.test.runner.AndroidJUnit4;
-import android.telephony.SubscriptionManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 import android.view.ViewGroup;
@@ -37,22 +40,15 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KeyguardIndicationControllerTest extends SysuiTestCase {
 
     private final String ORGANIZATION_NAME = "organization";
-    private final String DISCLOSURE_WITH_ORGANIZATION_NAME = "managed by organization";
 
-    private Context mContext = mock(Context.class);
+    private String mDisclosureWithOrganization;
+
     private DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
     private ViewGroup mIndicationArea = mock(ViewGroup.class);
     private KeyguardIndicationTextView mDisclosure = mock(KeyguardIndicationTextView.class);
@@ -61,19 +57,11 @@
 
     @Before
     public void setUp() throws Exception {
-        final Resources resources = mock(Resources.class);
-        when(mContext.getResources()).thenReturn(resources);
-        when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
-        when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
-        when(mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)).thenReturn(
-                mock(SubscriptionManager.class));
-        when(mContext.getSystemService(Context.TRUST_SERVICE)).thenReturn(
-                mock(TrustManager.class));
-        when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
-                mDevicePolicyManager);
-
-        when(resources.getString(R.string.do_disclosure_with_name, ORGANIZATION_NAME))
-                .thenReturn(DISCLOSURE_WITH_ORGANIZATION_NAME);
+        mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
+        mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class));
+        mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class));
+        mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name,
+                ORGANIZATION_NAME);
 
         when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure))
                 .thenReturn(mDisclosure);
@@ -113,7 +101,7 @@
         createController();
 
         verify(mDisclosure).setVisibility(View.VISIBLE);
-        verify(mDisclosure).switchIndication(DISCLOSURE_WITH_ORGANIZATION_NAME);
+        verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
         verifyNoMoreInteractions(mDisclosure);
     }
 
@@ -140,7 +128,7 @@
         monitor.onKeyguardVisibilityChanged(true);
 
         verify(mDisclosure).setVisibility(View.VISIBLE);
-        verify(mDisclosure).switchIndication(DISCLOSURE_WITH_ORGANIZATION_NAME);
+        verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
         verifyNoMoreInteractions(mDisclosure);
         reset(mDisclosure);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 9a697ee..87c4c66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -16,26 +16,24 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
-import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
-import android.os.UserManager;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -45,16 +43,9 @@
 
     @Before
     public void setUp() throws Exception {
-        final Context context = mock(Context.class);
-        when(context.getSystemService(Context.DEVICE_POLICY_SERVICE))
-                .thenReturn(mDevicePolicyManager);
-        when(context.getSystemService(Context.CONNECTIVITY_SERVICE))
-                .thenReturn(mock(ConnectivityManager.class));
-        final UserManager userManager = mock(UserManager.class);
-        when(userManager.getUserInfo(anyInt())).thenReturn(mock(UserInfo.class));
-        when(context.getSystemService(Context.USER_SERVICE))
-                .thenReturn(userManager);
-        mSecurityController = new SecurityControllerImpl(context);
+        mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
+        mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mock(ConnectivityManager.class));
+        mSecurityController = new SecurityControllerImpl(mContext);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
index bf73416..a952806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
@@ -16,15 +16,19 @@
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks;
+import android.content.ComponentName;
 import android.content.ContentProviderClient;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.res.Resources;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.ArrayMap;
 
 import com.android.systemui.utils.leaks.Tracker;
 import com.android.systemui.SysuiTestCase;
@@ -34,6 +38,10 @@
     private final FakeContentResolver mFakeContentResolver;
     private final FakeSettingsProvider mSettingsProvider;
 
+    private ArrayMap<String, Object> mMockSystemServices;
+    private ArrayMap<ComponentName, IBinder> mMockServices;
+    private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
+
     private Tracker mReceiver;
     private Tracker mService;
     private Tracker mComponent;
@@ -51,6 +59,33 @@
         mComponent = test.getTracker("component");
     }
 
+    @Override
+    public Resources getResources() {
+        return super.getResources();
+    }
+
+    public void addMockSystemService(String name, Object service) {
+        mMockSystemServices = lazyInit(mMockSystemServices);
+        mMockSystemServices.put(name, service);
+    }
+
+    public void addMockService(ComponentName component, IBinder service) {
+        mMockServices = lazyInit(mMockServices);
+        mMockServices.put(component, service);
+    }
+
+    private <T, V> ArrayMap<T, V> lazyInit(ArrayMap<T, V> services) {
+        return services != null ? services : new ArrayMap<T, V>();
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        if (mMockSystemServices != null && mMockSystemServices.containsKey(name)) {
+            return mMockSystemServices.get(name);
+        }
+        return super.getSystemService(name);
+    }
+
     public FakeSettingsProvider getSettingsProvider() {
         return mSettingsProvider;
     }
@@ -96,6 +131,7 @@
     @Override
     public boolean bindService(Intent service, ServiceConnection conn, int flags) {
         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
+        if (checkMocks(service.getComponent(), conn)) return true;
         return super.bindService(service, conn, flags);
     }
 
@@ -103,6 +139,7 @@
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
             Handler handler, UserHandle user) {
         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
+        if (checkMocks(service.getComponent(), conn)) return true;
         return super.bindServiceAsUser(service, conn, flags, handler, user);
     }
 
@@ -110,15 +147,35 @@
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
             UserHandle user) {
         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
+        if (checkMocks(service.getComponent(), conn)) return true;
         return super.bindServiceAsUser(service, conn, flags, user);
     }
 
+    private boolean checkMocks(ComponentName component, ServiceConnection conn) {
+        if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
+            mActiveServices = lazyInit(mActiveServices);
+            mActiveServices.put(conn, component);
+            conn.onServiceConnected(component, mMockServices.get(component));
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public void unbindService(ServiceConnection conn) {
         if (mService != null) mService.getLeakInfo(conn).clearAllocations();
+        if (mActiveServices != null && mActiveServices.containsKey(conn)) {
+            conn.onServiceDisconnected(mActiveServices.get(conn));
+            mActiveServices.remove(conn);
+            return;
+        }
         super.unbindService(conn);
     }
 
+    public boolean isBound(ComponentName component) {
+        return mActiveServices != null && mActiveServices.containsValue(component);
+    }
+
     @Override
     public void registerComponentCallbacks(ComponentCallbacks callback) {
         if (mComponent != null) mComponent.getLeakInfo(callback).addAllocation(new Throwable());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java
new file mode 100644
index 0000000..b131460
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils;
+
+import android.annotation.DrawableRes;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+public class TestableImageView extends ImageView {
+
+    private int mLastResId = -1;
+
+    public TestableImageView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void setImageResource(@DrawableRes int resId) {
+        mLastResId = resId;
+        super.setImageResource(resId);
+    }
+
+    public int getLastImageResource() {
+        return mLastResId;
+    }
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d3110a4..0ea4722 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -339,13 +339,15 @@
 
     /**
      *  Save the Bluetooth on/off state
-     *
      */
     private void persistBluetoothSetting(int value) {
         if (DBG) Slog.d(TAG, "Persisting Bluetooth Setting: " + value);
+        // waive WRITE_SECURE_SETTINGS permission check
+        long callingIdentity = Binder.clearCallingIdentity();
         Settings.Global.putInt(mContext.getContentResolver(),
                                Settings.Global.BLUETOOTH_ON,
                                value);
+        Binder.restoreCallingIdentity(callingIdentity);
     }
 
     /**
@@ -610,20 +612,26 @@
     }
 
     /**
-     * Action taken when GattService is turned off
+     * Action taken when GattService is turned on
      */
     private void onBluetoothGattServiceUp() {
         if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up");
         try {
             mBluetoothLock.readLock().lock();
-            if (isBleAppPresent() == false && mBluetooth != null
-                  && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+            if (mBluetooth == null) {
+                if (DBG) Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!");
+                return;
+            }
+            int st = mBluetooth.getState();
+            if (st != BluetoothAdapter.STATE_BLE_ON) {
+                if (DBG) Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: " +
+                        BluetoothAdapter.nameForState(st));
+                return;
+            }
+            if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) {
+                // This triggers transition to STATE_ON
                 mBluetooth.onLeServiceUp();
-
-                // waive WRITE_SECURE_SETTINGS permission check
-                long callingIdentity = Binder.clearCallingIdentity();
                 persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-                Binder.restoreCallingIdentity(callingIdentity);
             }
         } catch (RemoteException e) {
             Slog.e(TAG,"Unable to call onServiceUp", e);
@@ -763,10 +771,7 @@
 
         synchronized(mReceiver) {
             if (persist) {
-                // waive WRITE_SECURE_SETTINGS permission check
-                long callingIdentity = Binder.clearCallingIdentity();
                 persistBluetoothSetting(BLUETOOTH_OFF);
-                Binder.restoreCallingIdentity(callingIdentity);
             }
             mEnableExternal = false;
             sendDisableMsg();
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 1398530..4d6ffe6 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -16,12 +16,14 @@
 
 package com.android.server;
 
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
+import android.app.admin.PasswordMetrics;
 import android.app.backup.BackupManager;
 import android.app.trust.IStrongAuthTracker;
 import android.app.trust.TrustManager;
@@ -931,6 +933,7 @@
         synchronized (mSeparateChallengeLock) {
             setLockPatternInternal(pattern, savedCredential, userId);
             setSeparateProfileChallengeEnabled(userId, true, null);
+            notifyPasswordChanged(userId);
         }
     }
 
@@ -945,6 +948,7 @@
             setKeystorePassword(null, userId);
             fixateNewestUserKeyAuth(userId);
             onUserLockChanged(userId);
+            notifyActivePasswordMetricsAvailable(null, userId);
             return;
         }
 
@@ -994,6 +998,7 @@
         synchronized (mSeparateChallengeLock) {
             setLockPasswordInternal(password, savedCredential, userId);
             setSeparateProfileChallengeEnabled(userId, true, null);
+            notifyPasswordChanged(userId);
         }
     }
 
@@ -1007,6 +1012,7 @@
             setKeystorePassword(null, userId);
             fixateNewestUserKeyAuth(userId);
             onUserLockChanged(userId);
+            notifyActivePasswordMetricsAvailable(null, userId);
             return;
         }
 
@@ -1420,6 +1426,7 @@
                 // migrate credential to GateKeeper
                 credentialUtil.setCredential(credential, null, userId);
                 if (!hasChallenge) {
+                    notifyActivePasswordMetricsAvailable(credential, userId);
                     return VerifyCredentialResponse.OK;
                 }
                 // Fall through to get the auth token. Technically this should never happen,
@@ -1459,6 +1466,7 @@
             if (progressCallback != null) {
                 progressCallback.onCredentialVerified();
             }
+            notifyActivePasswordMetricsAvailable(credential, userId);
             unlockKeystore(credential, userId);
 
             Slog.i(TAG, "Unlocking user " + userId +
@@ -1482,6 +1490,36 @@
         return response;
     }
 
+    private void notifyActivePasswordMetricsAvailable(String password, @UserIdInt int userId) {
+        final PasswordMetrics metrics;
+        if (password == null) {
+            metrics = new PasswordMetrics();
+        } else {
+            metrics = PasswordMetrics.computeForPassword(password);
+            metrics.quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(userId);
+        }
+
+        // Asynchronous to avoid dead lock
+        mHandler.post(() -> {
+            DevicePolicyManager dpm = (DevicePolicyManager)
+                    mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+            dpm.setActivePasswordState(metrics, userId);
+        });
+    }
+
+    /**
+     * Call after {@link #notifyActivePasswordMetricsAvailable} so metrics are updated before
+     * reporting the password changed.
+     */
+    private void notifyPasswordChanged(@UserIdInt int userId) {
+        // Same handler as notifyActivePasswordMetricsAvailable to ensure correct ordering
+        mHandler.post(() -> {
+            DevicePolicyManager dpm = (DevicePolicyManager)
+                    mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+            dpm.reportPasswordChanged(userId);
+        });
+    }
+
     @Override
     public boolean checkVoldPassword(int userId) throws RemoteException {
         if (!mFirstCallToVold) {
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index a1c3564..f712f12 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -16,7 +16,11 @@
 
 package com.android.server;
 
+import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
+import static android.net.NetworkRecommendationProvider.EXTRA_SEQUENCE;
+
 import android.Manifest.permission;
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -25,27 +29,29 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.INetworkRecommendationProvider;
 import android.net.INetworkScoreCache;
 import android.net.INetworkScoreService;
 import android.net.NetworkKey;
-import android.net.NetworkScoreManager;
 import android.net.NetworkScorerAppManager;
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
 import android.net.RecommendationRequest;
 import android.net.RecommendationResult;
 import android.net.ScoredNetwork;
+import android.net.Uri;
 import android.net.wifi.WifiConfiguration;
-import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
+import android.provider.Settings.Global;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.TimedRemoteCaller;
 
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
@@ -59,6 +65,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 
 /**
@@ -67,17 +74,20 @@
  */
 public class NetworkScoreService extends INetworkScoreService.Stub {
     private static final String TAG = "NetworkScoreService";
-    private static final boolean DBG = false;
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final Context mContext;
     private final NetworkScorerAppManager mNetworkScorerAppManager;
+    private final RequestRecommendationCaller mRequestRecommendationCaller;
     @GuardedBy("mScoreCaches")
     private final Map<Integer, RemoteCallbackList<INetworkScoreCache>> mScoreCaches;
     /** Lock used to update mPackageMonitor when scorer package changes occur. */
     private final Object mPackageMonitorLock = new Object[0];
+    private final Object mServiceConnectionLock = new Object[0];
 
     @GuardedBy("mPackageMonitorLock")
     private NetworkScorerPackageMonitor mPackageMonitor;
+    @GuardedBy("mServiceConnectionLock")
     private ScoringServiceConnection mServiceConnection;
 
     private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
@@ -99,10 +109,10 @@
      * manages the service connection.
      */
     private class NetworkScorerPackageMonitor extends PackageMonitor {
-        final String mRegisteredPackage;
+        final List<String> mPackagesToWatch;
 
-        private NetworkScorerPackageMonitor(String mRegisteredPackage) {
-            this.mRegisteredPackage = mRegisteredPackage;
+        private NetworkScorerPackageMonitor(List<String> packagesToWatch) {
+            mPackagesToWatch = packagesToWatch;
         }
 
         @Override
@@ -136,7 +146,7 @@
         }
 
         private void evaluateBinding(String scorerPackageName, boolean forceUnbind) {
-            if (mRegisteredPackage.equals(scorerPackageName)) {
+            if (mPackagesToWatch.contains(scorerPackageName)) {
                 if (DBG) {
                     Log.d(TAG, "Evaluating binding for: " + scorerPackageName
                             + ", forceUnbind=" + forceUnbind);
@@ -146,13 +156,14 @@
                 if (activeScorer == null) {
                     // Package change has invalidated a scorer, this will also unbind any service
                     // connection.
-                    Log.i(TAG, "Package " + mRegisteredPackage +
-                            " is no longer valid, disabling scoring.");
-                    setScorerInternal(null);
-                } else if (activeScorer.mScoringServiceClassName == null) {
-                    // The scoring service is not available, make sure it's unbound.
+                    if (DBG) Log.d(TAG, "No active scorers available.");
                     unbindFromScoringServiceIfNeeded();
-                } else { // The scoring service changed in some way.
+                } else if (activeScorer.packageName.equals(scorerPackageName)) {
+                    if (DBG) {
+                        Log.d(TAG, "Possible change to the active scorer: "
+                            + activeScorer.packageName);
+                    }
+                    // The scoring service changed in some way.
                     if (forceUnbind) {
                         unbindFromScoringServiceIfNeeded();
                     }
@@ -162,6 +173,27 @@
         }
     }
 
+    /**
+     * Reevaluates the service binding when the Settings toggle is changed.
+     */
+    private class SettingsObserver extends ContentObserver {
+
+        public SettingsObserver() {
+            super(null /*handler*/);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            onChange(selfChange, null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (DBG) Log.d(TAG, String.format("onChange(%s, %s)", selfChange, uri));
+            bindToScoringServiceIfNeeded();
+        }
+    }
+
     public NetworkScoreService(Context context) {
       this(context, new NetworkScorerAppManager(context));
     }
@@ -176,24 +208,16 @@
         mContext.registerReceiverAsUser(
                 mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
                 null /* scheduler */);
+        // TODO(jjoslin): 12/15/16 - Make timeout configurable.
+        mRequestRecommendationCaller =
+            new RequestRecommendationCaller(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
     }
 
     /** Called when the system is ready to run third-party code but before it actually does so. */
     void systemReady() {
         if (DBG) Log.d(TAG, "systemReady");
-        ContentResolver cr = mContext.getContentResolver();
-        if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
-            // On first run, we try to initialize the scorer to the one configured at build time.
-            // This will be a no-op if the scorer isn't actually valid.
-            String defaultPackage = mContext.getResources().getString(
-                    R.string.config_defaultNetworkScorerPackageName);
-            if (!TextUtils.isEmpty(defaultPackage)) {
-                mNetworkScorerAppManager.setActiveScorer(defaultPackage);
-            }
-            Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
-        }
-
         registerPackageMonitorIfNeeded();
+        registerRecommendationSettingObserverIfNeeded();
     }
 
     /** Called when the system is ready for us to start third-party code. */
@@ -207,29 +231,40 @@
         bindToScoringServiceIfNeeded();
     }
 
+    private void registerRecommendationSettingObserverIfNeeded() {
+        final List<String> providerPackages =
+            mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
+        if (!providerPackages.isEmpty()) {
+            final ContentResolver resolver = mContext.getContentResolver();
+            final Uri uri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
+            resolver.registerContentObserver(uri, false, new SettingsObserver());
+        }
+    }
+
     private void registerPackageMonitorIfNeeded() {
         if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
-        NetworkScorerAppData scorer = mNetworkScorerAppManager.getActiveScorer();
+        final List<String> providerPackages =
+            mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
         synchronized (mPackageMonitorLock) {
             // Unregister the current monitor if needed.
             if (mPackageMonitor != null) {
                 if (DBG) {
                     Log.d(TAG, "Unregistering package monitor for "
-                            + mPackageMonitor.mRegisteredPackage);
+                            + mPackageMonitor.mPackagesToWatch);
                 }
                 mPackageMonitor.unregister();
                 mPackageMonitor = null;
             }
 
-            // Create and register the monitor if a scorer is active.
-            if (scorer != null) {
-                mPackageMonitor = new NetworkScorerPackageMonitor(scorer.mPackageName);
+            // Create and register the monitor if there are packages that could be providers.
+            if (!providerPackages.isEmpty()) {
+                mPackageMonitor = new NetworkScorerPackageMonitor(providerPackages);
                 // TODO: Need to update when we support per-user scorers. http://b/23422763
                 mPackageMonitor.register(mContext, null /* thread */, UserHandle.SYSTEM,
                         false /* externalStorage */);
                 if (DBG) {
                     Log.d(TAG, "Registered package monitor for "
-                            + mPackageMonitor.mRegisteredPackage);
+                            + mPackageMonitor.mPackagesToWatch);
                 }
             }
         }
@@ -243,22 +278,24 @@
 
     private void bindToScoringServiceIfNeeded(NetworkScorerAppData scorerData) {
         if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + scorerData + ")");
-        if (scorerData != null && scorerData.mScoringServiceClassName != null) {
-            ComponentName componentName =
-                    new ComponentName(scorerData.mPackageName, scorerData.mScoringServiceClassName);
-            // If we're connected to a different component then drop it.
-            if (mServiceConnection != null
-                    && !mServiceConnection.mComponentName.equals(componentName)) {
-                unbindFromScoringServiceIfNeeded();
-            }
+        if (scorerData != null && scorerData.recommendationServiceClassName != null) {
+            ComponentName componentName = new ComponentName(scorerData.packageName,
+                    scorerData.recommendationServiceClassName);
+            synchronized (mServiceConnectionLock) {
+                // If we're connected to a different component then drop it.
+                if (mServiceConnection != null
+                        && !mServiceConnection.mComponentName.equals(componentName)) {
+                    unbindFromScoringServiceIfNeeded();
+                }
 
-            // If we're not connected at all then create a new connection.
-            if (mServiceConnection == null) {
-                mServiceConnection = new ScoringServiceConnection(componentName);
-            }
+                // If we're not connected at all then create a new connection.
+                if (mServiceConnection == null) {
+                    mServiceConnection = new ScoringServiceConnection(componentName);
+                }
 
-            // Make sure the connection is connected (idempotent)
-            mServiceConnection.connect(mContext);
+                // Make sure the connection is connected (idempotent)
+                mServiceConnection.connect(mContext);
+            }
         } else { // otherwise make sure it isn't bound.
             unbindFromScoringServiceIfNeeded();
         }
@@ -266,10 +303,13 @@
 
     private void unbindFromScoringServiceIfNeeded() {
         if (DBG) Log.d(TAG, "unbindFromScoringServiceIfNeeded");
-        if (mServiceConnection != null) {
-            mServiceConnection.disconnect(mContext);
+        synchronized (mServiceConnectionLock) {
+            if (mServiceConnection != null) {
+                mServiceConnection.disconnect(mContext);
+            }
+            mServiceConnection = null;
         }
-        mServiceConnection = null;
+        clearInternal();
     }
 
     @Override
@@ -349,7 +389,8 @@
         // mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
         mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
 
-        return setScorerInternal(packageName);
+        // Scorers (recommendation providers) are selected and no longer set.
+        return false;
     }
 
     @Override
@@ -359,56 +400,13 @@
         if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) ||
                 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
                         PackageManager.PERMISSION_GRANTED) {
-            // The return value is discarded here because at this point, the call should always
-            // succeed. The only reason for failure is if the new package is not a valid scorer, but
-            // we're disabling scoring altogether here.
-            setScorerInternal(null /* packageName */);
+            // no-op for now but we could write to the setting if needed.
         } else {
             throw new SecurityException(
                     "Caller is neither the active scorer nor the scorer manager.");
         }
     }
 
-    /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
-    private boolean setScorerInternal(String packageName) {
-        if (DBG) Log.d(TAG, "setScorerInternal(" + packageName + ")");
-        long token = Binder.clearCallingIdentity();
-        try {
-            unbindFromScoringServiceIfNeeded();
-            // Preemptively clear scores even though the set operation could fail. We do this for
-            // safety as scores should never be compared across apps; in practice, Settings should
-            // only be allowing valid apps to be set as scorers, so failure here should be rare.
-            clearInternal();
-            // Get the scorer that is about to be replaced, if any, so we can notify it directly.
-            NetworkScorerAppData prevScorer = mNetworkScorerAppManager.getActiveScorer();
-            boolean result = mNetworkScorerAppManager.setActiveScorer(packageName);
-            // Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed
-            // then we'll attempt to restore the previous binding (if any), otherwise an attempt
-            // will be made to bind to the new scorer.
-            bindToScoringServiceIfNeeded();
-            if (result) { // new scorer successfully set
-                registerPackageMonitorIfNeeded();
-
-                Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
-                if (prevScorer != null) { // Directly notify the old scorer.
-                    intent.setPackage(prevScorer.mPackageName);
-                    // TODO: Need to update when we support per-user scorers. http://b/23422763
-                    mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-                }
-
-                if (packageName != null) { // Then notify the new scorer
-                    intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
-                    intent.setPackage(packageName);
-                    // TODO: Need to update when we support per-user scorers. http://b/23422763
-                    mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-                }
-            }
-            return result;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
     /** Clear scores. Callers are responsible for checking permissions as appropriate. */
     private void clearInternal() {
         sendCallback(new Consumer<INetworkScoreCache>() {
@@ -464,7 +462,22 @@
 
     @Override
     public RecommendationResult requestRecommendation(RecommendationRequest request) {
-        // TODO(jjoslin): 11/25/16 - Update with real impl.
+        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+        throwIfCalledOnMainThread();
+        final INetworkRecommendationProvider provider = getRecommendationProvider();
+        if (provider != null) {
+            try {
+                return mRequestRecommendationCaller.getRecommendationResult(provider, request);
+            } catch (RemoteException | TimeoutException e) {
+                Log.w(TAG, "Failed to request a recommendation.", e);
+                // TODO(jjoslin): 12/15/16 - Keep track of failures.
+            }
+        }
+
+        if (DBG) {
+            Log.d(TAG, "Returning the default network recommendation.");
+        }
+
         WifiConfiguration selectedConfig = null;
         if (request != null) {
             selectedConfig = request.getCurrentSelectedConfig();
@@ -474,7 +487,19 @@
 
     @Override
     public boolean requestScores(NetworkKey[] networks) {
-        // TODO(jjoslin): 12/13/16 - Implement
+        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+        final INetworkRecommendationProvider provider = getRecommendationProvider();
+        if (provider != null) {
+            try {
+                provider.requestScores(networks);
+                // TODO(jjoslin): 12/15/16 - Consider pushing null scores into the cache to prevent
+                // repeated requests for the same scores.
+                return true;
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to request scores.", e);
+                // TODO(jjoslin): 12/15/16 - Keep track of failures.
+            }
+        }
         return false;
     }
 
@@ -486,7 +511,7 @@
             writer.println("Scoring is disabled.");
             return;
         }
-        writer.println("Current scorer: " + currentScorer.mPackageName);
+        writer.println("Current scorer: " + currentScorer.packageName);
 
         sendCallback(new Consumer<INetworkScoreCache>() {
             @Override
@@ -499,10 +524,12 @@
             }
         }, getScoreCacheLists());
 
-        if (mServiceConnection != null) {
-            mServiceConnection.dump(fd, writer, args);
-        } else {
-            writer.println("ScoringServiceConnection: null");
+        synchronized (mServiceConnectionLock) {
+            if (mServiceConnection != null) {
+                mServiceConnection.dump(fd, writer, args);
+            } else {
+                writer.println("ScoringServiceConnection: null");
+            }
         }
         writer.flush();
     }
@@ -535,10 +562,27 @@
         }
     }
 
+    private void throwIfCalledOnMainThread() {
+        if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
+            throw new RuntimeException("Cannot invoke on the main thread");
+        }
+    }
+
+    @Nullable
+    private INetworkRecommendationProvider getRecommendationProvider() {
+        synchronized (mServiceConnectionLock) {
+            if (mServiceConnection != null) {
+                return mServiceConnection.getRecommendationProvider();
+            }
+        }
+        return null;
+    }
+
     private static class ScoringServiceConnection implements ServiceConnection {
         private final ComponentName mComponentName;
-        private boolean mBound = false;
-        private boolean mConnected = false;
+        private volatile boolean mBound = false;
+        private volatile boolean mConnected = false;
+        private volatile INetworkRecommendationProvider mRecommendationProvider;
 
         ScoringServiceConnection(ComponentName componentName) {
             mComponentName = componentName;
@@ -569,12 +613,19 @@
             } catch (RuntimeException e) {
                 Log.e(TAG, "Unbind failed.", e);
             }
+
+            mRecommendationProvider = null;
+        }
+
+        INetworkRecommendationProvider getRecommendationProvider() {
+            return mRecommendationProvider;
         }
 
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString());
             mConnected = true;
+            mRecommendationProvider = INetworkRecommendationProvider.Stub.asInterface(service);
         }
 
         @Override
@@ -583,6 +634,7 @@
                 Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString());
             }
             mConnected = false;
+            mRecommendationProvider = null;
         }
 
         public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -590,4 +642,43 @@
                     + ", connected: " + mConnected);
         }
     }
+
+    /**
+     * Executes the async requestRecommendation() call with a timeout.
+     */
+    private static final class RequestRecommendationCaller
+            extends TimedRemoteCaller<RecommendationResult> {
+        private final IRemoteCallback mCallback;
+
+        RequestRecommendationCaller(long callTimeoutMillis) {
+            super(callTimeoutMillis);
+            mCallback = new IRemoteCallback.Stub() {
+                @Override
+                public void sendResult(Bundle data) throws RemoteException {
+                    final RecommendationResult result =
+                            data.getParcelable(EXTRA_RECOMMENDATION_RESULT);
+                    final int sequence = data.getInt(EXTRA_SEQUENCE, -1);
+                    onRemoteMethodResult(result, sequence);
+                }
+            };
+        }
+
+        /**
+         * Runs the requestRecommendation() call on the given {@link INetworkRecommendationProvider}
+         * instance.
+         *
+         * @param target the {@link INetworkRecommendationProvider} to request a recommendation
+         *               from
+         * @param request the {@link RecommendationRequest} from the calling client
+         * @return a {@link RecommendationResult} from the provider
+         * @throws RemoteException if the call failed
+         * @throws TimeoutException if the call took longer than the set timeout
+         */
+        RecommendationResult getRecommendationResult(INetworkRecommendationProvider target,
+                RecommendationRequest request) throws RemoteException, TimeoutException {
+            final int sequence = onBeforeRemoteCall();
+            target.requestRecommendation(request, mCallback, sequence);
+            return getResultTimed(sequence);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9b67bd2..eae4905 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -369,6 +369,7 @@
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
@@ -539,8 +540,6 @@
     private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
             "android.intent.action.REMOTE_BUGREPORT_FINISHED";
 
-    // Used to indicate that a task is removed it should also be removed from recents.
-    private static final boolean REMOVE_FROM_RECENTS = true;
     // Used to indicate that an app transition should be animated.
     static final boolean ANIMATE = true;
 
@@ -4826,7 +4825,7 @@
                     // because we don't support returning them across task boundaries. Also, to
                     // keep backwards compatibility we remove the task from recents when finishing
                     // task with root activity.
-                    res = removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
+                    res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
                     if (!res) {
                         Slog.i(TAG, "Removing task failed to finish activity");
                     }
@@ -5471,7 +5470,7 @@
                             tr.getBaseIntent().getComponent().getPackageName();
                     if (tr.userId != userId) continue;
                     if (!taskPackageName.equals(packageName)) continue;
-                    removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+                    mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
                 }
             }
 
@@ -9585,79 +9584,6 @@
         mWindowManager.executeAppTransition();
     }
 
-    private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess,
-            boolean removeFromRecents) {
-        if (removeFromRecents) {
-            mRecentTasks.remove(tr);
-            tr.removedFromRecents();
-        }
-        ComponentName component = tr.getBaseIntent().getComponent();
-        if (component == null) {
-            Slog.w(TAG, "No component for base intent of task: " + tr);
-            return;
-        }
-
-        // Find any running services associated with this app and stop if needed.
-        mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
-
-        if (!killProcess) {
-            return;
-        }
-
-        // Determine if the process(es) for this task should be killed.
-        final String pkg = component.getPackageName();
-        ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
-        ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
-        for (int i = 0; i < pmap.size(); i++) {
-
-            SparseArray<ProcessRecord> uids = pmap.valueAt(i);
-            for (int j = 0; j < uids.size(); j++) {
-                ProcessRecord proc = uids.valueAt(j);
-                if (proc.userId != tr.userId) {
-                    // Don't kill process for a different user.
-                    continue;
-                }
-                if (proc == mHomeProcess) {
-                    // Don't kill the home process along with tasks from the same package.
-                    continue;
-                }
-                if (!proc.pkgList.containsKey(pkg)) {
-                    // Don't kill process that is not associated with this task.
-                    continue;
-                }
-
-                for (int k = 0; k < proc.activities.size(); k++) {
-                    TaskRecord otherTask = proc.activities.get(k).task;
-                    if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
-                        // Don't kill process(es) that has an activity in a different task that is
-                        // also in recents.
-                        return;
-                    }
-                }
-
-                if (proc.foregroundServices) {
-                    // Don't kill process(es) with foreground service.
-                    return;
-                }
-
-                // Add process to kill list.
-                procsToKill.add(proc);
-            }
-        }
-
-        // Kill the running processes.
-        for (int i = 0; i < procsToKill.size(); i++) {
-            ProcessRecord pr = procsToKill.get(i);
-            if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                    && pr.curReceivers.isEmpty()) {
-                pr.kill("remove task", true);
-            } else {
-                // We delay killing processes that are not in the background or running a receiver.
-                pr.waitingToKill = "remove task";
-            }
-        }
-    }
-
     private void removeTasksByPackageNameLocked(String packageName, int userId) {
         // Remove all tasks with activities in the specified package from the list of recent tasks
         for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
@@ -9667,7 +9593,7 @@
             ComponentName cn = tr.intent.getComponent();
             if (cn != null && cn.getPackageName().equals(packageName)) {
                 // If the package name matches, remove the task.
-                removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
+                mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
             }
         }
     }
@@ -9685,35 +9611,11 @@
             final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
                     && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
             if (sameComponent) {
-                removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+                mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
             }
         }
     }
 
-    /**
-     * Removes the task with the specified task id.
-     *
-     * @param taskId Identifier of the task to be removed.
-     * @param killProcess Kill any process associated with the task if possible.
-     * @param removeFromRecents Whether to also remove the task from recents.
-     * @return Returns true if the given task was found and removed.
-     */
-    private boolean removeTaskByIdLocked(int taskId, boolean killProcess,
-            boolean removeFromRecents) {
-        final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
-                taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
-        if (tr != null) {
-            tr.removeTaskActivitiesLocked();
-            cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
-            if (tr.isPersistable) {
-                notifyTaskPersisterLocked(null, true);
-            }
-            return true;
-        }
-        Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
-        return false;
-    }
-
     @Override
     public void removeStack(int stackId) {
         enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()");
@@ -9724,15 +9626,7 @@
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                if (stack == null) {
-                    return;
-                }
-                final ArrayList<TaskRecord> tasks = stack.getAllTasks();
-                for (int i = tasks.size() - 1; i >= 0; i--) {
-                    removeTaskByIdLocked(
-                            tasks.get(i).taskId, true /* killProcess */, REMOVE_FROM_RECENTS);
-                }
+                mStackSupervisor.removeStackLocked(stackId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -9761,7 +9655,7 @@
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
+                return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -22520,7 +22414,8 @@
                 long origId = Binder.clearCallingIdentity();
                 try {
                     // We remove the task from recents to preserve backwards
-                    if (!removeTaskByIdLocked(mTaskId, false, REMOVE_FROM_RECENTS)) {
+                    if (!mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
+                            REMOVE_FROM_RECENTS)) {
                         throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
                     }
                 } finally {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index fe261f1..b4b3465 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -249,6 +249,9 @@
     // Don't execute any calls to resume.
     static final boolean DEFER_RESUME = true;
 
+    // Used to indicate that a task is removed it should also be removed from recents.
+    static final boolean REMOVE_FROM_RECENTS = true;
+
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
             new ArrayMap<>();
@@ -2183,7 +2186,8 @@
                         // Update the return-to to reflect where the pinned stack task was moved
                         // from so that we retain the stack that was previously visible if the
                         // pinned stack is recreated. See moveActivityToPinnedStackLocked().
-                        task.setTaskToReturnTo(getFocusedStack().getStackId() == HOME_STACK_ID
+                        final int focusedStackId = getFocusedStack().getStackId();
+                        task.setTaskToReturnTo(focusedStackId == HOME_STACK_ID || !onTop
                                 ? HOME_ACTIVITY_TYPE : APPLICATION_ACTIVITY_TYPE);
                     }
                     moveTaskToStackLocked(tasks.get(i).taskId,
@@ -2374,6 +2378,141 @@
         return activityContainer.mStack;
     }
 
+    /**
+     * Removes the stack associed with the given {@param stackId}.  If the {@param stackId} is the
+     * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
+     * instead moved back onto the fullscreen stack.
+     */
+    void removeStackLocked(int stackId) {
+        final ActivityStack stack = getStack(stackId);
+        if (stack == null) {
+            return;
+        }
+
+        final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        if (stack.getStackId() == PINNED_STACK_ID) {
+            final ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+            if (fullscreenStack != null) {
+                final boolean isFullscreenStackVisible =
+                        fullscreenStack.getStackVisibilityLocked(null) == STACK_VISIBLE;
+                for (int i = 0; i < tasks.size(); i++) {
+                    // Insert the task either at the top of the fullscreen stack if it is hidden,
+                    // or just under the top task if it is currently visible
+                    final int insertPosition = isFullscreenStackVisible
+                            ? Math.max(0, fullscreenStack.getChildCount() - 1)
+                            : fullscreenStack.getChildCount();
+                    positionTaskInStackLocked(tasks.get(i).taskId, FULLSCREEN_WORKSPACE_STACK_ID,
+                            insertPosition);
+                }
+                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                resumeFocusedStackTopActivityLocked();
+            } else {
+                // If there is no fullscreen stack, then create the stack and move all the tasks
+                // onto the stack
+                moveTasksToFullscreenStackLocked(PINNED_STACK_ID, false /* onTop */);
+            }
+        } else {
+            for (int i = tasks.size() - 1; i >= 0; i--) {
+                removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+                        REMOVE_FROM_RECENTS);
+            }
+        }
+    }
+
+    /**
+     * Removes the task with the specified task id.
+     *
+     * @param taskId Identifier of the task to be removed.
+     * @param killProcess Kill any process associated with the task if possible.
+     * @param removeFromRecents Whether to also remove the task from recents.
+     * @return Returns true if the given task was found and removed.
+     */
+    boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
+        final TaskRecord tr = anyTaskForIdLocked(taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+        if (tr != null) {
+            tr.removeTaskActivitiesLocked();
+            cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
+            if (tr.isPersistable) {
+                mService.notifyTaskPersisterLocked(null, true);
+            }
+            return true;
+        }
+        Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
+        return false;
+    }
+
+    void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
+        if (removeFromRecents) {
+            mRecentTasks.remove(tr);
+            tr.removedFromRecents();
+        }
+        ComponentName component = tr.getBaseIntent().getComponent();
+        if (component == null) {
+            Slog.w(TAG, "No component for base intent of task: " + tr);
+            return;
+        }
+
+        // Find any running services associated with this app and stop if needed.
+        mService.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
+
+        if (!killProcess) {
+            return;
+        }
+
+        // Determine if the process(es) for this task should be killed.
+        final String pkg = component.getPackageName();
+        ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
+        ArrayMap<String, SparseArray<ProcessRecord>> pmap = mService.mProcessNames.getMap();
+        for (int i = 0; i < pmap.size(); i++) {
+
+            SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+            for (int j = 0; j < uids.size(); j++) {
+                ProcessRecord proc = uids.valueAt(j);
+                if (proc.userId != tr.userId) {
+                    // Don't kill process for a different user.
+                    continue;
+                }
+                if (proc == mService.mHomeProcess) {
+                    // Don't kill the home process along with tasks from the same package.
+                    continue;
+                }
+                if (!proc.pkgList.containsKey(pkg)) {
+                    // Don't kill process that is not associated with this task.
+                    continue;
+                }
+
+                for (int k = 0; k < proc.activities.size(); k++) {
+                    TaskRecord otherTask = proc.activities.get(k).task;
+                    if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+                        // Don't kill process(es) that has an activity in a different task that is
+                        // also in recents.
+                        return;
+                    }
+                }
+
+                if (proc.foregroundServices) {
+                    // Don't kill process(es) with foreground service.
+                    return;
+                }
+
+                // Add process to kill list.
+                procsToKill.add(proc);
+            }
+        }
+
+        // Kill the running processes.
+        for (int i = 0; i < procsToKill.size(); i++) {
+            ProcessRecord pr = procsToKill.get(i);
+            if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                    && pr.curReceivers.isEmpty()) {
+                pr.kill("remove task", true);
+            } else {
+                // We delay killing processes that are not in the background or running a receiver.
+                pr.waitingToKill = "remove task";
+            }
+        }
+    }
+
     int getNextStackId() {
         while (true) {
             if (mNextFreeStackId >= FIRST_DYNAMIC_STACK_ID
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 5e98859..9ffe2b7 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -433,6 +433,8 @@
                             }));
                     intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL,
                             mLastPortalProbeResult.detectUrl);
+                    intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
+                            getCaptivePortalUserAgent(mContext));
                     intent.setFlags(
                             Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
                     mContext.startActivityAsUser(intent, UserHandle.CURRENT);
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 63b5250..c0550c6 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -142,8 +142,9 @@
 
         if (DBG) {
             Slog.d(TAG, String.format(
-                    "showNotification tag=%s event=%s transport=%s extraInfo=%d highPrioriy=%s",
-                    tag, nameOf(eventId), getTransportName(transportType), extraInfo, highPriority));
+                    "showNotification tag=%s event=%s transport=%s extraInfo=%s highPrioriy=%s",
+                    tag, nameOf(eventId), getTransportName(transportType), extraInfo,
+                    highPriority));
         }
 
         Resources r = Resources.getSystem();
@@ -227,13 +228,14 @@
         }
         final int eventId = mNotificationTypeMap.get(id);
         if (DBG) {
-            Slog.d(TAG, String.format("clearing notification tag=%s event=", tag, nameOf(eventId)));
+            Slog.d(TAG, String.format("clearing notification tag=%s event=%s", tag,
+                   nameOf(eventId)));
         }
         try {
             mNotificationManager.cancelAsUser(tag, eventId, UserHandle.ALL);
         } catch (NullPointerException npe) {
             Slog.d(TAG, String.format(
-                    "failed to clear notification tag=%s event=", tag, nameOf(eventId)), npe);
+                    "failed to clear notification tag=%s event=%s", tag, nameOf(eventId)), npe);
         }
         mNotificationTypeMap.delete(id);
     }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index a03c4aa..dbd719b 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,6 +25,7 @@
 import android.app.NotificationChannel;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -136,15 +137,9 @@
     private boolean isPreChannelsNotification() {
         try {
             if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
-                final boolean isSystemNotification =
-                        NotificationManagerService.isUidSystem(sbn.getUid())
-                                || ("android".equals(sbn.getPackageName()));
-                if (isSystemNotification) {
-                    return false;
-                }
-                final ApplicationInfo applicationInfo =
+                  final ApplicationInfo applicationInfo =
                         mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(),
-                                0, sbn.getUserId());
+                                0, UserHandle.getUserId(sbn.getUid()));
                 if (applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
                     return true;
                 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 95718de..98d4c69 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -784,11 +784,6 @@
         }
     }
 
-    private static boolean isUidSystem(int uid) {
-        final int appid = UserHandle.getAppId(uid);
-        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
-    }
-
     private static class Record {
         static int UNKNOWN_UID = UserHandle.USER_NULL;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 34c1470..df1de11 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7639,6 +7639,11 @@
         }
     }
 
+    @Override
+    public void notifyDexLoad(String loadingPackageName, List<String> dexPaths, String loaderIsa) {
+      // TODO(calin): b/32871170
+    }
+
     // TODO: this is not used nor needed. Delete it.
     @Override
     public boolean performDexOptIfNeeded(String packageName) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3281bd6..4d3a35e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
 import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
+import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
 import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
 import static android.app.admin.DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED;
 import static android.app.admin.DevicePolicyManager.CODE_HAS_DEVICE_OWNER;
@@ -252,7 +253,6 @@
     private static final String ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED =
             "device-provisioning-config-applied";
     private static final String ATTR_DEVICE_PAIRED = "device-paired";
-
     private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
     private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
             = "application-restrictions-manager";
@@ -1689,9 +1689,9 @@
 
         mSecurityLogMonitor = new SecurityLogMonitor(this);
 
-        mHasFeature = mContext.getPackageManager()
+        mHasFeature = mInjector.getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
-        mIsWatch = mContext.getPackageManager()
+        mIsWatch = mInjector.getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_WATCH);
         if (!mHasFeature) {
             // Skip the rest of the initialization
@@ -2341,20 +2341,6 @@
                 out.endTag(null, "failed-password-attempts");
             }
 
-            final PasswordMetrics metrics = policy.mActivePasswordMetrics;
-            if (!metrics.isDefault()) {
-                out.startTag(null, "active-password");
-                out.attribute(null, "quality", Integer.toString(metrics.quality));
-                out.attribute(null, "length", Integer.toString(metrics.length));
-                out.attribute(null, "uppercase", Integer.toString(metrics.upperCase));
-                out.attribute(null, "lowercase", Integer.toString(metrics.lowerCase));
-                out.attribute(null, "letters", Integer.toString(metrics.letters));
-                out.attribute(null, "numeric", Integer.toString(metrics.numeric));
-                out.attribute(null, "symbols", Integer.toString(metrics.symbols));
-                out.attribute(null, "nonletter", Integer.toString(metrics.nonLetter));
-                out.endTag(null, "active-password");
-            }
-
             for (int i = 0; i < policy.mAcceptedCaCertificates.size(); i++) {
                 out.startTag(null, TAG_ACCEPTED_CA_CERTIFICATES);
                 out.attribute(null, ATTR_NAME, policy.mAcceptedCaCertificates.valueAt(i));
@@ -2455,6 +2441,7 @@
         JournaledFile journal = makeJournaledFile(userHandle);
         FileInputStream stream = null;
         File file = journal.chooseForRead();
+        boolean needsRewrite = false;
         try {
             stream = new FileInputStream(file);
             XmlPullParser parser = Xml.newPullParser();
@@ -2541,16 +2528,6 @@
                 } else if ("password-owner".equals(tag)) {
                     policy.mPasswordOwner = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
-                } else if ("active-password".equals(tag)) {
-                    final PasswordMetrics m = policy.mActivePasswordMetrics;
-                    m.quality = Integer.parseInt(parser.getAttributeValue(null, "quality"));
-                    m.length = Integer.parseInt(parser.getAttributeValue(null, "length"));
-                    m.upperCase = Integer.parseInt(parser.getAttributeValue(null, "uppercase"));
-                    m.lowerCase = Integer.parseInt(parser.getAttributeValue(null, "lowercase"));
-                    m.letters = Integer.parseInt(parser.getAttributeValue(null, "letters"));
-                    m.numeric = Integer.parseInt(parser.getAttributeValue(null, "numeric"));
-                    m.symbols = Integer.parseInt(parser.getAttributeValue(null, "symbols"));
-                    m.nonLetter = Integer.parseInt(parser.getAttributeValue(null, "nonletter"));
                 } else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
                     policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
                 } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
@@ -2576,6 +2553,8 @@
                     policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending);
                 } else if (TAG_INITIALIZATION_BUNDLE.equals(tag)) {
                     policy.mInitBundle = PersistableBundle.restoreFromXml(parser);
+                } else if ("active-password".equals(tag)) {
+                    needsRewrite = true;
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -2595,27 +2574,14 @@
             // Ignore
         }
 
+        // Might need to upgrade the file by rewriting it
+        if (needsRewrite) {
+            saveSettingsLocked(userHandle);
+        }
+
         // Generate a list of admins from the admin map
         policy.mAdminList.addAll(policy.mAdminMap.values());
 
-        // Validate that what we stored for the password quality matches
-        // sufficiently what is currently set.  Note that this is only
-        // a sanity check in case the two get out of sync; this should
-        // never normally happen.
-        final long identity = mInjector.binderClearCallingIdentity();
-        try {
-            int actualPasswordQuality = mLockPatternUtils.getActivePasswordQuality(userHandle);
-            if (actualPasswordQuality < policy.mActivePasswordMetrics.quality) {
-                Slog.w(LOG_TAG, "Active password quality 0x"
-                        + Integer.toHexString(policy.mActivePasswordMetrics.quality)
-                        + " does not match actual quality 0x"
-                        + Integer.toHexString(actualPasswordQuality));
-                policy.mActivePasswordMetrics = new PasswordMetrics();
-            }
-        } finally {
-            mInjector.binderRestoreCallingIdentity(identity);
-        }
-
         validatePasswordOwnerLocked(policy);
         updateMaximumTimeToLockLocked(userHandle);
         updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
@@ -3849,6 +3815,8 @@
 
     private boolean isActivePasswordSufficientForUserLocked(
             DevicePolicyData policy, int userHandle, boolean parent) {
+        enforceUserUnlocked(userHandle, parent);
+
         final int requiredPasswordQuality = getPasswordQuality(null, userHandle, parent);
         if (policy.mActivePasswordMetrics.quality < requiredPasswordQuality) {
             return false;
@@ -4460,7 +4428,7 @@
             }
 
             try {
-                int uid = mContext.getPackageManager().getPackageUidAsUser(
+                int uid = mInjector.getPackageManager().getPackageUidAsUser(
                         policy.mDelegatedCertInstallerPackage, userHandle);
                 return uid == callingUid;
             } catch (NameNotFoundException e) {
@@ -4923,33 +4891,52 @@
             return;
         }
         enforceFullCrossUsersPermission(userHandle);
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+        // If the managed profile doesn't have a separate password, set the metrics to default
+        if (isManagedProfile(userHandle) && !isSeparateProfileChallengeEnabled(userHandle)) {
+            metrics = new PasswordMetrics();
+        }
+
+        validateQualityConstant(metrics.quality);
+        DevicePolicyData policy = getUserData(userHandle);
+        synchronized (this) {
+            policy.mActivePasswordMetrics = metrics;
+        }
+    }
+
+    @Override
+    public void reportPasswordChanged(@UserIdInt int userId) {
+        if (!mHasFeature) {
+            return;
+        }
+        enforceFullCrossUsersPermission(userId);
 
         // Managed Profile password can only be changed when it has a separate challenge.
-        if (!isSeparateProfileChallengeEnabled(userHandle)) {
-            enforceNotManagedProfile(userHandle, "set the active password");
+        if (!isSeparateProfileChallengeEnabled(userId)) {
+            enforceNotManagedProfile(userId, "set the active password");
         }
 
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-        validateQualityConstant(metrics.quality);
 
-        DevicePolicyData policy = getUserData(userHandle);
+        DevicePolicyData policy = getUserData(userId);
 
         long ident = mInjector.binderClearCallingIdentity();
         try {
             synchronized (this) {
-                policy.mActivePasswordMetrics = metrics;
                 policy.mFailedPasswordAttempts = 0;
-                saveSettingsLocked(userHandle);
-                updatePasswordExpirationsLocked(userHandle);
-                setExpirationAlarmCheckLocked(mContext, userHandle, /* parent */ false);
+                saveSettingsLocked(userId);
+                updatePasswordExpirationsLocked(userId);
+                setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
 
                 // Send a broadcast to each profile using this password as its primary unlock.
                 sendAdminCommandForLockscreenPoliciesLocked(
                         DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
-                        DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
+                        DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
             }
-            removeCaApprovalsIfNeeded(userHandle);
+            removeCaApprovalsIfNeeded(userId);
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
         }
@@ -6021,6 +6008,14 @@
         }
     }
 
+    private boolean isDeviceOwnerPackage(String packageName, int userId) {
+        synchronized (this) {
+            return mOwners.hasDeviceOwner()
+                    && mOwners.getDeviceOwnerUserId() == userId
+                    && mOwners.getDeviceOwnerPackageName().equals(packageName);
+        }
+    }
+
     public boolean isProfileOwner(ComponentName who, int userId) {
         final ComponentName profileOwner = getProfileOwner(userId);
         return who != null && who.equals(profileOwner);
@@ -6103,7 +6098,7 @@
         Preconditions.checkNotNull(packageName, "packageName is null");
         final int callingUid = mInjector.binderGetCallingUid();
         try {
-            int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
+            int uid = mInjector.getPackageManager().getPackageUidAsUser(packageName,
                     UserHandle.getUserId(callingUid));
             if (uid != callingUid) {
                 throw new SecurityException("Invalid packageName");
@@ -6589,6 +6584,14 @@
                 "User must be running and unlocked");
     }
 
+    private void enforceUserUnlocked(@UserIdInt int userId, boolean parent) {
+        if (parent) {
+            enforceUserUnlocked(getProfileParentId(userId));
+        } else {
+            enforceUserUnlocked(userId);
+        }
+    }
+
     private void enforceManageUsers() {
         final int callingUid = mInjector.binderGetCallingUid();
         if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) {
@@ -6847,7 +6850,7 @@
             }
 
             try {
-                int uid = mContext.getPackageManager().getPackageUidAsUser(
+                int uid = mInjector.getPackageManager().getPackageUidAsUser(
                         policy.mApplicationRestrictionsManagingPackage, userHandle);
                 return uid == callingUid;
             } catch (NameNotFoundException e) {
@@ -8635,7 +8638,7 @@
             }
             final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent()
                     .getPackageName();
-            final String[] pkgs = mContext.getPackageManager().getPackagesForUid(callerUid);
+            final String[] pkgs = mInjector.getPackageManager().getPackagesForUid(callerUid);
 
             for (String pkg : pkgs) {
                 if (deviceOwnerPackageName.equals(pkg)) {
@@ -8672,7 +8675,7 @@
 
             ActivityInfo[] receivers = null;
             try {
-                receivers  = mContext.getPackageManager().getPackageInfo(
+                receivers  = mInjector.getPackageManager().getPackageInfo(
                         deviceOwnerPackage, PackageManager.GET_RECEIVERS).receivers;
             } catch (NameNotFoundException e) {
                 Log.e(LOG_TAG, "Cannot find device owner package", e);
@@ -8728,7 +8731,7 @@
                         < android.os.Build.VERSION_CODES.M) {
                     return false;
                 }
-                final PackageManager packageManager = mContext.getPackageManager();
+                final PackageManager packageManager = mInjector.getPackageManager();
                 switch (grantState) {
                     case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: {
                         mInjector.getPackageManagerInternal().grantRuntimePermission(packageName,
@@ -8763,7 +8766,7 @@
     @Override
     public int getPermissionGrantState(ComponentName admin, String packageName,
             String permission) throws RemoteException {
-        PackageManager packageManager = mContext.getPackageManager();
+        PackageManager packageManager = mInjector.getPackageManager();
 
         UserHandle user = mInjector.binderGetCallingUserHandle();
         synchronized (this) {
@@ -8800,17 +8803,33 @@
     }
 
     @Override
-    public boolean isProvisioningAllowed(String action) {
-        return checkProvisioningPreConditionSkipPermission(action) == CODE_OK;
+    public boolean isProvisioningAllowed(String action, String packageName) {
+        Preconditions.checkNotNull(packageName);
+
+        final int callingUid = mInjector.binderGetCallingUid();
+        final long ident = mInjector.binderClearCallingIdentity();
+        try {
+            final int uidForPackage = mInjector.getPackageManager().getPackageUidAsUser(
+                    packageName, UserHandle.getUserId(callingUid));
+            Preconditions.checkArgument(callingUid == uidForPackage,
+                    "Caller uid doesn't match the one for the provided package.");
+        } catch (NameNotFoundException e) {
+            throw new IllegalArgumentException("Invalid package provided " + packageName, e);
+        } finally {
+            mInjector.binderRestoreCallingIdentity(ident);
+        }
+
+        return checkProvisioningPreConditionSkipPermission(action, packageName) == CODE_OK;
     }
 
     @Override
-    public int checkProvisioningPreCondition(String action) {
+    public int checkProvisioningPreCondition(String action, String packageName) {
+        Preconditions.checkNotNull(packageName);
         enforceCanManageProfileAndDeviceOwners();
-        return checkProvisioningPreConditionSkipPermission(action);
+        return checkProvisioningPreConditionSkipPermission(action, packageName);
     }
 
-    private int checkProvisioningPreConditionSkipPermission(String action) {
+    private int checkProvisioningPreConditionSkipPermission(String action, String packageName) {
         if (!mHasFeature) {
             return CODE_DEVICE_ADMIN_NOT_SUPPORTED;
         }
@@ -8819,7 +8838,7 @@
         if (action != null) {
             switch (action) {
                 case DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE:
-                    return checkManagedProfileProvisioningPreCondition(callingUserId);
+                    return checkManagedProfileProvisioningPreCondition(packageName, callingUserId);
                 case DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE:
                     return checkDeviceOwnerProvisioningPreCondition(callingUserId);
                 case DevicePolicyManager.ACTION_PROVISION_MANAGED_USER:
@@ -8888,7 +8907,7 @@
         }
     }
 
-    private int checkManagedProfileProvisioningPreCondition(int callingUserId) {
+    private int checkManagedProfileProvisioningPreCondition(String packageName, int callingUserId) {
         if (!hasFeatureManagedUsers()) {
             return CODE_MANAGED_USERS_NOT_SUPPORTED;
         }
@@ -8901,24 +8920,25 @@
             // Managed user cannot have a managed profile.
             return CODE_USER_HAS_PROFILE_OWNER;
         }
+
         final long ident = mInjector.binderClearCallingIdentity();
         try {
-             /* STOPSHIP(b/31952368) Reinstate a check similar to this once ManagedProvisioning
-                   uses checkProvisioningPreCondition (see ag/1607846) and passes the packageName
-                   there. In isProvisioningAllowed we should check isCallerDeviceOwner, but for
-                   managed provisioning we need to check the package that is going to be set as PO
-                if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE)) {
-                    if (!isCallerDeviceOwner(callingUid)
-                            || isAdminAffectedByRestriction(mOwners.getDeviceOwnerComponent(),
-                                    UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserId)) {
+            final UserHandle callingUserHandle = UserHandle.of(callingUserId);
+            if (mUserManager.hasUserRestriction(
+                    UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle)) {
+                // The DO can initiate provisioning if the restriction was set by the DO.
+                if (!isDeviceOwnerPackage(packageName, callingUserId)
+                        || isAdminAffectedByRestriction(mOwners.getDeviceOwnerComponent(),
+                                UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserId)) {
                     // Caller is not DO or the restriction was set by the system.
-                    return false;
-                    }
-                } */
+                    return CODE_ADD_MANAGED_PROFILE_DISALLOWED;
+                }
+            }
+
             // TODO: Allow it if the caller is the DO? DO could just call removeUser() before
             // provisioning, so not strictly required...
             boolean canRemoveProfile = !mUserManager.hasUserRestriction(
-                        UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.of(callingUserId));
+                        UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, callingUserHandle);
             if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 50911cb..c653b8e 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -16,10 +16,13 @@
 
 package com.android.server;
 
+import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
+import static android.net.NetworkRecommendationProvider.EXTRA_SEQUENCE;
 import static android.net.NetworkScoreManager.CACHE_FILTER_NONE;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
@@ -28,12 +31,15 @@
 import static org.mockito.Matchers.anyListOf;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.Manifest.permission;
@@ -44,37 +50,42 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.net.INetworkRecommendationProvider;
 import android.net.INetworkScoreCache;
 import android.net.NetworkKey;
-import android.net.NetworkScoreManager;
 import android.net.NetworkScorerAppManager;
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
+import android.net.RecommendationRequest;
+import android.net.RecommendationResult;
 import android.net.ScoredNetwork;
 import android.net.WifiKey;
+import android.net.wifi.WifiConfiguration;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import com.android.internal.R;
 import com.android.server.devicepolicy.MockUtils;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
 
 /**
  * Tests for {@link NetworkScoreService}.
@@ -85,12 +96,8 @@
     private static final ScoredNetwork SCORED_NETWORK =
             new ScoredNetwork(new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00")),
                     null /* rssiCurve*/);
-    private static final NetworkScorerAppData PREV_SCORER = new NetworkScorerAppData(
-            "prevPackageName", 0, "prevScorerName", null /* configurationActivityClassName */,
-            "prevScoringServiceClass");
-    private static final NetworkScorerAppData NEW_SCORER = new NetworkScorerAppData(
-            "newPackageName", 1, "newScorerName", null /* configurationActivityClassName */,
-            "newScoringServiceClass");
+    private static final NetworkScorerAppData NEW_SCORER =
+        new NetworkScorerAppData("newPackageName", 1, "newScoringServiceClass");
 
     @Mock private PackageManager mPackageManager;
     @Mock private NetworkScorerAppManager mNetworkScorerAppManager;
@@ -98,10 +105,12 @@
     @Mock private Resources mResources;
     @Mock private INetworkScoreCache.Stub mNetworkScoreCache, mNetworkScoreCache2;
     @Mock private IBinder mIBinder, mIBinder2;
+    @Mock private INetworkRecommendationProvider mRecommendationProvider;
     @Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor;
 
     private ContentResolver mContentResolver;
     private NetworkScoreService mNetworkScoreService;
+    private RecommendationRequest mRecommendationRequest;
 
     @Before
     public void setUp() throws Exception {
@@ -112,44 +121,9 @@
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
         when(mContext.getResources()).thenReturn(mResources);
         mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager);
-    }
-
-    @Test
-    public void testSystemReady_networkScorerProvisioned() throws Exception {
-        Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 1);
-
-        mNetworkScoreService.systemReady();
-
-        verify(mNetworkScorerAppManager, never()).setActiveScorer(anyString());
-    }
-
-    @Test
-    public void testSystemReady_networkScorerNotProvisioned_defaultScorer() throws Exception {
-        Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 0);
-
-        when(mResources.getString(R.string.config_defaultNetworkScorerPackageName))
-                .thenReturn(NEW_SCORER.mPackageName);
-
-        mNetworkScoreService.systemReady();
-
-        verify(mNetworkScorerAppManager).setActiveScorer(NEW_SCORER.mPackageName);
-        assertEquals(1,
-                Settings.Global.getInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED));
-
-    }
-
-    @Test
-    public void testSystemReady_networkScorerNotProvisioned_noDefaultScorer() throws Exception {
-        Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 0);
-
-        when(mResources.getString(R.string.config_defaultNetworkScorerPackageName))
-                .thenReturn(null);
-
-        mNetworkScoreService.systemReady();
-
-        verify(mNetworkScorerAppManager, never()).setActiveScorer(anyString());
-        assertEquals(1,
-                Settings.Global.getInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED));
+        WifiConfiguration configuration = new WifiConfiguration();
+        mRecommendationRequest = new RecommendationRequest.Builder()
+            .setCurrentRecommendedWifiConfig(configuration).build();
     }
 
     @Test
@@ -159,13 +133,126 @@
         mNetworkScoreService.systemRunning();
 
         verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
-                new ComponentName(NEW_SCORER.mPackageName, NEW_SCORER.mScoringServiceClassName))),
+                new ComponentName(NEW_SCORER.packageName,
+                    NEW_SCORER.recommendationServiceClassName))),
                 any(ServiceConnection.class),
                 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
                 eq(UserHandle.SYSTEM));
     }
 
     @Test
+    public void testRequestScores_noPermission() throws Exception {
+        doThrow(new SecurityException()).when(mContext)
+            .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
+                anyString());
+        try {
+            mNetworkScoreService.requestScores(null);
+            fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testRequestScores_providerNotConnected() throws Exception {
+        assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
+        verifyZeroInteractions(mRecommendationProvider);
+    }
+
+    @Test
+    public void testRequestScores_providerThrowsRemoteException() throws Exception {
+        injectProvider();
+        doThrow(new RemoteException()).when(mRecommendationProvider)
+            .requestScores(any(NetworkKey[].class));
+
+        assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
+    }
+
+    @Test
+    public void testRequestScores_providerAvailable() throws Exception {
+        injectProvider();
+
+        final NetworkKey[] networks = new NetworkKey[0];
+        assertTrue(mNetworkScoreService.requestScores(networks));
+        verify(mRecommendationProvider).requestScores(networks);
+    }
+
+    @Test
+    public void testRequestRecommendation_noPermission() throws Exception {
+        doThrow(new SecurityException()).when(mContext)
+            .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
+                anyString());
+        try {
+            mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+            fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testRequestRecommendation_mainThread() throws Exception {
+        when(mContext.getMainLooper()).thenReturn(Looper.myLooper());
+        try {
+            mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+            fail("requestRecommendation run on main thread.");
+        } catch (RuntimeException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testRequestRecommendation_providerNotConnected() throws Exception {
+        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+
+        final RecommendationResult result =
+                mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+        assertNotNull(result);
+        assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
+                result.getWifiConfiguration());
+    }
+
+    @Test
+    public void testRequestRecommendation_providerThrowsRemoteException() throws Exception {
+        injectProvider();
+        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+        doThrow(new RemoteException()).when(mRecommendationProvider)
+                .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
+                        anyInt());
+
+        final RecommendationResult result =
+                mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+        assertNotNull(result);
+        assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
+                result.getWifiConfiguration());
+    }
+
+    @Test
+    public void testRequestRecommendation_resultReturned() throws Exception {
+        injectProvider();
+        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+        final WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.SSID = "testRequestRecommendation_resultReturned";
+        final RecommendationResult providerResult =
+                new RecommendationResult(wifiConfiguration);
+        final Bundle bundle = new Bundle();
+        bundle.putParcelable(EXTRA_RECOMMENDATION_RESULT, providerResult);
+        doAnswer(invocation -> {
+            bundle.putInt(EXTRA_SEQUENCE, invocation.getArgumentAt(2, int.class));
+            invocation.getArgumentAt(1, IRemoteCallback.class).sendResult(bundle);
+            return null;
+        }).when(mRecommendationProvider)
+                .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
+                        anyInt());
+
+        final RecommendationResult result =
+                mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+        assertNotNull(result);
+        assertEquals(providerResult.getWifiConfiguration().SSID,
+                result.getWifiConfiguration().SSID);
+    }
+
+    @Test
     public void testUpdateScores_notActiveScorer() {
         when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
 
@@ -288,45 +375,6 @@
     }
 
     @Test
-    public void testSetActiveScorer_failure() throws RemoteException {
-        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER);
-        when(mNetworkScorerAppManager.setActiveScorer(NEW_SCORER.mPackageName)).thenReturn(false);
-        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
-                CACHE_FILTER_NONE);
-
-        boolean success = mNetworkScoreService.setActiveScorer(NEW_SCORER.mPackageName);
-
-        assertFalse(success);
-        verify(mNetworkScoreCache).clearScores();
-        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
-                new ComponentName(PREV_SCORER.mPackageName, PREV_SCORER.mScoringServiceClassName))),
-                any(ServiceConnection.class),
-                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
-                eq(UserHandle.SYSTEM));
-    }
-
-    @Test
-    public void testSetActiveScorer_success() throws RemoteException {
-        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, NEW_SCORER);
-        when(mNetworkScorerAppManager.setActiveScorer(NEW_SCORER.mPackageName)).thenReturn(true);
-        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
-                CACHE_FILTER_NONE);
-
-        boolean success = mNetworkScoreService.setActiveScorer(NEW_SCORER.mPackageName);
-
-        assertTrue(success);
-        verify(mNetworkScoreCache).clearScores();
-        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
-                new ComponentName(NEW_SCORER.mPackageName, NEW_SCORER.mScoringServiceClassName))),
-                any(ServiceConnection.class),
-                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
-                eq(UserHandle.SYSTEM));
-        verify(mContext, times(2)).sendBroadcastAsUser(
-                MockUtils.checkIntentAction(NetworkScoreManager.ACTION_SCORER_CHANGED),
-                eq(UserHandle.SYSTEM));
-    }
-
-    @Test
     public void testDisableScoring_notActiveScorer_noBroadcastNetworkPermission() {
         when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
         when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
@@ -338,48 +386,6 @@
         } catch (SecurityException e) {
             // expected
         }
-
-    }
-
-    @Test
-    public void testDisableScoring_activeScorer() throws RemoteException {
-        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
-        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, null);
-        when(mNetworkScorerAppManager.setActiveScorer(null)).thenReturn(true);
-        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
-                CACHE_FILTER_NONE);
-
-        mNetworkScoreService.disableScoring();
-
-        verify(mNetworkScoreCache).clearScores();
-        verify(mContext).sendBroadcastAsUser(
-                MockUtils.checkIntent(new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED)
-                        .setPackage(PREV_SCORER.mPackageName)),
-                eq(UserHandle.SYSTEM));
-        verify(mContext, never()).bindServiceAsUser(any(Intent.class),
-                any(ServiceConnection.class), anyInt(), any(UserHandle.class));
-    }
-
-    @Test
-    public void testDisableScoring_notActiveScorer_hasBroadcastNetworkPermission()
-            throws RemoteException {
-        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
-        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
-                .thenReturn(PackageManager.PERMISSION_GRANTED);
-        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, null);
-        when(mNetworkScorerAppManager.setActiveScorer(null)).thenReturn(true);
-        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
-                CACHE_FILTER_NONE);
-
-        mNetworkScoreService.disableScoring();
-
-        verify(mNetworkScoreCache).clearScores();
-        verify(mContext).sendBroadcastAsUser(
-                MockUtils.checkIntent(new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED)
-                        .setPackage(PREV_SCORER.mPackageName)),
-                eq(UserHandle.SYSTEM));
-        verify(mContext, never()).bindServiceAsUser(any(Intent.class),
-                any(ServiceConnection.class), anyInt(), any(UserHandle.class));
     }
 
     @Test
@@ -434,4 +440,24 @@
 
         assertFalse(stringWriter.toString().isEmpty());
     }
+
+    // "injects" the mock INetworkRecommendationProvider into the NetworkScoreService.
+    private void injectProvider() {
+        final ComponentName componentName = new ComponentName(NEW_SCORER.packageName,
+                NEW_SCORER.recommendationServiceClassName);
+        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
+        when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
+                isA(UserHandle.class))).thenAnswer(new Answer<Boolean>() {
+            @Override
+            public Boolean answer(InvocationOnMock invocation) throws Throwable {
+                IBinder mockBinder = mock(IBinder.class);
+                when(mockBinder.queryLocalInterface(anyString()))
+                        .thenReturn(mRecommendationProvider);
+                invocation.getArgumentAt(1, ServiceConnection.class)
+                        .onServiceConnected(componentName, mockBinder);
+                return true;
+            }
+        });
+        mNetworkScoreService.systemRunning();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 48c9853..c35d114 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -77,7 +77,7 @@
 
 /**
  * Tests for DevicePolicyManager( and DevicePolicyManagerService).
- *
+ * You can run them via:
  m FrameworksServicesTests &&
  adb install \
    -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
@@ -85,6 +85,9 @@
    -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
 
  (mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
+ *
+ * , or:
+ * runtest -c com.android.server.devicepolicy.DevicePolicyManagerTest frameworks-services
  */
 @SmallTest
 public class DevicePolicyManagerTest extends DpmTestBase {
@@ -2010,7 +2013,7 @@
         // UnfinishedVerificationException.
     }
 
-    public void setup_DeviceAdminFeatureOff() throws Exception {
+    private void setup_DeviceAdminFeatureOff() throws Exception {
         when(mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN))
                 .thenReturn(false);
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
@@ -2026,6 +2029,8 @@
 
     public void testIsProvisioningAllowed_DeviceAdminFeatureOff() throws Exception {
         setup_DeviceAdminFeatureOff();
+        mContext.packageName = admin1.getPackageName();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, false);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
@@ -2047,7 +2052,7 @@
                 DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED);
     }
 
-    public void setup_ManagedProfileFeatureOff() throws Exception {
+    private void setup_ManagedProfileFeatureOff() throws Exception {
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(false);
         initializeDpms();
@@ -2061,6 +2066,8 @@
 
     public void testIsProvisioningAllowed_ManagedProfileFeatureOff() throws Exception {
         setup_ManagedProfileFeatureOff();
+        mContext.packageName = admin1.getPackageName();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
@@ -2102,7 +2109,7 @@
                 DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED);
     }
 
-    public void setup_nonSplitUser_firstBoot_primaryUser() throws Exception {
+    private void setup_nonSplitUser_firstBoot_primaryUser() throws Exception {
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(true);
         when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false);
@@ -2115,6 +2122,8 @@
 
     public void testIsProvisioningAllowed_nonSplitUser_firstBoot_primaryUser() throws Exception {
         setup_nonSplitUser_firstBoot_primaryUser();
+        mContext.packageName = admin1.getPackageName();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
@@ -2138,7 +2147,7 @@
                 DevicePolicyManager.CODE_NOT_SYSTEM_USER_SPLIT);
     }
 
-    public void setup_nonSplitUser_afterDeviceSetup_primaryUser() throws Exception {
+    private void setup_nonSplitUser_afterDeviceSetup_primaryUser() throws Exception {
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(true);
         when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false);
@@ -2152,6 +2161,8 @@
     public void testIsProvisioningAllowed_nonSplitUser_afterDeviceSetup_primaryUser()
             throws Exception {
         setup_nonSplitUser_afterDeviceSetup_primaryUser();
+        mContext.packageName = admin1.getPackageName();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 false/* because of completed device setup */);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
@@ -2176,7 +2187,88 @@
                 DevicePolicyManager.CODE_NOT_SYSTEM_USER_SPLIT);
     }
 
-    public void setup_splitUser_firstBoot_systemUser() throws Exception {
+    public void testIsProvisioningAllowed_nonSplitUser_withDo_primaryUser() throws Exception {
+        setDeviceOwner();
+        setup_nonSplitUser_afterDeviceSetup_primaryUser();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
+        mContext.packageName = admin1.getPackageName();
+
+        // COMP mode is allowed.
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+
+        when(mContext.userManager.hasUserRestriction(
+                eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
+                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+                .thenReturn(true);
+
+        // The DO should be allowed to initiate provisioning if it set the restriction itself.
+        when(mContext.userManager.getUserRestrictionSource(
+                eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
+                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+
+        // The DO should not be allowed to initiate provisioning if the restriction is set by
+        // another entity.
+        when(mContext.userManager.getUserRestrictionSource(
+                eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
+                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+    }
+
+    public void
+    testCheckProvisioningPreCondition_nonSplitUser_withDo_primaryUser() throws Exception {
+        setDeviceOwner();
+        setup_nonSplitUser_afterDeviceSetup_primaryUser();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
+                DevicePolicyManager.CODE_HAS_DEVICE_OWNER);
+
+        // COMP mode is allowed.
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+                DevicePolicyManager.CODE_OK);
+
+        // And other DPCs can also provisioning a managed profile (DO + BYOD case).
+        assertCheckProvisioningPreCondition(
+                DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+                "some.other.dpc.package.name",
+                DevicePolicyManager.CODE_OK);
+
+        when(mContext.userManager.hasUserRestriction(
+                eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
+                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+                .thenReturn(true);
+
+        // The DO should be allowed to initiate provisioning if it set the restriction itself, but
+        // other packages should be forbidden.
+        when(mContext.userManager.getUserRestrictionSource(
+                eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
+                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+                DevicePolicyManager.CODE_OK);
+        assertCheckProvisioningPreCondition(
+                DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+                "some.other.dpc.package.name",
+                DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED);
+
+        // The DO should not be allowed to initiate provisioning if the restriction is set by
+        // another entity.
+        when(mContext.userManager.getUserRestrictionSource(
+                eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
+                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+                DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED);
+                assertCheckProvisioningPreCondition(
+                DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+                "some.other.dpc.package.name",
+                DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED);
+    }
+
+    private void setup_splitUser_firstBoot_systemUser() throws Exception {
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(true);
         when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
@@ -2189,6 +2281,8 @@
 
     public void testIsProvisioningAllowed_splitUser_firstBoot_systemUser() throws Exception {
         setup_splitUser_firstBoot_systemUser();
+        mContext.packageName = admin1.getPackageName();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 false /* because canAddMoreManagedProfiles returns false */);
@@ -2213,7 +2307,7 @@
                 DevicePolicyManager.CODE_SYSTEM_USER);
     }
 
-    public void setup_splitUser_afterDeviceSetup_systemUser() throws Exception {
+    private void setup_splitUser_afterDeviceSetup_systemUser() throws Exception {
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(true);
         when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
@@ -2226,6 +2320,8 @@
 
     public void testIsProvisioningAllowed_splitUser_afterDeviceSetup_systemUser() throws Exception {
         setup_splitUser_afterDeviceSetup_systemUser();
+        mContext.packageName = admin1.getPackageName();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 true/* it's undefined behavior. Can be changed into false in the future */);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
@@ -2251,7 +2347,7 @@
                 DevicePolicyManager.CODE_SYSTEM_USER);
     }
 
-    public void setup_splitUser_firstBoot_primaryUser() throws Exception {
+    private void setup_splitUser_firstBoot_primaryUser() throws Exception {
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(true);
         when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
@@ -2264,6 +2360,8 @@
 
     public void testIsProvisioningAllowed_splitUser_firstBoot_primaryUser() throws Exception {
         setup_splitUser_firstBoot_primaryUser();
+        mContext.packageName = admin1.getPackageName();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
@@ -2286,7 +2384,7 @@
                 DevicePolicyManager.CODE_OK);
     }
 
-    public void setup_splitUser_afterDeviceSetup_primaryUser() throws Exception {
+    private void setup_splitUser_afterDeviceSetup_primaryUser() throws Exception {
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(true);
         when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
@@ -2300,6 +2398,8 @@
     public void testIsProvisioningAllowed_splitUser_afterDeviceSetup_primaryUser()
             throws Exception {
         setup_splitUser_afterDeviceSetup_primaryUser();
+        mContext.packageName = admin1.getPackageName();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 true/* it's undefined behavior. Can be changed into false in the future */);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
@@ -2324,7 +2424,7 @@
                 DevicePolicyManager.CODE_USER_SETUP_COMPLETED);
     }
 
-    public void setup_provisionManagedProfileWithDeviceOwner_systemUser() throws Exception {
+    private void setup_provisionManagedProfileWithDeviceOwner_systemUser() throws Exception {
         setDeviceOwner();
 
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
@@ -2340,6 +2440,8 @@
     public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_systemUser()
             throws Exception {
         setup_provisionManagedProfileWithDeviceOwner_systemUser();
+        mContext.packageName = admin1.getPackageName();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 false /* can't provision managed profile on system user */);
     }
@@ -2368,6 +2470,8 @@
     public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser()
             throws Exception {
         setup_provisionManagedProfileWithDeviceOwner_primaryUser();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
+        mContext.packageName = admin1.getPackageName();
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
     }
 
@@ -2375,6 +2479,8 @@
             throws Exception {
         setup_provisionManagedProfileWithDeviceOwner_primaryUser();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        // COMP mode is allowed.
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_OK);
     }
@@ -2386,8 +2492,8 @@
                 .thenReturn(true);
         when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
         when(mContext.userManager.hasUserRestriction(
-                UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
-                UserHandle.of(DpmMockContext.CALLER_USER_HANDLE)))
+                eq(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE),
+                eq(UserHandle.of(DpmMockContext.CALLER_USER_HANDLE))))
                 .thenReturn(true);
         when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
                 false /* we can't remove a managed profile */)).thenReturn(false);
@@ -2401,6 +2507,8 @@
     public void testIsProvisioningAllowed_provisionManagedProfileCantRemoveUser_primaryUser()
             throws Exception {
         setup_provisionManagedProfileCantRemoveUser_primaryUser();
+        mContext.packageName = admin1.getPackageName();
+        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
     }
 
@@ -2415,7 +2523,8 @@
     public void testCheckProvisioningPreCondition_permission() {
         // GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted
         try {
-            dpm.checkProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE);
+            dpm.checkProvisioningPreCondition(
+                    DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, "some.package");
             fail("Didn't throw SecurityException");
         } catch (SecurityException expected) {
         }
@@ -2820,8 +2929,14 @@
     }
 
     private void assertCheckProvisioningPreCondition(String action, int provisioningCondition) {
-        assertEquals("checkProvisioningPreCondition(" + action + ") returning unexpected result",
-                provisioningCondition, dpm.checkProvisioningPreCondition(action));
+        assertCheckProvisioningPreCondition(action, admin1.getPackageName(), provisioningCondition);
+    }
+
+    private void assertCheckProvisioningPreCondition(
+            String action, String packageName, int provisioningCondition) {
+        assertEquals("checkProvisioningPreCondition("
+                        + action + ", " + packageName + ") returning unexpected result",
+                provisioningCondition, dpm.checkProvisioningPreCondition(action, packageName));
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index db27f72..8a11976 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -82,6 +82,10 @@
                 eq(packageName),
                 eq(0),
                 eq(userId));
+
+        doReturn(ai.uid).when(mMockContext.packageManager).getPackageUidAsUser(
+                eq(packageName),
+                eq(userId));
     }
 
     protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid)
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 939a3b8..8f2a44c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1154,6 +1154,17 @@
     public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL =
         "support_3gpp_call_forwarding_while_roaming_bool";
 
+    /**
+     * Determine whether user edited tether APN (type dun) has effect
+     * {@code false} - Default. APN with dun type in telephony database has no effect.
+     *
+     * {@code true}  - DUN APN added/edited in ApnEditor will be used for tethering data call.
+     *
+     * @hide
+     */
+    public static final String KEY_EDITABLE_TETHER_APN_BOOL =
+            "editable_tether_apn_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1358,6 +1369,7 @@
         sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
         sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
         sDefaults.putBoolean(KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
+        sDefaults.putBoolean(KEY_EDITABLE_TETHER_APN_BOOL, false);
     }
 
     /**
diff --git a/tools/layoutlib/.idea/modules.xml b/tools/layoutlib/.idea/modules.xml
index 9bdc381..6ffc1cc 100644
--- a/tools/layoutlib/.idea/modules.xml
+++ b/tools/layoutlib/.idea/modules.xml
@@ -4,6 +4,7 @@
     <modules>
       <module fileurl="file://$PROJECT_DIR$/bridge/bridge.iml" filepath="$PROJECT_DIR$/bridge/bridge.iml" />
       <module fileurl="file://$PROJECT_DIR$/create/create.iml" filepath="$PROJECT_DIR$/create/create.iml" />
+      <module fileurl="file://$PROJECT_DIR$/legacy/legacy.iml" filepath="$PROJECT_DIR$/legacy/legacy.iml" />
       <module fileurl="file://$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" filepath="$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" />
     </modules>
   </component>
diff --git a/tools/layoutlib/legacy/Android.mk b/tools/layoutlib/legacy/Android.mk
new file mode 100644
index 0000000..5855f89
--- /dev/null
+++ b/tools/layoutlib/legacy/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2008 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)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_JAVA_LIBRARIES := \
+	layoutlib_api-prebuilt
+
+LOCAL_MODULE := layoutlib-legacy
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/tools/layoutlib/legacy/legacy.iml b/tools/layoutlib/legacy/legacy.iml
new file mode 100644
index 0000000..a167a75
--- /dev/null
+++ b/tools/layoutlib/legacy/legacy.iml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/tools/layoutlib/legacy/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/legacy/src/com/android/layoutlib/bridge/Bridge.java
new file mode 100644
index 0000000..0cfc181
--- /dev/null
+++ b/tools/layoutlib/legacy/src/com/android/layoutlib/bridge/Bridge.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.Result.Status;
+import com.android.ide.common.rendering.api.SessionParams;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+/**
+ * Legacy Bridge used in the SDK version of layoutlib
+ */
+public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
+    private static final String SDK_NOT_SUPPORTED = "The SDK layoutlib version is not supported";
+    private static final Result NOT_SUPPORTED_RESULT =
+            Status.NOT_IMPLEMENTED.createResult(SDK_NOT_SUPPORTED);
+    private static BufferedImage sImage;
+
+    private static class BridgeRenderSession extends RenderSession {
+
+        @Override
+        public synchronized BufferedImage getImage() {
+            if (sImage == null) {
+                sImage = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
+                Graphics2D g = sImage.createGraphics();
+                g.clearRect(0, 0, 500, 500);
+                g.drawString(SDK_NOT_SUPPORTED, 20, 20);
+                g.dispose();
+            }
+
+            return sImage;
+        }
+
+        @Override
+        public Result render(long timeout, boolean forceMeasure) {
+            return NOT_SUPPORTED_RESULT;
+        }
+
+        @Override
+        public Result measure(long timeout) {
+            return NOT_SUPPORTED_RESULT;
+        }
+
+        @Override
+        public Result getResult() {
+            return NOT_SUPPORTED_RESULT;
+        }
+    }
+
+
+    @Override
+    public RenderSession createSession(SessionParams params) {
+        return new BridgeRenderSession();
+    }
+
+    @Override
+    public int getApiLevel() {
+        return 0;
+    }
+}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
index 21b7a04..84ec8b7 100644
--- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
+++ b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
@@ -171,6 +171,9 @@
                     arg1.getDevice().getSyncService().pullFile(arg0,
                             target.getAbsoluteFile().toString(), new NullProgressMonitor());
                 } catch (Exception e) {
+                    if (target != null) {
+                        target.delete();
+                    }
                     e.printStackTrace();
                     target = null;
                 }
@@ -189,6 +192,9 @@
                     out.write(arg0);
                     out.close();
                 } catch (Exception e) {
+                    if (target != null) {
+                        target.delete();
+                    }
                     e.printStackTrace();
                     target = null;
                 }
@@ -215,6 +221,8 @@
             return analyzeHprof(hprofLocalFile);
         } catch (Exception e) {
             throw new RuntimeException(e);
+        } finally {
+            hprofLocalFile.delete();
         }
     }
 }