Merge "Show Search button in menu of Wifi Preferences page."
diff --git a/res/layout/dialog_hardware_info.xml b/res/layout/dialog_hardware_info.xml
new file mode 100644
index 0000000..f9d52b8
--- /dev/null
+++ b/res/layout/dialog_hardware_info.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:padding="24dp">
+
+        <TextView
+            android:id="@+id/model_label"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="@android:style/TextAppearance.Material.Body1"
+            android:textColor="?android:attr/textColorSecondary"
+            android:text="@string/model_info" />
+        <TextView
+            android:id="@+id/model_value"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingBottom="24dp"
+            android:textAppearance="@android:style/TextAppearance.Material.Body2" />
+
+        <TextView
+            android:id="@+id/hardware_rev_label"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="@android:style/TextAppearance.Material.Body1"
+            android:textColor="?android:attr/textColorSecondary"
+            android:text="@string/hardware_revision" />
+        <TextView
+            android:id="@+id/hardware_rev_value"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingBottom="24dp"
+            android:textAppearance="@android:style/TextAppearance.Material.Body2" />
+
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/res/layout/vpn_dialog.xml b/res/layout/vpn_dialog.xml
index 6822e95..61b0861 100644
--- a/res/layout/vpn_dialog.xml
+++ b/res/layout/vpn_dialog.xml
@@ -20,7 +20,7 @@
     <LinearLayout android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical"
-            android:padding="8dp">
+            android:padding="24dp">
 
         <LinearLayout android:id="@+id/editor"
                 android:layout_width="match_parent"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4af9cc8..c9a1bf7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2364,6 +2364,10 @@
     <string name="security_patch">Android security patch level</string>
     <!-- About phone screen, status item label  [CHAR LIMIT=40] -->
     <string name="model_info">Model</string>
+    <!-- About phone screen, dialog title for showing hardware information such as model, serial number, etc.[CHAR LIMIT=60] -->
+    <string name="hardware_info">Model &amp; hardware</string>
+    <!-- Label for device's hardware revision value [CHAR LIMIT=40] -->
+    <string name="hardware_revision">Hardware version</string>
     <!-- About phone screen, fcc equipment id label  [CHAR LIMIT=40] -->
     <string name="fcc_equipment_id">Equipment ID</string>
     <!-- About phone screen,  setting option name  [CHAR LIMIT=40] -->
@@ -3755,7 +3759,9 @@
     <!-- On Text & language settings screen, setting summary for the Auto-punctuate setting. -->
     <string name="auto_punctuate_summary">Press Space key twice to insert \u0022.\u0022</string>
     <!-- On Security & location settings screen, setting check box name. Title of the checkbox to set whether password edit fields will show the most recent character typed and then hide it, or just hide it right away.  By hide, I mean mask it out. -->
-    <string name="show_password">Make passwords visible</string>
+    <string name="show_password">Show passwords</string>
+    <!-- On Security & location settings screen. This is a short summary text describing what "Show passwords" setting does -->
+    <string name="show_password_summary">Display characters briefly as you type</string>
     <!-- Warning message about security implications of enabling an input method, displayed as a dialog
          message when the user selects to enable an IME. -->
     <string name="ime_security_warning">This input method may be able to collect
diff --git a/res/values/themes.xml b/res/values/themes.xml
index c961cce..d6df2ab 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -216,7 +216,6 @@
     </style>
 
     <style name="Theme.ConfirmDeviceCredentials" parent="Theme.SubSettings">
-        <item name="android:windowLightStatusBar">false</item>
         <item name="confirmDeviceCredentialsSideMargin">@dimen/confirm_credentials_side_margin</item>
         <item name="confirmDeviceCredentialsTopMargin">@dimen/confirm_credentials_top_margin</item>
     </style>
diff --git a/res/xml/app_storage_settings.xml b/res/xml/app_storage_settings.xml
index 0254b13..6bd8ae3 100644
--- a/res/xml/app_storage_settings.xml
+++ b/res/xml/app_storage_settings.xml
@@ -51,23 +51,11 @@
             android:layout="@layout/horizontal_preference" />
 
         <Preference
-            android:key="external_code_size"
-            android:title="@string/external_code_size_label"
-            android:selectable="false"
-            android:layout="@layout/horizontal_preference" />
-
-        <Preference
             android:key="data_size"
             android:title="@string/data_size_label"
             android:selectable="false"
             android:layout="@layout/horizontal_preference" />
 
-        <Preference
-            android:key="external_data_size"
-            android:title="@string/external_data_size_label"
-            android:selectable="false"
-            android:layout="@layout/horizontal_preference" />
-
         <com.android.settings.applications.LayoutPreference
             android:key="clear_data_button"
             android:selectable="false"
diff --git a/res/xml/installed_app_details_ia.xml b/res/xml/installed_app_details_ia.xml
index 5b9bdc7..3e06e39 100644
--- a/res/xml/installed_app_details_ia.xml
+++ b/res/xml/installed_app_details_ia.xml
@@ -23,6 +23,12 @@
         android:selectable="false"
         android:order="-10000"/>
 
+    <com.android.settings.applications.LayoutPreference
+      android:key="action_buttons"
+      android:layout="@layout/app_action_buttons"
+      android:selectable="false"
+      android:order="-9999"/>
+
     <Preference
         android:key="notification_settings"
         android:title="@string/notifications_label"
diff --git a/res/xml/security_settings_misc.xml b/res/xml/security_settings_misc.xml
index 544769d..04272df 100644
--- a/res/xml/security_settings_misc.xml
+++ b/res/xml/security_settings_misc.xml
@@ -26,8 +26,10 @@
             <intent android:action="android.settings.LOCATION_SOURCE_SETTINGS"/>
         </Preference>
 
-        <SwitchPreference android:key="show_password"
-                android:title="@string/show_password"/>
+        <SwitchPreference
+            android:key="show_password"
+            android:title="@string/show_password"
+            android:summary="@string/show_password_summary"/>
 
     </PreferenceCategory>
 
diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java
index 017a348..3e9304d 100644
--- a/src/com/android/settings/ChooseLockGeneric.java
+++ b/src/com/android/settings/ChooseLockGeneric.java
@@ -166,16 +166,6 @@
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
             mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
-            if (mIsSetNewPassword) {
-                // In ACTION_SET_NEW_PARENT_PROFILE_PASSWORD or ACTION_SET_NEW_PASSWORD, the user
-                // will be asked to confirm the password if one has been set.
-                // On fingerprint supported device, fingerprint options are represented in the
-                // options. If the user chooses to skip fingerprint setup, ChooseLockGeneric is
-                // relaunched to only show options without fingerprint. In this case, we shouldn't
-                // ask the user to confirm the password again.
-                mPasswordConfirmed = getActivity().getIntent().getBooleanExtra(
-                        PASSWORD_CONFIRMED, false);
-            }
 
             if (savedInstanceState != null) {
                 mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
@@ -246,11 +236,12 @@
                 showFactoryResetProtectionWarningDialog(key);
                 return true;
             } else if (KEY_SKIP_FINGERPRINT.equals(key)) {
-                Intent chooseLockGenericIntent = new Intent(getActivity(), ChooseLockGeneric.class);
+                Intent chooseLockGenericIntent = new Intent(getActivity(),
+                    ChooseLockGeneric.InternalActivity.class);
                 chooseLockGenericIntent.setAction(getIntent().getAction());
                 // Forward the target user id to  ChooseLockGeneric.
                 chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
-                chooseLockGenericIntent.putExtra(PASSWORD_CONFIRMED, mPasswordConfirmed);
+                chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed);
                 startActivityForResult(chooseLockGenericIntent, SKIP_FINGERPRINT_REQUEST);
                 return true;
             } else {
diff --git a/src/com/android/settings/DeviceInfoSettings.java b/src/com/android/settings/DeviceInfoSettings.java
index 89ba999..e9d3f86 100644
--- a/src/com/android/settings/DeviceInfoSettings.java
+++ b/src/com/android/settings/DeviceInfoSettings.java
@@ -128,7 +128,7 @@
         controllers.add(new BasebandVersionPreferenceController(context));
         controllers.add(new FirmwareVersionPreferenceController(context, lifecycle));
         controllers.add(new RegulatoryInfoPreferenceController(context));
-        controllers.add(new DeviceModelPreferenceController(context));
+        controllers.add(new DeviceModelPreferenceController(context, fragment));
         controllers.add(new SecurityPatchPreferenceController(context));
         controllers.add(new FccEquipmentIdPreferenceController(context));
         controllers.add(new SELinuxStatusPreferenceController(context));
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index 1bd7416..fb590cc 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -521,6 +521,14 @@
         return result;
     }
 
+    private static CharSequence getActiveTrustAgentLabel(Context context,
+            TrustAgentManager trustAgentManager, LockPatternUtils utils,
+            DevicePolicyManager dpm) {
+        ArrayList<TrustAgentComponentInfo> agents = getActiveTrustAgents(context,
+                trustAgentManager, utils, dpm);
+        return agents.isEmpty() ? null : agents.get(0).title;
+    }
+
     @Override
     public void onGearClick(GearPreference p) {
         if (KEY_UNLOCK_SET_OR_CHANGE.equals(p.getKey())) {
@@ -908,6 +916,7 @@
         private SwitchPreference mPowerButtonInstantlyLocks;
         private RestrictedPreference mOwnerInfoPref;
 
+        private TrustAgentManager mTrustAgentManager;
         private LockPatternUtils mLockPatternUtils;
         private DevicePolicyManager mDPM;
 
@@ -919,6 +928,9 @@
         @Override
         public void onCreate(Bundle icicle) {
             super.onCreate(icicle);
+            SecurityFeatureProvider securityFeatureProvider =
+                    FeatureFactory.getFactory(getActivity()).getSecurityFeatureProvider();
+            mTrustAgentManager = securityFeatureProvider.getTrustAgentManager();
             mLockPatternUtils = new LockPatternUtils(getContext());
             mDPM = getContext().getSystemService(DevicePolicyManager.class);
             createPreferenceHierarchy();
@@ -973,13 +985,12 @@
             // lock instantly on power key press
             mPowerButtonInstantlyLocks = (SwitchPreference) findPreference(
                     KEY_POWER_INSTANTLY_LOCKS);
-            Preference trustAgentPreference = findPreference(KEY_TRUST_AGENT);
-            if (mPowerButtonInstantlyLocks != null &&
-                    trustAgentPreference != null &&
-                    trustAgentPreference.getTitle().length() > 0) {
+            CharSequence trustAgentLabel = getActiveTrustAgentLabel(getContext(),
+                    mTrustAgentManager, mLockPatternUtils, mDPM);
+            if (mPowerButtonInstantlyLocks != null && !TextUtils.isEmpty(trustAgentLabel)) {
                 mPowerButtonInstantlyLocks.setSummary(getString(
                         R.string.lockpattern_settings_power_button_instantly_locks_summary,
-                        trustAgentPreference.getTitle()));
+                        trustAgentLabel));
             }
 
             mOwnerInfoPref = (RestrictedPreference) findPreference(KEY_OWNER_INFO_SETTINGS);
@@ -1048,14 +1059,15 @@
                     }
                 }
 
-                Preference preference = findPreference(KEY_TRUST_AGENT);
-                if (preference != null && preference.getTitle().length() > 0) {
+                CharSequence trustAgentLabel = getActiveTrustAgentLabel(getContext(),
+                        mTrustAgentManager, mLockPatternUtils, mDPM);
+                if (!TextUtils.isEmpty(trustAgentLabel)) {
                     if (Long.valueOf(values[best].toString()) == 0) {
                         summary = getString(R.string.lock_immediately_summary_with_exception,
-                                preference.getTitle());
+                                trustAgentLabel);
                     } else {
                         summary = getString(R.string.lock_after_timeout_summary_with_exception,
-                                entries[best], preference.getTitle());
+                                entries[best], trustAgentLabel);
                     }
                 } else {
                     summary = getString(R.string.lock_after_timeout_summary, entries[best]);
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index 32c8d32..0ea9515 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -105,12 +105,6 @@
     private static final String KEY_URI_CATEGORY = "uri_category";
     private static final String KEY_CLEAR_URI = "clear_uri_button";
 
-    private Preference mTotalSize;
-    private Preference mAppSize;
-    private Preference mDataSize;
-    private Preference mExternalCodeSize;
-    private Preference mExternalDataSize;
-
     // Views related to cache info
     private Preference mCacheSize;
     private Button mClearDataButton;
@@ -125,15 +119,9 @@
     private PreferenceCategory mUri;
 
     private boolean mCanClearData = true;
-    private boolean mHaveSizes = false;
 
     private AppStorageStats mLastResult;
-    private long mLastCodeSize = -1;
-    private long mLastDataSize = -1;
-    private long mLastExternalCodeSize = -1;
-    private long mLastExternalDataSize = -1;
-    private long mLastCacheSize = -1;
-    private long mLastTotalSize = -1;
+    private AppStorageSizesController mSizeController;
 
     private ClearCacheObserver mClearCacheObserver;
     private ClearUserDataObserver mClearDataObserver;
@@ -166,17 +154,15 @@
         mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
 
         // Set default values on sizes
-        mTotalSize = findPreference(KEY_TOTAL_SIZE);
-        mAppSize =  findPreference(KEY_APP_SIZE);
-        mDataSize =  findPreference(KEY_DATA_SIZE);
-        mExternalCodeSize = findPreference(KEY_EXTERNAL_CODE_SIZE);
-        mExternalDataSize = findPreference(KEY_EXTERNAL_DATA_SIZE);
+        mSizeController = new AppStorageSizesController.Builder()
+                .setTotalSizePreference(findPreference(KEY_TOTAL_SIZE))
+                .setAppSizePreference(findPreference(KEY_APP_SIZE))
+                .setDataSizePreference(findPreference(KEY_DATA_SIZE))
+                .setCacheSizePreference(findPreference(KEY_CACHE_SIZE))
+                .setComputingString(R.string.computing_size)
+                .setErrorString(R.string.invalid_size_value)
+                .build();
 
-        if (Environment.isExternalStorageEmulated()) {
-            PreferenceCategory category = (PreferenceCategory) findPreference(KEY_STORAGE_CATEGORY);
-            category.removePreference(mExternalCodeSize);
-            category.removePreference(mExternalDataSize);
-        }
         mClearDataButton = (Button) ((LayoutPreference) findPreference(KEY_CLEAR_DATA))
                 .findViewById(R.id.button);
 
@@ -265,13 +251,6 @@
         dialog.dismiss();
     }
 
-    private String getSizeStr(long size) {
-        if (size == SIZE_INVALID) {
-            return mInvalidSizeStr.toString();
-        }
-        return Formatter.formatFileSize(getActivity(), size);
-    }
-
     @Override
     protected boolean refreshUi() {
         retrieveAppEntry();
@@ -521,7 +500,7 @@
 
     @Override
     public void onLoadFinished(Loader<AppStorageStats> loader, AppStorageStats result) {
-        mLastResult = result;
+        mSizeController.setResult(result);
         updateUiWithSize(result);
     }
 
@@ -545,39 +524,15 @@
     }
 
     private void updateUiWithSize(AppStorageStats result) {
+        mSizeController.updateUi(getContext());
+
         if (result == null) {
-            mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1;
-            if (!mHaveSizes) {
-                mAppSize.setSummary(mComputingStr);
-                mDataSize.setSummary(mComputingStr);
-                mCacheSize.setSummary(mComputingStr);
-                mTotalSize.setSummary(mComputingStr);
-            }
             mClearDataButton.setEnabled(false);
             mClearCacheButton.setEnabled(false);
         } else {
-            mHaveSizes = true;
             long codeSize = result.getCodeBytes();
             long dataSize = result.getDataBytes();
-            if (mLastCodeSize != codeSize) {
-                mLastCodeSize = codeSize;
-                mAppSize.setSummary(getSizeStr(codeSize));
-            }
-            if (mLastDataSize != dataSize) {
-                mLastDataSize = dataSize;
-                mDataSize.setSummary(getSizeStr(dataSize));
-            }
             long cacheSize = result.getCacheBytes();
-            if (mLastCacheSize != cacheSize) {
-                mLastCacheSize = cacheSize;
-                mCacheSize.setSummary(getSizeStr(cacheSize));
-            }
-
-            long totalSize = codeSize + dataSize + cacheSize;
-            if (mLastTotalSize != totalSize) {
-                mLastTotalSize = totalSize;
-                mTotalSize.setSummary(getSizeStr(totalSize));
-            }
 
             if (dataSize <= 0 || !mCanClearData) {
                 mClearDataButton.setEnabled(false);
diff --git a/src/com/android/settings/applications/AppStorageSizesController.java b/src/com/android/settings/applications/AppStorageSizesController.java
new file mode 100644
index 0000000..bc8f680
--- /dev/null
+++ b/src/com/android/settings/applications/AppStorageSizesController.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settings.applications;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.v7.preference.Preference;
+import android.text.format.Formatter;
+
+import com.android.internal.util.Preconditions;
+import com.android.settingslib.applications.StorageStatsSource;
+
+/**
+ * Handles setting the sizes for the app info screen.
+ */
+public class AppStorageSizesController {
+    private final Preference mTotalSize;
+    private final Preference mAppSize;
+    private final Preference mDataSize;
+    private final Preference mCacheSize;
+    private final @StringRes int mComputing;
+    private final @StringRes int mError;
+
+    @Nullable
+    private StorageStatsSource.AppStorageStats mLastResult;
+    private boolean mLastResultFailed;
+    private long mLastCodeSize = -1;
+    private long mLastDataSize = -1;
+    private long mLastCacheSize = -1;
+    private long mLastTotalSize = -1;
+
+    private AppStorageSizesController(Preference total, Preference app,
+            Preference data, Preference cache, @StringRes int computing, @StringRes int error) {
+        mTotalSize = total;
+        mAppSize = app;
+        mDataSize = data;
+        mCacheSize = cache;
+        mComputing = computing;
+        mError = error;
+    }
+
+    /**
+     * Updates the UI using storage stats.
+     * @param context Context to use to fetch strings
+     */
+    public void updateUi(Context context) {
+        if (mLastResult == null) {
+            int errorRes = mLastResultFailed ? mError : mComputing;
+
+            mAppSize.setSummary(errorRes);
+            mDataSize.setSummary(errorRes);
+            mCacheSize.setSummary(errorRes);
+            mTotalSize.setSummary(errorRes);
+        } else {
+            long codeSize = mLastResult.getCodeBytes();
+            long dataSize = mLastResult.getDataBytes();
+            if (mLastCodeSize != codeSize) {
+                mLastCodeSize = codeSize;
+                mAppSize.setSummary(getSizeStr(context, codeSize));
+            }
+            if (mLastDataSize != dataSize) {
+                mLastDataSize = dataSize;
+                mDataSize.setSummary(getSizeStr(context, dataSize));
+            }
+            long cacheSize = mLastResult.getCacheBytes();
+            if (mLastCacheSize != cacheSize) {
+                mLastCacheSize = cacheSize;
+                mCacheSize.setSummary(getSizeStr(context, cacheSize));
+            }
+
+            long totalSize = codeSize + dataSize + cacheSize;
+            if (mLastTotalSize != totalSize) {
+                mLastTotalSize = totalSize;
+                mTotalSize.setSummary(getSizeStr(context, totalSize));
+            }
+        }
+    }
+
+    /**
+     * Sets a result for the controller to use to update the UI.
+     * @param result A result for the UI. If null, count as a failed calculation.
+     */
+    public void setResult(StorageStatsSource.AppStorageStats result) {
+        mLastResult = result;
+        mLastResultFailed = result == null;
+    }
+
+    private String getSizeStr(Context context, long size) {
+        return Formatter.formatFileSize(context, size);
+    }
+
+    public static class Builder {
+        private Preference mTotalSize;
+        private Preference mAppSize;
+        private Preference mDataSize;
+        private Preference mCacheSize;
+        private @StringRes int mComputing;
+        private @StringRes int mError;
+
+        public Builder setAppSizePreference(Preference preference) {
+            mAppSize = preference;
+            return this;
+        }
+
+        public Builder setDataSizePreference(Preference preference) {
+            mDataSize = preference;
+            return this;
+        }
+
+        public Builder setCacheSizePreference(Preference preference) {
+            mCacheSize = preference;
+            return this;
+        }
+
+        public Builder setTotalSizePreference(Preference preference) {
+            mTotalSize = preference;
+            return this;
+        }
+
+        public Builder setComputingString(@StringRes int sequence) {
+            mComputing = sequence;
+            return this;
+        }
+
+        public Builder setErrorString(@StringRes int sequence) {
+            mError = sequence;
+            return this;
+        }
+
+        public AppStorageSizesController build() {
+            return new AppStorageSizesController(
+                    Preconditions.checkNotNull(mTotalSize),
+                    Preconditions.checkNotNull(mAppSize),
+                    Preconditions.checkNotNull(mDataSize),
+                    Preconditions.checkNotNull(mCacheSize),
+                    mComputing,
+                    mError);
+        }
+    }
+}
diff --git a/src/com/android/settings/applications/FetchPackageStorageAsyncLoader.java b/src/com/android/settings/applications/FetchPackageStorageAsyncLoader.java
index 3477299..97e5b7b 100644
--- a/src/com/android/settings/applications/FetchPackageStorageAsyncLoader.java
+++ b/src/com/android/settings/applications/FetchPackageStorageAsyncLoader.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.os.UserHandle;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 import com.android.settings.utils.AsyncLoader;
@@ -30,6 +31,7 @@
  * Fetches the storage stats using the StorageStatsManager for a given package and user tuple.
  */
 public class FetchPackageStorageAsyncLoader extends AsyncLoader<AppStorageStats> {
+    private static final String TAG = "FetchPackageStorage";
     private final StorageStatsSource mSource;
     private final ApplicationInfo mInfo;
     private final UserHandle mUser;
@@ -44,7 +46,13 @@
 
     @Override
     public AppStorageStats loadInBackground() {
-        return mSource.getStatsForPackage(mInfo.volumeUuid, mInfo.packageName, mUser);
+        AppStorageStats result = null;
+        try {
+            result = mSource.getStatsForPackage(mInfo.volumeUuid, mInfo.packageName, mUser);
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Package may have been removed during query, failing gracefully", e);
+        }
+        return result;
     }
 
     @Override
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index c443e73..da6bbc0 100755
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.applications;
 
-import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
 import android.Manifest.permission;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -68,7 +66,6 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.webkit.IWebViewUpdateService;
 import android.widget.Button;
@@ -115,6 +112,8 @@
 import java.util.HashSet;
 import java.util.List;
 
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
 /**
  * Activity to display application information from Settings. This activity presents
  * extended information associated with a package like code, data, total size, permissions
@@ -148,7 +147,7 @@
     private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3;
 
     private static final String KEY_HEADER = "header_view";
-    private static final String KEY_FOOTER = "header_footer";
+    private static final String KEY_ACTION_BUTTONS = "action_buttons";
     private static final String KEY_NOTIFICATION = "notification_settings";
     private static final String KEY_STORAGE = "storage_settings";
     private static final String KEY_PERMISSION = "permission_settings";
@@ -165,7 +164,7 @@
     private boolean mInitialized;
     private boolean mShowUninstalled;
     private LayoutPreference mHeader;
-    private LayoutPreference mFooter;
+    private LayoutPreference mActionButtons;
     private Button mUninstallButton;
     private boolean mUpdatedSysApp = false;
     private Button mForceStopButton;
@@ -325,13 +324,13 @@
         super.onCreate(icicle);
         final Activity activity = getActivity();
 
+        if (!ensurePackageInfoAvailable(activity)) {
+            return;
+        }
+
         setHasOptionsMenu(true);
         addPreferencesFromResource(R.xml.installed_app_details_ia);
         addDynamicPrefs();
-        mFooter = new LayoutPreference(getPrefContext(), R.layout.app_action_buttons);
-        mFooter.setOrder(-9999);
-        mFooter.setKey(KEY_FOOTER);
-        getPreferenceScreen().addPreference(mFooter);
         if (Utils.isBandwidthControlEnabled()) {
             INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
                     ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
@@ -390,6 +389,7 @@
         }
         final Activity activity = getActivity();
         mHeader = (LayoutPreference) findPreference(KEY_HEADER);
+        mActionButtons = (LayoutPreference) findPreference(KEY_ACTION_BUTTONS);
         FeatureFactory.getFactory(activity)
             .getApplicationFeatureProvider(activity)
             .newAppHeaderController(this, mHeader.findViewById(R.id.app_snippet))
@@ -434,10 +434,27 @@
         refreshUi();
     }
 
+    /**
+     * Ensures the {@link PackageInfo} is available to proceed. If it's not available, the fragment
+     * will finish.
+     *
+     * @return true if packageInfo is available.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    boolean ensurePackageInfoAvailable(Activity activity) {
+        if (mPackageInfo == null) {
+            mFinishing = true;
+            Log.w(LOG_TAG, "Package info not available. Is this package already uninstalled?");
+            activity.finishAndRemoveTask();
+            return false;
+        }
+        return true;
+    }
+
     private void prepareUninstallAndStop() {
-        mForceStopButton = (Button) mFooter.findViewById(R.id.right_button);
+        mForceStopButton = (Button) mActionButtons.findViewById(R.id.right_button);
         mForceStopButton.setText(R.string.force_stop);
-        mUninstallButton = (Button) mFooter.findViewById(R.id.left_button);
+        mUninstallButton = (Button) mActionButtons.findViewById(R.id.left_button);
         mForceStopButton.setEnabled(false);
     }
 
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppInfo.java b/src/com/android/settings/applications/defaultapps/DefaultAppInfo.java
index 96512f5..d276fd2 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAppInfo.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAppInfo.java
@@ -35,8 +35,6 @@
     public final ComponentName componentName;
     public final PackageItemInfo packageItemInfo;
     public final String summary;
-    // Description for why this item is disabled, if null, the item is enabled.
-    public final String disabledDescription;
     public final boolean enabled;
 
     public DefaultAppInfo(int uid, ComponentName cn) {
@@ -52,21 +50,19 @@
         userId = uid;
         componentName = cn;
         this.summary = summary;
-        this.disabledDescription = null;
         this.enabled = enabled;
     }
 
-    public DefaultAppInfo(PackageItemInfo info, String description) {
+    public DefaultAppInfo(PackageItemInfo info, String summary, boolean enabled) {
         userId = UserHandle.myUserId();
         packageItemInfo = info;
         componentName = null;
-        summary = null;
-        this.disabledDescription = description;
-        enabled = true;
+        this.summary = summary;
+        this.enabled = enabled;
     }
 
     public DefaultAppInfo(PackageItemInfo info) {
-        this(info, null);
+        this(info, null /* summary */, true /* enabled */);
     }
 
     public CharSequence loadLabel(PackageManager pm) {
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java b/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java
index e4bcf06..785f7ad 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java
@@ -116,26 +116,9 @@
             screen.addPreference(nonePref);
         }
         for (Map.Entry<String, DefaultAppInfo> app : mCandidates.entrySet()) {
-            final RadioButtonPreference pref = new RadioButtonPreference(getPrefContext());
-            final String appKey = app.getKey();
-            final DefaultAppInfo info = app.getValue();
-            pref.setTitle(info.loadLabel(mPm.getPackageManager()));
-            pref.setIcon(info.loadIcon(mPm.getPackageManager()));
-            pref.setKey(appKey);
-            if (TextUtils.equals(defaultAppKey, appKey)) {
-                pref.setChecked(true);
-            }
-            if (TextUtils.equals(systemDefaultAppKey, appKey)) {
-                pref.setSummary(R.string.system_app);
-            } else if (!TextUtils.isEmpty(info.summary)) {
-                pref.setSummary(info.summary);
-            }
-            if (!TextUtils.isEmpty(app.getValue().disabledDescription)) {
-                pref.setEnabled(false);
-                pref.setSummary(app.getValue().disabledDescription);
-            }
-            pref.setEnabled(info.enabled);
-            pref.setOnClickListener(this);
+            RadioButtonPreference pref = new RadioButtonPreference(getPrefContext());
+            configurePreferenceFromAppInfo(
+                    pref, app.getKey(), app.getValue(), defaultAppKey, systemDefaultAppKey);
             screen.addPreference(pref);
         }
         mayCheckOnlyRadioButton();
@@ -259,4 +242,22 @@
         }
     }
 
+    @VisibleForTesting
+    public RadioButtonPreference configurePreferenceFromAppInfo(RadioButtonPreference pref,
+            String appKey, DefaultAppInfo info, String defaultAppKey, String systemDefaultAppKey) {
+        pref.setTitle(info.loadLabel(mPm.getPackageManager()));
+        pref.setIcon(info.loadIcon(mPm.getPackageManager()));
+        pref.setKey(appKey);
+        if (TextUtils.equals(defaultAppKey, appKey)) {
+            pref.setChecked(true);
+        }
+        if (TextUtils.equals(systemDefaultAppKey, appKey)) {
+            pref.setSummary(R.string.system_app);
+        } else if (!TextUtils.isEmpty(info.summary)) {
+            pref.setSummary(info.summary);
+        }
+        pref.setEnabled(info.enabled);
+        pref.setOnClickListener(this);
+        return pref;
+    }
 }
diff --git a/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java b/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java
index 1b47561..a2c4302 100644
--- a/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java
@@ -15,10 +15,12 @@
  */
 package com.android.settings.deviceinfo;
 
+import android.app.Fragment;
 import android.content.Context;
 import android.os.Build;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
 
 import com.android.settings.core.PreferenceController;
 import com.android.settingslib.DeviceInfoUtils;
@@ -27,8 +29,11 @@
 
     private static final String KEY_DEVICE_MODEL = "device_model";
 
-    public DeviceModelPreferenceController(Context context) {
+    private final Fragment mHost;
+
+    public DeviceModelPreferenceController(Context context, Fragment host) {
         super(context);
+        mHost = host;
     }
 
     @Override
@@ -49,4 +54,14 @@
     public String getPreferenceKey() {
         return KEY_DEVICE_MODEL;
     }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (!TextUtils.equals(preference.getKey(), KEY_DEVICE_MODEL)) {
+            return false;
+        }
+        final HardwareInfoDialogFragment fragment = HardwareInfoDialogFragment.newInstance();
+        fragment.show(mHost.getFragmentManager(), HardwareInfoDialogFragment.TAG);
+        return true;
+    }
 }
diff --git a/src/com/android/settings/deviceinfo/HardwareInfoDialogFragment.java b/src/com/android/settings/deviceinfo/HardwareInfoDialogFragment.java
new file mode 100644
index 0000000..d68b47a
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/HardwareInfoDialogFragment.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.DeviceInfoUtils;
+
+public class HardwareInfoDialogFragment extends InstrumentedDialogFragment {
+
+    public static final String TAG = "HardwareInfo";
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.DIALOG_SETTINGS_HARDWARE_INFO;
+    }
+
+    public static HardwareInfoDialogFragment newInstance() {
+        final HardwareInfoDialogFragment fragment = new HardwareInfoDialogFragment();
+        return fragment;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.hardware_info)
+                .setPositiveButton(android.R.string.ok, null);
+        final View content = LayoutInflater.from(builder.getContext())
+                .inflate(R.layout.dialog_hardware_info, null /* parent */);
+        // Model
+        setText(content, R.id.model_label, R.id.model_value,
+                Build.MODEL + DeviceInfoUtils.getMsvSuffix());
+        // Hardware rev
+        setText(content, R.id.hardware_rev_label, R.id.hardware_rev_value,
+                SystemProperties.get("ro.boot.hardware.revision"));
+
+        return builder.setView(content).create();
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    void setText(View content, int labelViewId, int valueViewId, String value) {
+        if (content == null) {
+            return;
+        }
+        final View labelView = content.findViewById(labelViewId);
+        final TextView valueView = content.findViewById(valueViewId);
+        if (!TextUtils.isEmpty(value)) {
+            labelView.setVisibility(View.VISIBLE);
+            valueView.setVisibility(View.VISIBLE);
+            valueView.setText(value);
+        } else {
+            labelView.setVisibility(View.GONE);
+            valueView.setVisibility(View.GONE);
+        }
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
index a5e8373..41800a2 100644
--- a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
+++ b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
@@ -97,7 +97,7 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         if (mStoragePreference == null) {
-            mStoragePreference = new StorageItemPreferenceAlternate(mContext);
+            mStoragePreference = new StorageItemPreferenceAlternate(screen.getContext());
 
             PreferenceGroup group =
                     (PreferenceGroup) screen.findPreference(TARGET_PREFERENCE_GROUP_KEY);
diff --git a/src/com/android/settings/deviceinfo/storage/UserProfileController.java b/src/com/android/settings/deviceinfo/storage/UserProfileController.java
index 5da9fec..963784d 100644
--- a/src/com/android/settings/deviceinfo/storage/UserProfileController.java
+++ b/src/com/android/settings/deviceinfo/storage/UserProfileController.java
@@ -60,7 +60,7 @@
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
-        mStoragePreference = new StorageItemPreferenceAlternate(mContext);
+        mStoragePreference = new StorageItemPreferenceAlternate(screen.getContext());
         mStoragePreference.setOrder(mPreferenceOrder);
         mStoragePreference.setKey(PREFERENCE_KEY_BASE + mUser.id);
         mStoragePreference.setTitle(mUser.name);
diff --git a/src/com/android/settings/network/NetworkScoreManagerWrapper.java b/src/com/android/settings/network/NetworkScoreManagerWrapper.java
index 0d35378..3489640 100644
--- a/src/com/android/settings/network/NetworkScoreManagerWrapper.java
+++ b/src/com/android/settings/network/NetworkScoreManagerWrapper.java
@@ -56,6 +56,13 @@
         return mNetworkScoreManager.getActiveScorerPackage();
     }
 
+    /**
+     * Returns metadata about the active scorer or <code>null</code> if there is no active scorer.
+     */
+    @Nullable
+    public NetworkScorerAppData getActiveScorer() {
+        return mNetworkScoreManager.getActiveScorer();
+    }
 
     /**
      * Set the active scorer to a new package and clear existing scores.
diff --git a/src/com/android/settings/network/NetworkScorerPicker.java b/src/com/android/settings/network/NetworkScorerPicker.java
index da9d84f..2d04e56 100644
--- a/src/com/android/settings/network/NetworkScorerPicker.java
+++ b/src/com/android/settings/network/NetworkScorerPicker.java
@@ -26,9 +26,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 import com.android.settings.core.InstrumentedPreferenceFragment;
-import com.android.settings.core.instrumentation.Instrumentable;
 import com.android.settings.widget.RadioButtonPreference;
 
 import java.util.List;
@@ -43,8 +43,7 @@
 
     @Override
     public int getMetricsCategory() {
-        //TODO(35854268): Add logging.
-        return Instrumentable.METRICS_CATEGORY_UNKNOWN;
+        return MetricsProto.MetricsEvent.SETTINGS_NETWORK_SCORER;
     }
 
     @Override
diff --git a/src/com/android/settings/network/NetworkScorerPickerPreferenceController.java b/src/com/android/settings/network/NetworkScorerPickerPreferenceController.java
new file mode 100644
index 0000000..6ff6edb
--- /dev/null
+++ b/src/com/android/settings/network/NetworkScorerPickerPreferenceController.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network;
+
+import android.content.Context;
+import android.net.NetworkScorerAppData;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import com.android.settings.R;
+import com.android.settings.core.PreferenceController;
+
+/**
+ * {@link PreferenceController} that shows the active network scorer and toggles the preference
+ * based on {@link Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED}.
+ */
+public class NetworkScorerPickerPreferenceController extends PreferenceController {
+
+    private static final String KEY_NETWORK_SCORER_PICKER = "network_scorer_picker";
+
+    private final NetworkScoreManagerWrapper mNetworkScoreManager;
+
+    public NetworkScorerPickerPreferenceController(Context context,
+            NetworkScoreManagerWrapper networkScoreManager) {
+        super(context);
+        mNetworkScoreManager = networkScoreManager;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_NETWORK_SCORER_PICKER;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) == 1;
+        preference.setEnabled(enabled);
+        if (!enabled) {
+            preference.setSummary(null);
+            return;
+        }
+
+        NetworkScorerAppData scorer = mNetworkScoreManager.getActiveScorer();
+        if (scorer == null) {
+            preference.setSummary(mContext.getString(
+                    R.string.network_scorer_picker_none_preference));
+        } else {
+            preference.setSummary(scorer.getRecommendationServiceLabel());
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+}
diff --git a/src/com/android/settings/vpn2/LegacyVpnPreference.java b/src/com/android/settings/vpn2/LegacyVpnPreference.java
index c1550e2..a4d7221 100644
--- a/src/com/android/settings/vpn2/LegacyVpnPreference.java
+++ b/src/com/android/settings/vpn2/LegacyVpnPreference.java
@@ -34,7 +34,7 @@
 
     LegacyVpnPreference(Context context) {
         super(context, null /* attrs */);
-        setIcon(R.mipmap.ic_launcher_settings);
+        setIcon(R.drawable.ic_settings_24dp);
     }
 
     public VpnProfile getProfile() {
diff --git a/src/com/android/settings/webview/WebViewAppPicker.java b/src/com/android/settings/webview/WebViewAppPicker.java
index aa229ac..586ee36 100644
--- a/src/com/android/settings/webview/WebViewAppPicker.java
+++ b/src/com/android/settings/webview/WebViewAppPicker.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageInfo;
 import android.content.Context;
 import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
@@ -58,7 +59,7 @@
         List<ApplicationInfo> pkgs =
                 getWebViewUpdateServiceWrapper().getValidWebViewApplicationInfos(getContext());
         for (ApplicationInfo ai : pkgs) {
-            packageInfoList.add(new DefaultAppInfo(ai,
+            packageInfoList.add(createDefaultAppInfo(ai,
                       getDisabledReason(getWebViewUpdateServiceWrapper(),
                               getContext(), ai.packageName)));
         }
@@ -92,7 +93,6 @@
         }
     }
 
-
     private WebViewUpdateServiceWrapper createDefaultWebViewUpdateServiceWrapper() {
         return new WebViewUpdateServiceWrapper();
     }
@@ -107,6 +107,13 @@
         return MetricsEvent.WEBVIEW_IMPLEMENTATION;
     }
 
+    @VisibleForTesting
+    DefaultAppInfo createDefaultAppInfo(
+            ApplicationInfo applicationInfo, String disabledReason) {
+        return new DefaultAppInfo(applicationInfo, disabledReason,
+                TextUtils.isEmpty(disabledReason) /* enabled */);
+    }
+
     /**
      * Returns the reason why a package cannot be used as WebView implementation.
      * This is either because of it being disabled, uninstalled, or hidden for any user.
diff --git a/src/com/android/settings/widget/VideoPreference.java b/src/com/android/settings/widget/VideoPreference.java
index c8786dc..e06ef56 100644
--- a/src/com/android/settings/widget/VideoPreference.java
+++ b/src/com/android/settings/widget/VideoPreference.java
@@ -62,6 +62,7 @@
                     .build();
             mMediaPlayer = MediaPlayer.create(mContext, mVideoPath);
             if (mMediaPlayer != null && mMediaPlayer.getDuration() > 0) {
+                setVisible(true);
                 setLayoutResource(R.layout.video_preference);
 
                 mPreviewResource = attributes.getResourceId(
@@ -71,6 +72,8 @@
 
                 mMediaPlayer.setOnPreparedListener(mediaPlayer -> mediaPlayer.setLooping(true));
                 mAnimationAvailable = true;
+            } else {
+                setVisible(false);
             }
         } catch (Exception e) {
             Log.w(TAG, "Animation resource not found. Will not show animation.");
diff --git a/src/com/android/settings/wifi/ConfigureWifiSettings.java b/src/com/android/settings/wifi/ConfigureWifiSettings.java
index 5dbeda2..78e869b 100644
--- a/src/com/android/settings/wifi/ConfigureWifiSettings.java
+++ b/src/com/android/settings/wifi/ConfigureWifiSettings.java
@@ -15,9 +15,11 @@
  */
 package com.android.settings.wifi;
 
+import static android.content.Context.NETWORK_SCORE_SERVICE;
 import static android.content.Context.WIFI_SERVICE;
 
 import android.content.Context;
+import android.net.NetworkScoreManager;
 import android.net.wifi.WifiManager;
 import android.provider.SearchIndexableResource;
 
@@ -26,6 +28,8 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.core.PreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.network.NetworkScoreManagerWrapper;
+import com.android.settings.network.NetworkScorerPickerPreferenceController;
 import com.android.settings.network.WifiCallingPreferenceController;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
@@ -71,6 +75,9 @@
         controllers.add(new CellularFallbackPreferenceController(context));
         controllers.add(new NotifyOpenNetworksPreferenceController(context, getLifecycle()));
         controllers.add(new WifiWakeupPreferenceController(context, getLifecycle()));
+        controllers.add(new NetworkScorerPickerPreferenceController(context,
+                new NetworkScoreManagerWrapper(
+                        (NetworkScoreManager) getSystemService(NETWORK_SCORE_SERVICE))));
         controllers.add(new WifiSleepPolicyPreferenceController(context));
         controllers.add(new WifiP2pPreferenceController(context, getLifecycle(), mWifiManager));
         controllers.add(new WifiCallingPreferenceController(context));
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 9405e8c..8a3c8ac 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -48,7 +48,6 @@
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.Menu;
-import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.ProgressBar;
@@ -93,7 +92,6 @@
 
     /* package */ static final int MENU_ID_WPS_PBC = Menu.FIRST;
     private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1;
-    private static final int MENU_ID_SCAN = Menu.FIRST + 5;
     private static final int MENU_ID_CONNECT = Menu.FIRST + 6;
     private static final int MENU_ID_FORGET = Menu.FIRST + 7;
     private static final int MENU_ID_MODIFY = Menu.FIRST + 8;
@@ -166,8 +164,6 @@
     private Preference mSavedNetworksPreference;
     private LinkablePreference mStatusMessagePreference;
 
-    private MenuItem mScanMenuItem;
-
     // For Search
     private static final String DATA_KEY_REFERENCE = "main_toggle_wifi";
 
@@ -372,26 +368,6 @@
     }
 
     @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        // If the user is not allowed to configure wifi, do not show the menu.
-        if (isUiRestricted()) return;
-
-        addOptionsMenuItems(menu);
-        super.onCreateOptionsMenu(menu, inflater);
-    }
-
-    /**
-     * @param menu
-     */
-    void addOptionsMenuItems(Menu menu) {
-        final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled();
-        mScanMenuItem = menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh)
-                .setIcon(com.android.internal.R.drawable.ic_menu_refresh);
-        mScanMenuItem.setEnabled(wifiIsEnabled)
-                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-    }
-
-    @Override
     public int getMetricsCategory() {
         return MetricsEvent.WIFI;
     }
@@ -443,10 +419,6 @@
             case MENU_ID_WPS_PIN:
                 showDialog(WPS_PIN_DIALOG_ID);
                 return true;
-            case MENU_ID_SCAN:
-                mMetricsFeatureProvider.action(getActivity(), MetricsEvent.ACTION_WIFI_FORCE_SCAN);
-                mWifiTracker.forceScan();
-                return true;
         }
         return super.onOptionsItemSelected(item);
     }
@@ -719,9 +691,6 @@
                     mAccessPointsPreferenceCategory.addPreference(mSeeAllNetworksPreference);
                 }
                 setConfigureWifiSettingsVisibility();
-                if (mScanMenuItem != null) {
-                    mScanMenuItem.setEnabled(true);
-                }
                 break;
 
             case WifiManager.WIFI_STATE_ENABLING:
@@ -739,9 +708,6 @@
                 setOffMessage();
                 setConfigureWifiSettingsVisibility();
                 setProgressBarVisible(false);
-                if (mScanMenuItem != null) {
-                    mScanMenuItem.setEnabled(false);
-                }
                 break;
         }
     }
diff --git a/tests/app/Android.mk b/tests/app/Android.mk
index 381006a..c0c0ccf 100644
--- a/tests/app/Android.mk
+++ b/tests/app/Android.mk
@@ -14,6 +14,7 @@
     espresso-contrib-nodep \
     espresso-intents-nodep \
     ub-uiautomator \
+    truth-prebuilt \
     legacy-android-test
 
 # Include all test java files.
diff --git a/tests/app/src/com/android/settings/ChooseLockGenericTest.java b/tests/app/src/com/android/settings/ChooseLockGenericTest.java
new file mode 100644
index 0000000..dee6697
--- /dev/null
+++ b/tests/app/src/com/android/settings/ChooseLockGenericTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
+import android.support.test.runner.lifecycle.Stage;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
+
+import android.text.format.DateUtils;
+import android.view.KeyEvent;
+
+import com.android.settings.R;
+
+import java.util.Collection;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for {@link ChooseLockGenericTest}
+ *
+ * m SettingsTests &&
+ * adb install \
+ * -r -g  ${ANDROID_PRODUCT_OUT}/data/app/SettingsTests/SettingsTests.apk &&
+ * adb shell am instrument -e class com.android.settings.ChooseLockGenericTest \
+ * -w com.android.settings.tests/android.support.test.runner.AndroidJUnitRunner
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class ChooseLockGenericTest {
+    private static final long TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
+    private static final Intent PHISHING_ATTACK_INTENT = new Intent()
+            .putExtra("confirm_credentials", false)
+            .putExtra("password_confirmed", true);
+
+    private UiDevice mDevice;
+    private Context mTargetContext;
+    private String mSettingPackage;
+    private PackageManager mPackageManager;
+    @Rule
+    public ActivityTestRule<ChooseLockGeneric> mChooseLockGenericActivityRule =
+            new ActivityTestRule<>(
+                    ChooseLockGeneric.class,
+                    true /* enable touch at launch */,
+                    false /* don't launch at every test */);
+
+    @Before
+    public void setUp() throws Exception {
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mTargetContext = getInstrumentation().getTargetContext();
+        mSettingPackage = mTargetContext.getPackageName();
+        mPackageManager = mTargetContext.getPackageManager();
+
+        setPassword();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        clearPassword();
+    }
+
+    @Test
+    public void testConfirmLockPasswordShown_deviceWithPassword() throws Exception, Throwable {
+        // GIVEN a PIN password is set on this device at set up.
+        // WHEN ChooseLockGeneric is launched with no extras.
+        mChooseLockGenericActivityRule.launchActivity(null /* No extras */);
+        // THEN ConfirmLockPassword.InternalActivity is shown.
+        assertThat(getCurrentActivity()).isInstanceOf(ConfirmLockPassword.InternalActivity.class);
+    }
+
+    @Test
+    public void testConfirmLockPasswordShown_deviceWithPassword_phishingAttack()
+            throws Exception, Throwable {
+        // GIVEN a PIN password is set on this device at set up.
+        // WHEN ChooseLockGeneric is launched with extras to by-pass lock password confirmation.
+        mChooseLockGenericActivityRule.launchActivity(PHISHING_ATTACK_INTENT);
+        // THEN ConfirmLockPassword.InternalActivity is still shown.
+        assertThat(getCurrentActivity()).isInstanceOf(ConfirmLockPassword.InternalActivity.class);
+    }
+
+    private Activity getCurrentActivity() throws Throwable {
+        getInstrumentation().waitForIdleSync();
+        final Activity[] activity = new Activity[1];
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                Collection<Activity> activities = ActivityLifecycleMonitorRegistry.getInstance()
+                        .getActivitiesInStage(Stage.RESUMED);
+                activity[0] = activities.iterator().next();
+            }
+        });
+        return activity[0];
+    }
+
+    private void launchNewPassword() throws Exception {
+        Intent newPasswordIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD)
+                .setPackage(mSettingPackage)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        getInstrumentation().getContext().startActivity(newPasswordIntent);
+        mDevice.waitForIdle();
+    }
+
+    /** Sets a PIN password, 12345, for testing. */
+    private void setPassword() throws Exception {
+        launchNewPassword();
+
+        if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+            // Set "lock_none", but it actually means we don't want to enroll a fingerprint.
+            UiObject view = new UiObject(
+                    new UiSelector().resourceId(mSettingPackage + ":id/lock_none"));
+            assertTrue("lock_none", view.waitForExists(TIMEOUT));
+            view.click();
+            mDevice.waitForIdle();
+        }
+
+        // Pick PIN from the option list
+        UiObject view = new UiObject(new UiSelector()
+                .resourceId(mSettingPackage + ":id/lock_pin"));
+        assertTrue("lock_pin", view.waitForExists(TIMEOUT));
+        view.click();
+        mDevice.waitForIdle();
+
+        // Ignore any interstitial options
+        view = new UiObject(new UiSelector()
+                .resourceId(mSettingPackage + ":id/encrypt_dont_require_password"));
+        if (view.waitForExists(TIMEOUT)) {
+            view.click();
+            mDevice.waitForIdle();
+        }
+
+        // Yes, we really want to
+        view = new UiObject(new UiSelector()
+                .resourceId(mSettingPackage + ":id/next_button"));
+        if (view.waitForExists(TIMEOUT)) {
+            view.click();
+            mDevice.waitForIdle();
+        }
+
+        // Set our PIN
+        view = new UiObject(new UiSelector()
+                .resourceId(mSettingPackage + ":id/password_entry"));
+        assertTrue("password_entry", view.waitForExists(TIMEOUT));
+
+        // Enter it twice to confirm
+        enterTestPin();
+        enterTestPin();
+
+        mDevice.pressBack();
+    }
+
+    /** Clears the previous set PIN password. */
+    private void clearPassword() throws Exception {
+        launchNewPassword();
+
+        // Enter current PIN
+        UiObject view = new UiObject(
+                new UiSelector().resourceId(mSettingPackage + ":id/password_entry"));
+        if (!view.waitForExists(TIMEOUT)) {
+            // Odd, maybe there is a crash dialog showing; try dismissing it
+            mDevice.pressBack();
+            mDevice.waitForIdle();
+
+            assertTrue("password_entry", view.waitForExists(TIMEOUT));
+        }
+
+        enterTestPin();
+
+        // Set back to "none"
+        view = new UiObject(new UiSelector().resourceId(mSettingPackage + ":id/lock_none"));
+        assertTrue("lock_none", view.waitForExists(TIMEOUT));
+        view.click();
+        mDevice.waitForIdle();
+
+        // Yes, we really want "none" if prompted again
+        view = new UiObject(new UiSelector().resourceId(mSettingPackage + ":id/lock_none"));
+        if (view.waitForExists(TIMEOUT)) {
+            view.click();
+            mDevice.waitForIdle();
+        }
+
+        // Yes, we really want to
+        view = new UiObject(new UiSelector()
+                .resourceId("android:id/button1"));
+        if (view.waitForExists(TIMEOUT)) {
+            view.click();
+            mDevice.waitForIdle();
+        }
+
+        mDevice.pressBack();
+    }
+
+    private void enterTestPin() throws Exception {
+        mDevice.waitForIdle();
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_1);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_2);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_3);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_4);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_5);
+        mDevice.waitForIdle();
+        mDevice.pressEnter();
+        mDevice.waitForIdle();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java b/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java
new file mode 100644
index 0000000..7204bd1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java
@@ -0,0 +1,95 @@
+package com.android.settings.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import com.android.settings.R;
+import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class AppStorageSizesControllerTest {
+    private static final String COMPUTING = "Computing…";
+    private static final String INVALID_SIZE = "Couldn’t compute package size.";
+    private AppStorageSizesController mController;
+    private Context mContext;
+
+    private Preference mAppPreference;
+    private Preference mCachePreference;
+    private Preference mDataPreference;
+    private Preference mTotalPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mAppPreference = new Preference(mContext);
+        mCachePreference = new Preference(mContext);
+        mDataPreference = new Preference(mContext);
+        mTotalPreference = new Preference(mContext);
+
+        mController = new AppStorageSizesController.Builder()
+                .setAppSizePreference(mAppPreference)
+                .setCacheSizePreference(mCachePreference)
+                .setDataSizePreference(mDataPreference)
+                .setTotalSizePreference(mTotalPreference)
+                .setErrorString(R.string.invalid_size_value)
+                .setComputingString(R.string.computing_size)
+                .build();
+    }
+
+    @Test
+    public void requestingUpdateBeforeValuesSetIsComputing() {
+        mController.updateUi(mContext);
+
+        assertThat(mAppPreference.getSummary()).isEqualTo(COMPUTING);
+        assertThat(mCachePreference.getSummary()).isEqualTo(COMPUTING);
+        assertThat(mDataPreference.getSummary()).isEqualTo(COMPUTING);
+        assertThat(mTotalPreference.getSummary()).isEqualTo(COMPUTING);
+    }
+
+    @Test
+    public void requestingUpdateAfterFailureHasErrorText() {
+        mController.setResult(null);
+        mController.updateUi(mContext);
+
+        assertThat(mAppPreference.getSummary()).isEqualTo(INVALID_SIZE);
+        assertThat(mCachePreference.getSummary()).isEqualTo(INVALID_SIZE);
+        assertThat(mDataPreference.getSummary()).isEqualTo(INVALID_SIZE);
+        assertThat(mTotalPreference.getSummary()).isEqualTo(INVALID_SIZE);
+    }
+
+    @Test
+    public void properlyPopulatedAfterValidEntry() {
+        AppStorageStats result = mock(AppStorageStats.class);
+        when(result.getCodeBytes()).thenReturn(1L);
+        when(result.getCacheBytes()).thenReturn(10L);
+        when(result.getDataBytes()).thenReturn(100L);
+        when(result.getTotalBytes()).thenReturn(111L);
+
+        mController.setResult(result);
+        mController.updateUi(mContext);
+
+        assertThat(mAppPreference.getSummary()).isEqualTo("1.00B");
+        assertThat(mCachePreference.getSummary()).isEqualTo("10.00B");
+        assertThat(mDataPreference.getSummary()).isEqualTo("100B");
+        assertThat(mTotalPreference.getSummary()).isEqualTo("111B");
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/FetchPackageStorageAsyncLoaderTest.java b/tests/robotests/src/com/android/settings/applications/FetchPackageStorageAsyncLoaderTest.java
index 0b1d1aa..04eeb02 100644
--- a/tests/robotests/src/com/android/settings/applications/FetchPackageStorageAsyncLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/applications/FetchPackageStorageAsyncLoaderTest.java
@@ -43,6 +43,7 @@
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class FetchPackageStorageAsyncLoaderTest {
+    private static final String PACKAGE_NAME = "com.test.package";
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     @Mock
@@ -63,10 +64,22 @@
         when(mSource.getStatsForPackage(anyString(), anyString(), any(UserHandle.class)))
                 .thenReturn(stats);
         ApplicationInfo info = new ApplicationInfo();
-        info.packageName = "com.test.package";
+        info.packageName = PACKAGE_NAME;
 
         FetchPackageStorageAsyncLoader task = new FetchPackageStorageAsyncLoader(
                 mContext, mSource, info, new UserHandle(0));
         assertThat(task.loadInBackground()).isEqualTo(stats);
     }
+
+    @Test
+    public void installerExceptionHandledCleanly() {
+        when(mSource.getStatsForPackage(anyString(), anyString(), any(UserHandle.class))).
+                thenThrow(new IllegalStateException("intentional failure"));
+        ApplicationInfo info = new ApplicationInfo();
+        info.packageName = PACKAGE_NAME;
+        FetchPackageStorageAsyncLoader task = new FetchPackageStorageAsyncLoader(
+                mContext, mSource, info, new UserHandle(0));
+
+        assertThat(task.loadInBackground()).isNull();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
index 4fe6293..b0cd8d5 100644
--- a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
@@ -16,12 +16,8 @@
 
 package com.android.settings.applications;
 
-import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
+import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -44,6 +40,14 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public final class InstalledAppDetailsTest {
@@ -51,24 +55,27 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private UserManager mUserManager;
     @Mock
+    private Activity mActivity;
+    @Mock
     private DevicePolicyManager mDevicePolicyManager;
 
+    private InstalledAppDetails mAppDetail;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mAppDetail = new InstalledAppDetails();
     }
 
     @Test
     public void getInstallationStatus_notInstalled_shouldReturnUninstalled() {
-        final InstalledAppDetails mAppDetail = new InstalledAppDetails();
 
         assertThat(mAppDetail.getInstallationStatus(new ApplicationInfo()))
-            .isEqualTo(R.string.not_installed);
+                .isEqualTo(R.string.not_installed);
     }
 
     @Test
     public void getInstallationStatus_enabled_shouldReturnInstalled() {
-        final InstalledAppDetails mAppDetail = new InstalledAppDetails();
         final ApplicationInfo info = new ApplicationInfo();
         info.flags = ApplicationInfo.FLAG_INSTALLED;
         info.enabled = true;
@@ -78,7 +85,6 @@
 
     @Test
     public void getInstallationStatus_disabled_shouldReturnDisabled() {
-        final InstalledAppDetails mAppDetail = new InstalledAppDetails();
         final ApplicationInfo info = new ApplicationInfo();
         info.flags = ApplicationInfo.FLAG_INSTALLED;
         info.enabled = false;
@@ -90,7 +96,6 @@
     public void shouldShowUninstallForAll_installForOneOtherUserOnly_shouldReturnTrue() {
         when(mDevicePolicyManager.packageHasActiveAdmins(anyString())).thenReturn(false);
         when(mUserManager.getUsers().size()).thenReturn(2);
-        final InstalledAppDetails mAppDetail = new InstalledAppDetails();
         ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager);
         ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager);
         final ApplicationInfo info = new ApplicationInfo();
@@ -107,7 +112,6 @@
     public void shouldShowUninstallForAll_installForSelfOnly_shouldReturnFalse() {
         when(mDevicePolicyManager.packageHasActiveAdmins(anyString())).thenReturn(false);
         when(mUserManager.getUsers().size()).thenReturn(2);
-        final InstalledAppDetails mAppDetail = new InstalledAppDetails();
         ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager);
         ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager);
         final ApplicationInfo info = new ApplicationInfo();
@@ -139,6 +143,22 @@
 
         assertThat(InstalledAppDetails.getStorageSummary(context, stats, false))
                 .isEqualTo("1.00B used in Internal storage");
+    }
 
+    @Test
+    public void launchFragment_hasNoPackageInfo_shouldFinish() {
+        ReflectionHelpers.setField(mAppDetail, "mPackageInfo", null);
+
+        assertThat(mAppDetail.ensurePackageInfoAvailable(mActivity)).isFalse();
+        verify(mActivity).finishAndRemoveTask();
+    }
+
+    @Test
+    public void launchFragment_hasPackageInfo_shouldReturnTrue() {
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo);
+
+        assertThat(mAppDetail.ensurePackageInfoAvailable(mActivity)).isTrue();
+        verify(mActivity, never()).finishAndRemoveTask();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutoFillPickerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutoFillPickerTest.java
index f2cf729..1ee18cf 100644
--- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutoFillPickerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutoFillPickerTest.java
@@ -24,6 +24,7 @@
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.applications.PackageManagerWrapper;
+import com.android.settings.testutils.FakeFeatureFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -60,6 +61,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        FakeFeatureFactory.setupForTest(mActivity);
         when(mActivity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
         mPicker = spy(new DefaultAutoFillPicker());
         mPicker.onAttach((Context) mActivity);
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/DeviceModelPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/DeviceModelPreferenceControllerTest.java
index 06d81de..ea60e4c 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/DeviceModelPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/DeviceModelPreferenceControllerTest.java
@@ -16,6 +16,7 @@
 package com.android.settings.deviceinfo;
 
 
+import android.app.Fragment;
 import android.content.Context;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
@@ -32,15 +33,20 @@
 import org.robolectric.annotation.Config;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class DeviceModelPreferenceControllerTest {
+
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Fragment mFragment;
     @Mock
     private Preference mPreference;
     @Mock
@@ -50,9 +56,10 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mController = new DeviceModelPreferenceController(mContext);
+        mController = new DeviceModelPreferenceController(mContext, mFragment);
         when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
                 .thenReturn(mPreference);
+        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
     }
 
     @Test
@@ -66,4 +73,13 @@
 
         verify(mPreference).setSummary(anyString());
     }
+
+    @Test
+    public void clickPreference_shouldLaunchHardwareInfoDialog() {
+        assertThat(mController.handlePreferenceTreeClick(mPreference))
+                .isTrue();
+        verify(mFragment).getFragmentManager();
+        verify(mFragment.getFragmentManager().beginTransaction())
+                .add(any(HardwareInfoDialogFragment.class), eq(HardwareInfoDialogFragment.TAG));
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/HardwareInfoDialogFragmentTest.java b/tests/robotests/src/com/android/settings/deviceinfo/HardwareInfoDialogFragmentTest.java
new file mode 100644
index 0000000..e3dec76
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/HardwareInfoDialogFragmentTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo;
+
+import android.app.Activity;
+import android.os.SystemProperties;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class HardwareInfoDialogFragmentTest {
+
+    private Activity mActivity;
+
+    @Before
+    public void setUp() {
+        mActivity = Robolectric.setupActivity(Activity.class);
+    }
+
+    @Test
+    public void display_shouldShowHardwareRevision() {
+        final String TEST_HARDWARE_REV = "123";
+        SystemProperties.set("ro.boot.hardware.revision", TEST_HARDWARE_REV);
+
+        final HardwareInfoDialogFragment fragment = spy(HardwareInfoDialogFragment.newInstance());
+        fragment.show(mActivity.getFragmentManager(), HardwareInfoDialogFragment.TAG);
+
+        verify(fragment).setText(
+                any(View.class), eq(R.id.hardware_rev_label), eq(R.id.hardware_rev_value),
+                eq(TEST_HARDWARE_REV));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
index 7222f53..c8f3069 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
@@ -55,6 +55,10 @@
     private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_secondary_users";
     @Mock
     private UserManagerWrapper mUserManager;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private PreferenceGroup mGroup;
 
     private Context mContext;
     private SecondaryUserController mController;
@@ -66,19 +70,19 @@
         mContext = RuntimeEnvironment.application;
         mPrimaryUser = new UserInfo();
         mController = new SecondaryUserController(mContext, mPrimaryUser);
+
+        when(mScreen.getContext()).thenReturn(mContext);
+        when(mScreen.findPreference(anyString())).thenReturn(mGroup);
+        when(mGroup.getKey()).thenReturn(TARGET_PREFERENCE_GROUP_KEY);
     }
 
     @Test
     public void controllerAddsSecondaryUser() throws Exception {
         mPrimaryUser.name = TEST_NAME;
-        PreferenceScreen screen = mock(PreferenceScreen.class);
-        PreferenceGroup group = mock(PreferenceGroup.class);
-        when(screen.findPreference(anyString())).thenReturn(group);
-        when(group.getKey()).thenReturn(TARGET_PREFERENCE_GROUP_KEY);
-        mController.displayPreference(screen);
+        mController.displayPreference(mScreen);
 
         final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
-        verify(group).addPreference(argumentCaptor.capture());
+        verify(mGroup).addPreference(argumentCaptor.capture());
         Preference preference = argumentCaptor.getValue();
         assertThat(preference.getTitle()).isEqualTo(TEST_NAME);
     }
@@ -86,15 +90,11 @@
     @Test
     public void controllerUpdatesSummaryOfNewPreference() throws Exception {
         mPrimaryUser.name = TEST_NAME;
-        PreferenceScreen screen = mock(PreferenceScreen.class);
-        PreferenceGroup group = mock(PreferenceGroup.class);
-        when(screen.findPreference(anyString())).thenReturn(group);
-        when(group.getKey()).thenReturn(TARGET_PREFERENCE_GROUP_KEY);
-        mController.displayPreference(screen);
+        mController.displayPreference(mScreen);
         mController.setSize(10L);
         final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
 
-        verify(group).addPreference(argumentCaptor.capture());
+        verify(mGroup).addPreference(argumentCaptor.capture());
 
         Preference preference = argumentCaptor.getValue();
         assertThat(preference.getSummary()).isEqualTo("10.00B");
@@ -153,11 +153,7 @@
     public void controllerUpdatesPreferenceOnAcceptingResult() throws Exception {
         mPrimaryUser.name = TEST_NAME;
         mPrimaryUser.id = 10;
-        PreferenceScreen screen = mock(PreferenceScreen.class);
-        PreferenceGroup group = mock(PreferenceGroup.class);
-        when(screen.findPreference(anyString())).thenReturn(group);
-        when(group.getKey()).thenReturn(TARGET_PREFERENCE_GROUP_KEY);
-        mController.displayPreference(screen);
+        mController.displayPreference(mScreen);
         StorageAsyncLoader.AppsStorageResult userResult =
                 new StorageAsyncLoader.AppsStorageResult();
         SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
@@ -166,7 +162,7 @@
 
         mController.handleResult(result);
         final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
-        verify(group).addPreference(argumentCaptor.capture());
+        verify(mGroup).addPreference(argumentCaptor.capture());
         Preference preference = argumentCaptor.getValue();
 
         assertThat(preference.getSummary()).isEqualTo("99.00B");
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java
index 2cd4f76..ed49da4 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.Intent;
@@ -53,6 +54,8 @@
 
     @Mock
     private UserManagerWrapper mUserManager;
+    @Mock
+    private PreferenceScreen mScreen;
 
     private Context mContext;
     private UserProfileController mController;
@@ -64,17 +67,17 @@
         mContext = spy(RuntimeEnvironment.application);
         mPrimaryProfile = new UserInfo();
         mController = new UserProfileController(mContext, mPrimaryProfile, 0);
+        when(mScreen.getContext()).thenReturn(mContext);
     }
 
     @Test
     public void controllerAddsPrimaryProfilePreference() throws Exception {
         mPrimaryProfile.name = TEST_NAME;
         mPrimaryProfile.id = 10;
-        PreferenceScreen screen = mock(PreferenceScreen.class);
-        mController.displayPreference(screen);
+        mController.displayPreference(mScreen);
 
         final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
-        verify(screen).addPreference(argumentCaptor.capture());
+        verify(mScreen).addPreference(argumentCaptor.capture());
         Preference preference = argumentCaptor.getValue();
 
         assertThat(preference.getTitle()).isEqualTo(TEST_NAME);
@@ -85,11 +88,10 @@
     public void tappingProfilePreferenceSendsToStorageProfileFragment() throws Exception {
         mPrimaryProfile.name = TEST_NAME;
         mPrimaryProfile.id = 10;
-        PreferenceScreen screen = mock(PreferenceScreen.class);
-        mController.displayPreference(screen);
+        mController.displayPreference(mScreen);
 
         final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
-        verify(screen).addPreference(argumentCaptor.capture());
+        verify(mScreen).addPreference(argumentCaptor.capture());
         Preference preference = argumentCaptor.getValue();
         assertThat(mController.handlePreferenceTreeClick(preference)).isTrue();
         final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -105,8 +107,7 @@
     public void acceptingResultUpdatesPreferenceSize() throws Exception {
         mPrimaryProfile.name = TEST_NAME;
         mPrimaryProfile.id = 10;
-        PreferenceScreen screen = mock(PreferenceScreen.class);
-        mController.displayPreference(screen);
+        mController.displayPreference(mScreen);
         SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
         StorageAsyncLoader.AppsStorageResult userResult =
                 new StorageAsyncLoader.AppsStorageResult();
@@ -115,7 +116,7 @@
 
         mController.handleResult(result);
         final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
-        verify(screen).addPreference(argumentCaptor.capture());
+        verify(mScreen).addPreference(argumentCaptor.capture());
         Preference preference = argumentCaptor.getValue();
 
         assertThat(preference.getSummary()).isEqualTo("99.00B");
diff --git a/tests/robotests/src/com/android/settings/network/NetworkScorerPickerPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/NetworkScorerPickerPreferenceControllerTest.java
new file mode 100644
index 0000000..6bf47ac
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/NetworkScorerPickerPreferenceControllerTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import static android.provider.Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.NetworkScorerAppData;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class NetworkScorerPickerPreferenceControllerTest {
+
+    private static final String TEST_SCORER_PACKAGE = "Test Package";
+    private static final String TEST_SCORER_CLASS = "Test Class";
+    private static final String TEST_SCORER_LABEL = "Test Label";
+
+    private Context mContext;
+    @Mock
+    private NetworkScoreManagerWrapper mNetworkScorer;
+    private NetworkScorerPickerPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = new NetworkScorerPickerPreferenceController(mContext, mNetworkScorer);
+    }
+
+    @Test
+    public void testIsAvailable_shouldAlwaysReturnTrue() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void updateState_preferenceSetSummaryAsActiveScorerLabel() {
+        Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 1);
+        ComponentName scorer = new ComponentName(TEST_SCORER_PACKAGE, TEST_SCORER_CLASS);
+        NetworkScorerAppData scorerAppData = new NetworkScorerAppData(
+                0, scorer, TEST_SCORER_LABEL, null /* enableUseOpenWifiActivity */);
+        when(mNetworkScorer.getActiveScorer()).thenReturn(scorerAppData);
+        Preference preference = mock(Preference.class);
+
+        mController.updateState(preference);
+
+        verify(preference).setSummary(TEST_SCORER_LABEL);
+    }
+
+    @Test
+    public void updateState_noActiveScorer_preferenceSetSummaryToNone() {
+        Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 1);
+        when(mNetworkScorer.getActiveScorer()).thenReturn(null);
+        Preference preference = mock(Preference.class);
+
+        mController.updateState(preference);
+
+        verify(preference).setSummary(mContext.getString(
+                R.string.network_scorer_picker_none_preference));
+    }
+
+    @Test
+    public void updateState_networkRecommendationsDisabled_preferenceDisabled() {
+        Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 0);
+        when(mNetworkScorer.getActiveScorer()).thenReturn(null);
+        Preference preference = mock(Preference.class);
+
+        mController.updateState(preference);
+
+        verify(preference).setEnabled(false);
+        verify(preference).setSummary(null);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java b/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java
index 78839fa..c58ce37 100644
--- a/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java
+++ b/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java
@@ -35,11 +35,13 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.UserInfo;
 import android.os.UserManager;
+import android.support.v7.preference.PreferenceManager;
 
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
-import com.android.settings.widget.RadioButtonPreference;
 import com.android.settings.applications.PackageManagerWrapper;
+import com.android.settings.applications.defaultapps.DefaultAppInfo;
+import com.android.settings.widget.RadioButtonPreference;
 
 import java.util.Arrays;
 
@@ -51,6 +53,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -66,7 +69,7 @@
     private Activity mActivity;
     @Mock
     private UserManager mUserManager;
-    @Mock
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private PackageManagerWrapper mPackageManager;
 
     private WebViewAppPicker mPicker;
@@ -89,6 +92,8 @@
         doNothing().when(mPicker).updateCheckedState(any());
         doReturn(mActivity).when(mPicker).getActivity();
 
+        ReflectionHelpers.setField(mPicker, "mPm", mPackageManager);
+
         mWvusWrapper = mock(WebViewUpdateServiceWrapper.class);
         mPicker.setWebViewUpdateServiceWrapper(mWvusWrapper);
     }
@@ -152,6 +157,62 @@
     }
 
     @Test
+    public void testDisabledPackageShownAsDisabled() {
+        String disabledReason = "disabled";
+        DefaultAppInfo webviewAppInfo = mPicker.createDefaultAppInfo(
+                createApplicationInfo(DEFAULT_PACKAGE_NAME), disabledReason);
+
+        RadioButtonPreference mockPreference = mock(RadioButtonPreference.class);
+        mPicker.configurePreferenceFromAppInfo(mockPreference,
+                DEFAULT_PACKAGE_NAME, webviewAppInfo, null, null);
+
+        verify(mockPreference, times(1)).setEnabled(eq(false));
+        verify(mockPreference, never()).setEnabled(eq(true));
+    }
+
+    @Test
+    public void testEnabledPackageShownAsEnabled() {
+        String disabledReason = "";
+        DefaultAppInfo webviewAppInfo = mPicker.createDefaultAppInfo(
+                createApplicationInfo(DEFAULT_PACKAGE_NAME), disabledReason);
+
+        RadioButtonPreference mockPreference = mock(RadioButtonPreference.class);
+        mPicker.configurePreferenceFromAppInfo(mockPreference,
+                DEFAULT_PACKAGE_NAME, webviewAppInfo, null, null);
+
+        verify(mockPreference, times(1)).setEnabled(eq(true));
+        verify(mockPreference, never()).setEnabled(eq(false));
+    }
+
+    @Test
+    public void testDisabledPackageShowsDisabledReasonSummary() {
+        String disabledReason = "disabled";
+        DefaultAppInfo webviewAppInfo = mPicker.createDefaultAppInfo(
+                createApplicationInfo(DEFAULT_PACKAGE_NAME), disabledReason);
+
+        RadioButtonPreference mockPreference = mock(RadioButtonPreference.class);
+        mPicker.configurePreferenceFromAppInfo(mockPreference,
+                DEFAULT_PACKAGE_NAME, webviewAppInfo, null, null);
+
+        verify(mockPreference, times(1)).setSummary(eq(disabledReason));
+        // Ensure we haven't called setSummary several times.
+        verify(mockPreference, times(1)).setSummary(any());
+    }
+
+    @Test
+    public void testEnabledPackageShowsEmptySummary() {
+        String disabledReason = null;
+        DefaultAppInfo webviewAppInfo = mPicker.createDefaultAppInfo(
+                createApplicationInfo(DEFAULT_PACKAGE_NAME), disabledReason);
+
+        RadioButtonPreference mockPreference = mock(RadioButtonPreference.class);
+        mPicker.configurePreferenceFromAppInfo(mockPreference,
+                DEFAULT_PACKAGE_NAME, webviewAppInfo, null, null);
+
+        verify(mockPreference, never()).setSummary(any());
+    }
+
+    @Test
     public void testFinishIfNotAdmin() {
         doReturn(false).when(mUserManager).isAdminUser();
         mPicker.onAttach((Context) mActivity);