Merge "Respect DISALLOW_UNIFIED_PASSWORD in Settings."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6ccb8d1..0418d51 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3312,13 +3312,13 @@
             </intent-filter>
         </activity>
 
-        <provider android:name=".SettingsSliceProvider"
+        <provider android:name=".slices.SettingsSliceProvider"
                   android:authorities="com.android.settings.slices"
                   android:exported="true">
         </provider>
 
         <receiver
-            android:name=".SliceBroadcastReceiver" >
+            android:name=".slices.SliceBroadcastReceiver" >
             <intent-filter>
                 <action android:name="com.android.settings.slice.action.WIFI_CHANGED"/>
             </intent-filter>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 2ba7919..e3fa070 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -97,6 +97,7 @@
     <!-- For Search -->
     <declare-styleable name="Preference">
         <attr name="keywords" format="string" />
+        <attr name="controller" format="string" />
     </declare-styleable>
 
     <!-- For DotsPageIndicator -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7331d72..694a1b0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -393,6 +393,8 @@
     <string name="bluetooth_paired_device_title">Your devices</string>
     <!-- Title for pairing bluetooth device page [CHAR LIMIT=none] -->
     <string name="bluetooth_pairing_page_title">Pair new device</string>
+    <!-- Summary for bluetooth item in connection detail page -->
+    <string name="bluetooth_pref_summary">Allow device to pair and connect to bluetooth devices</string>
 
     <!-- Title for connected device group [CHAR LIMIT=none]-->
     <string name="connected_device_connected_title">Currently connected</string>
@@ -6726,14 +6728,14 @@
     <!-- Do not disturb: Subtitle for DND behavior indicating no sound will get past DND. [CHAR LIMIT=30] -->
     <string name="zen_mode_behavior_no_sound">No sound</string>
 
-    <!-- Do not disturb: Subtitle for DND behavior indicating no sound will get past DND due to user and/or API-invoked Total Silence mode. [CHAR LIMIT=40] -->
-    <string name="zen_mode_behavior_total_silence">No sound (Total Silence)</string>
+    <!-- Do not disturb: Subtitle for DND behavior indicating no sound will get past DND. [CHAR LIMIT=40] -->
+    <string name="zen_mode_behavior_total_silence">Total Silence</string>
 
     <!-- Do not disturb: Used before specifying which sounds can bypass DND (ie: No sound except alarms and reminders). [CHAR LIMIT=40] -->
     <string name="zen_mode_behavior_no_sound_except">No sound except <xliff:g id="categories" example="alarms, media and system feedback">%1$s</xliff:g></string>
 
-    <!-- Do not disturb: Specifies sounds that can bypass DND in user and/or API-invoked Alarms Only mode.  [CHAR LIMIT=100] -->
-    <string name="zen_mode_behavior_alarms_only">No sound except alarms, media and system feedback (Alarms only)</string>
+    <!-- Do not disturb: Specifies alarms and media can bypass DND.  [CHAR LIMIT=100] -->
+    <string name="zen_mode_behavior_alarms_only">No sound except alarms and media</string>
 
     <!--  Do not disturb: Title for the zen mode automation option in Settings. [CHAR LIMIT=40] -->
     <string name="zen_mode_automation_settings_title">Turn on automatically</string>
@@ -7313,8 +7315,11 @@
     <!-- [CHAR LIMIT=50] Zen mode settings: Alarms option -->
     <string name="zen_mode_alarms">Alarms</string>
 
-    <!-- [CHAR LIMIT=50] Zen mode settings: Media and system sounds option -->
-    <string name="zen_mode_media_system_other">Media and system feedback</string>
+    <!-- [CHAR LIMIT=50] Zen mode settings: Media option -->
+    <string name="zen_mode_media_system_other">Media</string>
+
+    <!-- [CHAR LIMIT=120] Zen mode settings: Media secondary text explaining sounds include system feedback such as system tapping sounds, haptic feedback, etc. -->
+    <string name="zen_mode_media_system_other_secondary_text">Includes system feedback like touch and charging sounds</string>
 
     <!-- [CHAR LIMIT=50] Zen mode settings: Reminders option -->
     <string name="zen_mode_reminders">Reminders</string>
@@ -7334,6 +7339,9 @@
     <!-- [CHAR LIMIT=200] Zen mode settings: Repeat callers option summary -->
     <string name="zen_mode_repeat_callers_summary">If the same person calls a second time within a <xliff:g id="minutes">%d</xliff:g> minute period</string>
 
+    <!-- [CHAR LIMIT=50] Zen mode settings dnd beahvior description: when a user has chosen custom sounds to bypass DND -->
+    <string name="zen_mode_behavior_summary_custom">Custom</string>
+
     <!-- [CHAR LIMIT=20] Zen mode settings: When option -->
     <string name="zen_mode_when">Automatically turn on</string>
 
@@ -9124,4 +9132,9 @@
     <!-- Note displayed when certain features are not available on low ram devices. [CHAR LIMIT=NONE] -->
     <string name="disabled_low_ram_device">This feature is not available on this device</string>
 
+    <!-- UI debug setting: enable gnss raw meas full tracking [CHAR LIMIT=25] -->
+    <string name="enable_gnss_raw_meas_full_tracking">Force Full GnssMeasurement</string>
+    <!-- UI debug setting: enable gpu debug layers summary [CHAR LIMIT=50] -->
+    <string name="enable_gnss_raw_meas_full_tracking_summary">Disable GNSS duty-cycling, track all constellations and frequencies.</string>
+
 </resources>
diff --git a/res/xml/app_info_settings.xml b/res/xml/app_info_settings.xml
index 664210b..99c76b8 100644
--- a/res/xml/app_info_settings.xml
+++ b/res/xml/app_info_settings.xml
@@ -132,6 +132,17 @@
 
     </PreferenceCategory>
 
+    <!-- App installer info -->
+    <PreferenceCategory
+        android:key="app_installer"
+        android:title="@string/app_install_details_group_title">
+
+        <Preference
+            android:key="app_info_store"
+            android:title="@string/app_install_details_title" />
+
+    </PreferenceCategory>
+
     <Preference
         android:key="app_version"
         android:selectable="false"
diff --git a/res/xml/bluetooth_pairing_detail.xml b/res/xml/bluetooth_pairing_detail.xml
index e60da75..e654a3c 100644
--- a/res/xml/bluetooth_pairing_detail.xml
+++ b/res/xml/bluetooth_pairing_detail.xml
@@ -19,7 +19,9 @@
     android:title="@string/bluetooth_pairing_pref_title">
 
     <Preference
-        android:key="device_name"/>
+        android:key="bt_pair_rename_devices"
+        android:title="@string/bluetooth_device_name"
+        android:summary="@string/summary_placeholder" />
 
     <com.android.settings.bluetooth.BluetoothProgressCategory
         android:key="available_devices"
diff --git a/res/xml/connected_devices_advanced.xml b/res/xml/connected_devices_advanced.xml
index 946151f..57a2580 100644
--- a/res/xml/connected_devices_advanced.xml
+++ b/res/xml/connected_devices_advanced.xml
@@ -19,10 +19,11 @@
     android:key="connected_devices_screen"
     android:title="@string/connected_devices_dashboard_title">
 
-    <com.android.settings.widget.MasterSwitchPreference
-      android:key="toggle_bluetooth"
+    <SwitchPreference
+      android:key="toggle_bluetooth_switch"
       android:title="@string/bluetooth_settings_title"
       android:icon="@drawable/ic_settings_bluetooth"
+      android:summary="@string/bluetooth_pref_summary"
       android:order="-7"/>
 
     <SwitchPreference
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index d1eb366..60efcab 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -133,6 +133,11 @@
             android:title="@string/mock_location_app" />
 
         <SwitchPreference
+            android:key="enable_gnss_raw_meas_full_tracking"
+            android:title="@string/enable_gnss_raw_meas_full_tracking"
+            android:summary="@string/enable_gnss_raw_meas_full_tracking_summary"/>
+
+        <SwitchPreference
                 android:key="debug_view_attributes"
                 android:title="@string/debug_view_attributes" />
 
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index f9f5d2b..333fd2a 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -25,8 +25,9 @@
     <Preference
         android:key="brightness"
         android:title="@string/brightness"
-        settings:keywords="@string/keywords_display_brightness_level">
-        <intent android:action="com.android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
+        settings:keywords="@string/keywords_display_brightness_level"
+        settings:controller="com.android.settings.display.AutoBrightnessPreferenceController">
+    <intent android:action="com.android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
     </Preference>
 
     <com.android.settings.display.NightDisplayPreference
diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml
index 8b9d0ca..1dfa6fb 100644
--- a/res/xml/system_dashboard_fragment.xml
+++ b/res/xml/system_dashboard_fragment.xml
@@ -26,7 +26,8 @@
         android:title="@string/gesture_preference_title"
         android:icon="@drawable/ic_settings_gestures"
         android:order="-250"
-        android:fragment="com.android.settings.gestures.GestureSettings" />
+        android:fragment="com.android.settings.gestures.GestureSettings"
+        settings:controller="com.android.settings.gestures.GesturesSettingPreferenceController"/>
 
     <!-- Backup -->
     <Preference
@@ -34,7 +35,8 @@
         android:title="@string/privacy_settings_title"
         android:summary="@string/summary_placeholder"
         android:icon="@drawable/ic_settings_backup"
-        android:order="-60">
+        android:order="-60"
+        settings:controller="com.android.settings.backup.BackupSettingsActivityPreferenceController">
         <intent android:action="android.settings.BACKUP_AND_RESET_SETTINGS" />
     </Preference>
 
@@ -44,14 +46,16 @@
         android:title="@string/system_update_settings_list_item_title"
         android:summary="@string/summary_placeholder"
         android:icon="@drawable/ic_system_update"
-        android:order="-30">
+        android:order="-30"
+        settings:controller="com.android.settings.deviceinfo.SystemUpdatePreferenceController">
         <intent android:action="android.settings.SYSTEM_UPDATE_SETTINGS" />
     </Preference>
 
     <Preference
         android:key="additional_system_update_settings"
         android:title="@string/additional_system_update_settings_list_item_title"
-        android:order="-31">
+        android:order="-31"
+        settings:controller="com.android.settings.deviceinfo.AdditionalSystemUpdatePreferenceController">
         <intent android:action="android.intent.action.MAIN"
                 android:targetPackage="@string/additional_system_update"
                 android:targetClass="@string/additional_system_update_menu" />
diff --git a/res/xml/zen_mode_behavior_settings.xml b/res/xml/zen_mode_behavior_settings.xml
index c0849da..6aeebe6 100644
--- a/res/xml/zen_mode_behavior_settings.xml
+++ b/res/xml/zen_mode_behavior_settings.xml
@@ -34,7 +34,8 @@
        <!-- Media -->
        <SwitchPreference
            android:key="zen_mode_media"
-           android:title="@string/zen_mode_media_system_other"/>
+           android:title="@string/zen_mode_media_system_other"
+           android:summary="@string/zen_mode_media_system_other_secondary_text"/>
 
        <!-- Reminders -->
        <SwitchPreference
diff --git a/src/com/android/settings/DeviceAdminAdd.java b/src/com/android/settings/DeviceAdminAdd.java
index 2fd769b..0ad882d 100644
--- a/src/com/android/settings/DeviceAdminAdd.java
+++ b/src/com/android/settings/DeviceAdminAdd.java
@@ -69,6 +69,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 public class DeviceAdminAdd extends Activity {
     static final String TAG = "DeviceAdminAdd";
@@ -145,18 +146,14 @@
                 DevicePolicyManager.EXTRA_DEVICE_ADMIN);
         if (who == null) {
             String packageName = getIntent().getStringExtra(EXTRA_DEVICE_ADMIN_PACKAGE_NAME);
-            for (ComponentName component : mDPM.getActiveAdmins()) {
-                if (component.getPackageName().equals(packageName)) {
-                    who = component;
-                    mUninstalling = true;
-                    break;
-                }
-            }
-            if (who == null) {
+            Optional<ComponentName> installedAdmin = findAdminWithPackageName(packageName);
+            if (!installedAdmin.isPresent()) {
                 Log.w(TAG, "No component specified in " + action);
                 finish();
                 return;
             }
+            who = installedAdmin.get();
+            mUninstalling = true;
         }
 
         if (action != null && action.equals(DevicePolicyManager.ACTION_SET_PROFILE_OWNER)) {
@@ -692,6 +689,18 @@
         return info != null ? info.isManagedProfile() : false;
     }
 
+    /**
+     * @return an {@link Optional} containing the admin with a given package name, if it exists,
+     *         or {@link Optional#empty()} otherwise.
+     */
+    private Optional<ComponentName> findAdminWithPackageName(String packageName) {
+        List<ComponentName> admins = mDPM.getActiveAdmins();
+        if (admins == null) {
+            return Optional.empty();
+        }
+        return admins.stream().filter(i -> i.getPackageName().equals(packageName)).findAny();
+    }
+
     private boolean isAdminUninstallable() {
         // System apps can't be uninstalled.
         return !mDeviceAdmin.getActivityInfo().applicationInfo.isSystemApp();
diff --git a/src/com/android/settings/PreviewSeekBarPreferenceFragment.java b/src/com/android/settings/PreviewSeekBarPreferenceFragment.java
index 544999a..f5f3017 100644
--- a/src/com/android/settings/PreviewSeekBarPreferenceFragment.java
+++ b/src/com/android/settings/PreviewSeekBarPreferenceFragment.java
@@ -58,6 +58,7 @@
     private DotsPageIndicator mPageIndicator;
 
     private TextView mLabel;
+    private LabeledSeekBar mSeekBar;
     private View mLarger;
     private View mSmaller;
 
@@ -110,19 +111,17 @@
         // seek bar.
         final int max = Math.max(1, mEntries.length - 1);
 
-        final LabeledSeekBar seekBar = (LabeledSeekBar) content.findViewById(R.id.seek_bar);
-        seekBar.setLabels(mEntries);
-        seekBar.setMax(max);
-        seekBar.setProgress(mInitialIndex);
-        seekBar.setOnSeekBarChangeListener(new onPreviewSeekBarChangeListener());
+        mSeekBar = (LabeledSeekBar) content.findViewById(R.id.seek_bar);
+        mSeekBar.setLabels(mEntries);
+        mSeekBar.setMax(max);
 
         mSmaller = content.findViewById(R.id.smaller);
         mSmaller.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                final int progress = seekBar.getProgress();
+                final int progress = mSeekBar.getProgress();
                 if (progress > 0) {
-                    seekBar.setProgress(progress - 1, true);
+                    mSeekBar.setProgress(progress - 1, true);
                 }
             }
         });
@@ -131,9 +130,9 @@
         mLarger.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                final int progress = seekBar.getProgress();
-                if (progress < seekBar.getMax()) {
-                    seekBar.setProgress(progress + 1, true);
+                final int progress = mSeekBar.getProgress();
+                if (progress < mSeekBar.getMax()) {
+                    mSeekBar.setProgress(progress + 1, true);
                 }
             }
         });
@@ -141,7 +140,7 @@
         if (mEntries.length == 1) {
             // The larger and smaller buttons will be disabled when we call
             // setPreviewLayer() later in this method.
-            seekBar.setEnabled(false);
+            mSeekBar.setEnabled(false);
         }
 
         final Context context = getContext();
@@ -172,6 +171,21 @@
         return root;
     }
 
+    @Override
+    public void onStart() {
+        super.onStart();
+        // Set SeekBar listener here to avoid onProgressChanged() is called
+        // during onRestoreInstanceState().
+        mSeekBar.setProgress(mCurrentIndex);
+        mSeekBar.setOnSeekBarChangeListener(new onPreviewSeekBarChangeListener());
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        mSeekBar.setOnSeekBarChangeListener(null);
+    }
+
     /**
      * Creates new configuration based on the current position of the SeekBar.
      */
diff --git a/src/com/android/settings/applications/AppInfoDashboardFragment.java b/src/com/android/settings/applications/AppInfoDashboardFragment.java
deleted file mode 100755
index d7d91f3..0000000
--- a/src/com/android/settings/applications/AppInfoDashboardFragment.java
+++ /dev/null
@@ -1,1166 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.android.settings.applications;
-
-import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceCategory;
-import android.support.v7.preference.PreferenceScreen;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.webkit.IWebViewUpdateService;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.DeviceAdminAdd;
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.Utils;
-import com.android.settings.applications.appinfo.AppBatteryPreferenceController;
-import com.android.settings.applications.appinfo.AppDataUsagePreferenceController;
-import com.android.settings.applications.appinfo.AppMemoryPreferenceController;
-import com.android.settings.applications.appinfo.AppNotificationPreferenceController;
-import com.android.settings.applications.appinfo.AppOpenByDefaultPreferenceController;
-import com.android.settings.applications.appinfo.AppPermissionPreferenceController;
-import com.android.settings.applications.appinfo.AppStoragePreferenceController;
-import com.android.settings.applications.appinfo.AppVersionPreferenceController;
-import com.android.settings.applications.appinfo.DefaultBrowserShortcutPreferenceController;
-import com.android.settings.applications.appinfo.DefaultEmergencyShortcutPreferenceController;
-import com.android.settings.applications.appinfo.DefaultHomeShortcutPreferenceController;
-import com.android.settings.applications.appinfo.DefaultPhoneShortcutPreferenceController;
-import com.android.settings.applications.appinfo.DefaultSmsShortcutPreferenceController;
-import com.android.settings.applications.appinfo.DrawOverlayDetailPreferenceController;
-import com.android.settings.applications.appinfo.ExternalSourceDetailPreferenceController;
-import com.android.settings.applications.appinfo.PictureInPictureDetailPreferenceController;
-import com.android.settings.applications.appinfo.WriteSystemSettingsPreferenceController;
-import com.android.settings.applications.instantapps.InstantAppButtonsController;
-import com.android.settings.applications.manageapplications.ManageApplications;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
-import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.widget.ActionButtonPreference;
-import com.android.settings.widget.EntityHeaderController;
-import com.android.settings.widget.PreferenceCategoryController;
-import com.android.settings.wrapper.DevicePolicyManagerWrapper;
-import com.android.settingslib.RestrictedLockUtils;
-import com.android.settingslib.applications.AppUtils;
-import com.android.settingslib.applications.ApplicationsState;
-import com.android.settingslib.applications.ApplicationsState.AppEntry;
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Dashboard fragment to display application information from Settings. This activity presents
- * extended information associated with a package like code, data, total size, permissions
- * used by the application and also the set of default launchable activities.
- * For system applications, an option to clear user data is displayed only if data size is > 0.
- * System applications that do not want clear user data do not have this option.
- * For non-system applications, there is no option to clear data. Instead there is an option to
- * uninstall the application.
- */
-public class AppInfoDashboardFragment extends DashboardFragment
-        implements ApplicationsState.Callbacks {
-
-    private static final String TAG = "AppInfoDashboard";
-
-    // Menu identifiers
-    public static final int UNINSTALL_ALL_USERS_MENU = 1;
-    public static final int UNINSTALL_UPDATES = 2;
-
-    // Result code identifiers
-    public static final int REQUEST_UNINSTALL = 0;
-    private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
-
-    public static final int SUB_INFO_FRAGMENT = 1;
-
-    public static final int LOADER_CHART_DATA = 2;
-    public static final int LOADER_STORAGE = 3;
-    @VisibleForTesting
-    public static final int LOADER_BATTERY = 4;
-
-    // Dialog identifiers used in showDialog
-    private static final int DLG_BASE = 0;
-    private static final int DLG_FORCE_STOP = DLG_BASE + 1;
-    private static final int DLG_DISABLE = DLG_BASE + 2;
-    private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3;
-    private static final String KEY_HEADER = "header_view";
-    private static final String KEY_INSTANT_APP_BUTTONS = "instant_app_buttons";
-    private static final String KEY_ACTION_BUTTONS = "action_buttons";
-    private static final String KEY_INSTANT_APP_SUPPORTED_LINKS =
-            "instant_app_launch_supported_domain_urls";
-
-    public static final String ARG_PACKAGE_NAME = "package";
-    public static final String ARG_PACKAGE_UID = "uid";
-
-    protected static final boolean localLOGV = false;
-    private static final String KEY_ADVANCED_APP_INFO_CATEGORY = "advanced_app_info";
-
-    private EnforcedAdmin mAppsControlDisallowedAdmin;
-    private boolean mAppsControlDisallowedBySystem;
-
-    private ApplicationFeatureProvider mApplicationFeatureProvider;
-    private ApplicationsState mState;
-    private ApplicationsState.Session mSession;
-    private ApplicationsState.AppEntry mAppEntry;
-    private PackageInfo mPackageInfo;
-    private int mUserId;
-    private String mPackageName;
-
-    private DevicePolicyManagerWrapper mDpm;
-    private UserManager mUserManager;
-    private PackageManager mPm;
-
-    private boolean mFinishing;
-    private boolean mListeningToPackageRemove;
-
-
-    private final HashSet<String> mHomePackages = new HashSet<>();
-
-    private boolean mInitialized;
-    private boolean mShowUninstalled;
-    private LayoutPreference mHeader;
-    private boolean mUpdatedSysApp = false;
-    private AppDomainsPreference mInstantAppDomainsPreference;
-    private boolean mDisableAfterUninstall;
-
-    private List<Callback> mCallbacks = new ArrayList<>();
-
-    @VisibleForTesting
-    ActionButtonPreference mActionButtons;
-
-    private InstantAppButtonsController mInstantAppButtonsController;
-
-    /**
-     * Callback to invoke when app info has been changed.
-     */
-    public interface Callback {
-        void refreshUi();
-    }
-
-    @VisibleForTesting
-    boolean handleDisableable() {
-        boolean disableable = false;
-        // Try to prevent the user from bricking their phone
-        // by not allowing disabling of apps signed with the
-        // system cert and any launcher app in the system.
-        if (mHomePackages.contains(mAppEntry.info.packageName)
-                || Utils.isSystemPackage(getContext().getResources(), mPm, mPackageInfo)) {
-            // Disable button for core system applications.
-            mActionButtons
-                    .setButton1Text(R.string.disable_text)
-                    .setButton1Positive(false);
-        } else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
-            mActionButtons
-                    .setButton1Text(R.string.disable_text)
-                    .setButton1Positive(false);
-            disableable = !mApplicationFeatureProvider.getKeepEnabledPackages()
-                    .contains(mAppEntry.info.packageName);
-        } else {
-            mActionButtons
-                    .setButton1Text(R.string.enable_text)
-                    .setButton1Positive(true);
-            disableable = true;
-        }
-
-        return disableable;
-    }
-
-    private boolean isDisabledUntilUsed() {
-        return mAppEntry.info.enabledSetting
-                == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
-    }
-
-    private void initUninstallButtons() {
-        final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
-        boolean enabled;
-        if (isBundled) {
-            enabled = handleDisableable();
-        } else {
-            enabled = initUninstallButtonForUserApp();
-        }
-        // If this is a device admin, it can't be uninstalled or disabled.
-        // We do this here so the text of the button is still set correctly.
-        if (isBundled && mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
-            enabled = false;
-        }
-
-        // We don't allow uninstalling DO/PO on *any* users, because if it's a system app,
-        // "uninstall" is actually "downgrade to the system version + disable", and "downgrade"
-        // will clear data on all users.
-        if (Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mPackageInfo.packageName)) {
-            enabled = false;
-        }
-
-        // Don't allow uninstalling the device provisioning package.
-        if (Utils.isDeviceProvisioningPackage(getResources(), mAppEntry.info.packageName)) {
-            enabled = false;
-        }
-
-        // If the uninstall intent is already queued, disable the uninstall button
-        if (mDpm.isUninstallInQueue(mPackageName)) {
-            enabled = false;
-        }
-
-        // Home apps need special handling.  Bundled ones we don't risk downgrading
-        // because that can interfere with home-key resolution.  Furthermore, we
-        // can't allow uninstallation of the only home app, and we don't want to
-        // allow uninstallation of an explicitly preferred one -- the user can go
-        // to Home settings and pick a different one, after which we'll permit
-        // uninstallation of the now-not-default one.
-        if (enabled && mHomePackages.contains(mPackageInfo.packageName)) {
-            if (isBundled) {
-                enabled = false;
-            } else {
-                ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
-                ComponentName currentDefaultHome  = mPm.getHomeActivities(homeActivities);
-                if (currentDefaultHome == null) {
-                    // No preferred default, so permit uninstall only when
-                    // there is more than one candidate
-                    enabled = (mHomePackages.size() > 1);
-                } else {
-                    // There is an explicit default home app -- forbid uninstall of
-                    // that one, but permit it for installed-but-inactive ones.
-                    enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName());
-                }
-            }
-        }
-
-        if (mAppsControlDisallowedBySystem) {
-            enabled = false;
-        }
-
-        try {
-            IWebViewUpdateService webviewUpdateService =
-                IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
-            if (webviewUpdateService.isFallbackPackage(mAppEntry.info.packageName)) {
-                enabled = false;
-            }
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-
-        mActionButtons.setButton1Enabled(enabled);
-        if (enabled) {
-            // Register listener
-            mActionButtons.setButton1OnClickListener(v -> handleUninstallButtonClick());
-        }
-    }
-
-    @VisibleForTesting
-    boolean initUninstallButtonForUserApp() {
-        boolean enabled = true;
-        if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0
-                && mUserManager.getUsers().size() >= 2) {
-            // When we have multiple users, there is a separate menu
-            // to uninstall for all users.
-            enabled = false;
-        } else if (AppUtils.isInstant(mPackageInfo.applicationInfo)) {
-            enabled = false;
-            mActionButtons.setButton1Visible(false);
-        }
-        mActionButtons.setButton1Text(R.string.uninstall_text).setButton1Positive(false);
-        return enabled;
-    }
-
-    /** Called when the activity is first created. */
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        mFinishing = false;
-        final Activity activity = getActivity();
-        mApplicationFeatureProvider = FeatureFactory.getFactory(activity)
-                .getApplicationFeatureProvider(activity);
-        mDpm = new DevicePolicyManagerWrapper(
-                (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE));
-        mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
-        mPm = activity.getPackageManager();
-
-        retrieveAppEntry();
-        startListeningToPackageRemove();
-
-        if (!ensurePackageInfoAvailable(activity)) {
-            return;
-        }
-
-        setHasOptionsMenu(true);
-
-        addDynamicPrefs();
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return MetricsEvent.APPLICATIONS_INSTALLED_APP_DETAILS;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mAppsControlDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(getActivity(),
-                UserManager.DISALLOW_APPS_CONTROL, mUserId);
-        mAppsControlDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(getActivity(),
-                UserManager.DISALLOW_APPS_CONTROL, mUserId);
-
-        if (!refreshUi()) {
-            setIntentAndFinish(true, true);
-        }
-    }
-
-    @Override
-    protected int getPreferenceScreenResId() {
-        return R.xml.app_info_settings;
-    }
-
-    @Override
-    protected String getLogTag() {
-        return TAG;
-    }
-
-    @Override
-    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
-        final String packageName = getPackageName();
-        final List<AbstractPreferenceController> controllers = new ArrayList<>();
-        final Lifecycle lifecycle = getLifecycle();
-
-        // The following are controllers for preferences that needs to refresh the preference state
-        // when app state changes.
-        controllers.add(new AppStoragePreferenceController(context, this, lifecycle));
-        controllers.add(new AppDataUsagePreferenceController(context, this, lifecycle));
-        controllers.add(new AppNotificationPreferenceController(context, this));
-        controllers.add(new AppOpenByDefaultPreferenceController(context, this));
-        controllers.add(new AppPermissionPreferenceController(context, this, packageName));
-        controllers.add(new AppVersionPreferenceController(context, this));
-
-        for (AbstractPreferenceController controller : controllers) {
-            mCallbacks.add((Callback) controller);
-        }
-
-        // The following are controllers for preferences that don't need to refresh the preference
-        // state when app state changes.
-        controllers.add(new AppBatteryPreferenceController(context, this, packageName, lifecycle));
-        controllers.add(new AppMemoryPreferenceController(context, this, lifecycle));
-        controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName));
-        controllers.add(new DefaultBrowserShortcutPreferenceController(context, packageName));
-        controllers.add(new DefaultPhoneShortcutPreferenceController(context, packageName));
-        controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName));
-        controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName));
-
-        final List<AbstractPreferenceController> advancedAppInfoControllers = new ArrayList<>();
-        advancedAppInfoControllers.add(new DrawOverlayDetailPreferenceController(context, this));
-        advancedAppInfoControllers.add(new WriteSystemSettingsPreferenceController(context, this));
-        advancedAppInfoControllers.add(
-                new PictureInPictureDetailPreferenceController(context, this, packageName));
-        advancedAppInfoControllers.add(
-                new ExternalSourceDetailPreferenceController(context, this, packageName));
-        controllers.addAll(advancedAppInfoControllers);
-        controllers.add(new PreferenceCategoryController(
-                context, KEY_ADVANCED_APP_INFO_CATEGORY, advancedAppInfoControllers));
-
-        return controllers;
-    }
-
-    public ApplicationsState.AppEntry getAppEntry() {
-        if (mAppEntry == null) {
-            retrieveAppEntry();
-        }
-        return mAppEntry;
-    }
-
-    public PackageInfo getPackageInfo() {
-        if (mAppEntry == null) {
-            retrieveAppEntry();
-        }
-        return mPackageInfo;
-    }
-
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        if (mFinishing) {
-            return;
-        }
-        final Activity activity = getActivity();
-        mHeader = (LayoutPreference) findPreference(KEY_HEADER);
-        mActionButtons = ((ActionButtonPreference) findPreference(KEY_ACTION_BUTTONS))
-                .setButton2Text(R.string.force_stop)
-                .setButton2Positive(false)
-                .setButton2Enabled(false);
-        EntityHeaderController.newInstance(activity, this, mHeader.findViewById(R.id.entity_header))
-                .setRecyclerView(getListView(), getLifecycle())
-                .setPackageName(mPackageName)
-                .setHasAppInfoLink(false)
-                .setButtonActions(EntityHeaderController.ActionType.ACTION_APP_PREFERENCE,
-                        EntityHeaderController.ActionType.ACTION_NONE)
-                .styleActionBar(activity)
-                .bindHeaderButtons();
-
-        mInstantAppDomainsPreference =
-                (AppDomainsPreference) findPreference(KEY_INSTANT_APP_SUPPORTED_LINKS);
-    }
-
-    @Override
-    public void onPackageSizeChanged(String packageName) {
-        if (!TextUtils.equals(packageName, mPackageName)) {
-            Log.d(TAG, "Package change irrelevant, skipping");
-          return;
-        }
-        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(TAG, "Package info not available. Is this package already uninstalled?");
-            activity.finishAndRemoveTask();
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        super.onCreateOptionsMenu(menu, inflater);
-        menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset)
-                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-        menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
-                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-    }
-
-    @Override
-    public void onPrepareOptionsMenu(Menu menu) {
-        if (mFinishing) {
-            return;
-        }
-        menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry));
-        mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
-        MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES);
-        uninstallUpdatesItem.setVisible(mUpdatedSysApp && !mAppsControlDisallowedBySystem);
-        if (uninstallUpdatesItem.isVisible()) {
-            RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getActivity(),
-                    uninstallUpdatesItem, mAppsControlDisallowedAdmin);
-        }
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case UNINSTALL_ALL_USERS_MENU:
-                uninstallPkg(mAppEntry.info.packageName, true, false);
-                return true;
-            case UNINSTALL_UPDATES:
-                uninstallPkg(mAppEntry.info.packageName, false, false);
-                return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
-        switch (requestCode) {
-            case REQUEST_UNINSTALL:
-                // Refresh option menu
-                getActivity().invalidateOptionsMenu();
-
-                if (mDisableAfterUninstall) {
-                    mDisableAfterUninstall = false;
-                    new DisableChanger(this, mAppEntry.info,
-                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
-                            .execute((Object)null);
-                }
-                // continue with following operations
-            case REQUEST_REMOVE_DEVICE_ADMIN:
-                if (!refreshUi()) {
-                    setIntentAndFinish(true, true);
-                } else {
-                    startListeningToPackageRemove();
-                }
-                break;
-        }
-    }
-
-    /**
-     * Utility method to hide and show specific preferences based on whether the app being displayed
-     * is an Instant App or an installed app.
-     */
-    @VisibleForTesting
-    void prepareInstantAppPrefs() {
-        final boolean isInstant = AppUtils.isInstant(mPackageInfo.applicationInfo);
-        if (isInstant) {
-            Set<String> handledDomainSet = Utils.getHandledDomains(mPm, mPackageInfo.packageName);
-            String[] handledDomains = handledDomainSet.toArray(new String[handledDomainSet.size()]);
-            mInstantAppDomainsPreference.setTitles(handledDomains);
-            // Dummy values, unused in the implementation
-            mInstantAppDomainsPreference.setValues(new int[handledDomains.length]);
-        } else {
-            getPreferenceScreen().removePreference(mInstantAppDomainsPreference);
-        }
-    }
-
-    // Utility method to set application label and icon.
-    private void setAppLabelAndIcon(PackageInfo pkgInfo) {
-        final View appSnippet = mHeader.findViewById(R.id.entity_header);
-        mState.ensureIcon(mAppEntry);
-        final Activity activity = getActivity();
-        final boolean isInstantApp = AppUtils.isInstant(mPackageInfo.applicationInfo);
-        final CharSequence summary =
-                isInstantApp ? null : getString(Utils.getInstallationStatus(mAppEntry.info));
-        EntityHeaderController.newInstance(activity, this, appSnippet)
-                .setLabel(mAppEntry)
-                .setIcon(mAppEntry)
-                .setSummary(summary)
-                .setIsInstantApp(isInstantApp)
-                .done(activity, false /* rebindActions */);
-    }
-
-    @VisibleForTesting
-    boolean shouldShowUninstallForAll(AppEntry appEntry) {
-        boolean showIt = true;
-        if (mUpdatedSysApp) {
-            showIt = false;
-        } else if (appEntry == null) {
-            showIt = false;
-        } else if ((appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-            showIt = false;
-        } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
-            showIt = false;
-        } else if (UserHandle.myUserId() != 0) {
-            showIt = false;
-        } else if (mUserManager.getUsers().size() < 2) {
-            showIt = false;
-        } else if (PackageUtil.countPackageInUsers(mPm, mUserManager, mPackageName) < 2
-                && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
-            showIt = false;
-        } else if (AppUtils.isInstant(appEntry.info)) {
-            showIt = false;
-        }
-        return showIt;
-    }
-
-    private boolean signaturesMatch(String pkg1, String pkg2) {
-        if (pkg1 != null && pkg2 != null) {
-            try {
-                final int match = mPm.checkSignatures(pkg1, pkg2);
-                if (match >= PackageManager.SIGNATURE_MATCH) {
-                    return true;
-                }
-            } catch (Exception e) {
-                // e.g. named alternate package not found during lookup;
-                // this is an expected case sometimes
-            }
-        }
-        return false;
-    }
-
-    @VisibleForTesting
-    boolean refreshUi() {
-        retrieveAppEntry();
-        if (mAppEntry == null) {
-            return false; // onCreate must have failed, make sure to exit
-        }
-
-        if (mPackageInfo == null) {
-            return false; // onCreate must have failed, make sure to exit
-        }
-
-        // Get list of "home" apps and trace through any meta-data references
-        List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
-        mPm.getHomeActivities(homeActivities);
-        mHomePackages.clear();
-        for (int i = 0; i< homeActivities.size(); i++) {
-            ResolveInfo ri = homeActivities.get(i);
-            final String activityPkg = ri.activityInfo.packageName;
-            mHomePackages.add(activityPkg);
-
-            // Also make sure to include anything proxying for the home app
-            final Bundle metadata = ri.activityInfo.metaData;
-            if (metadata != null) {
-                final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE);
-                if (signaturesMatch(metaPkg, activityPkg)) {
-                    mHomePackages.add(metaPkg);
-                }
-            }
-        }
-
-        checkForceStop();
-        setAppLabelAndIcon(mPackageInfo);
-        initUninstallButtons();
-        prepareInstantAppPrefs();
-
-        // Update the preference summaries.
-        Activity context = getActivity();
-        for (Callback callback : mCallbacks) {
-            callback.refreshUi();
-        }
-
-        if (!mInitialized) {
-            // First time init: are we displaying an uninstalled app?
-            mInitialized = true;
-            mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0;
-        } else {
-            // All other times: if the app no longer exists then we want
-            // to go away.
-            try {
-                ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo(
-                        mAppEntry.info.packageName,
-                        PackageManager.MATCH_DISABLED_COMPONENTS
-                        | PackageManager.MATCH_ANY_USER);
-                if (!mShowUninstalled) {
-                    // If we did not start out with the app uninstalled, then
-                    // it transitioning to the uninstalled state for the current
-                    // user means we should go away as well.
-                    return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0;
-                }
-            } catch (NameNotFoundException e) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    protected AlertDialog createDialog(int id, int errorCode) {
-        switch (id) {
-            case DLG_DISABLE:
-                return new AlertDialog.Builder(getActivity())
-                        .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
-                        .setPositiveButton(R.string.app_disable_dlg_positive,
-                                new DialogInterface.OnClickListener() {
-                            public void onClick(DialogInterface dialog, int which) {
-                                // Disable the app
-                                mMetricsFeatureProvider.action(getContext(),
-                                        MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
-                                new DisableChanger(AppInfoDashboardFragment.this, mAppEntry.info,
-                                        PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
-                                .execute((Object)null);
-                            }
-                        })
-                        .setNegativeButton(R.string.dlg_cancel, null)
-                        .create();
-            case DLG_SPECIAL_DISABLE:
-                return new AlertDialog.Builder(getActivity())
-                        .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
-                        .setPositiveButton(R.string.app_disable_dlg_positive,
-                                new DialogInterface.OnClickListener() {
-                            public void onClick(DialogInterface dialog, int which) {
-                                // Disable the app and ask for uninstall
-                                mMetricsFeatureProvider.action(getContext(),
-                                        MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
-                                uninstallPkg(mAppEntry.info.packageName,
-                                        false, true);
-                            }
-                        })
-                        .setNegativeButton(R.string.dlg_cancel, null)
-                        .create();
-            case DLG_FORCE_STOP:
-                return new AlertDialog.Builder(getActivity())
-                        .setTitle(getActivity().getText(R.string.force_stop_dlg_title))
-                        .setMessage(getActivity().getText(R.string.force_stop_dlg_text))
-                        .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
-                            public void onClick(DialogInterface dialog, int which) {
-                                // Force stop
-                                forceStopPackage(mAppEntry.info.packageName);
-                            }
-                        })
-                        .setNegativeButton(R.string.dlg_cancel, null)
-                        .create();
-        }
-        if (mInstantAppButtonsController != null) {
-            return mInstantAppButtonsController.createDialog(id);
-        }
-        return null;
-    }
-
-    private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
-        stopListeningToPackageRemove();
-         // Create new intent to launch Uninstaller activity
-        Uri packageURI = Uri.parse("package:"+packageName);
-        Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
-        uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
-        mMetricsFeatureProvider.action(
-                getContext(), MetricsEvent.ACTION_SETTINGS_UNINSTALL_APP);
-        startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
-        mDisableAfterUninstall = andDisable;
-    }
-
-    private void forceStopPackage(String pkgName) {
-        mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_APP_FORCE_STOP, pkgName);
-        ActivityManager am = (ActivityManager) getActivity().getSystemService(
-                Context.ACTIVITY_SERVICE);
-        Log.d(TAG, "Stopping package " + pkgName);
-        am.forceStopPackage(pkgName);
-        int userId = UserHandle.getUserId(mAppEntry.info.uid);
-        mState.invalidatePackage(pkgName, userId);
-        AppEntry newEnt = mState.getEntry(pkgName, userId);
-        if (newEnt != null) {
-            mAppEntry = newEnt;
-        }
-        checkForceStop();
-    }
-
-    private void updateForceStopButton(boolean enabled) {
-        mActionButtons
-                .setButton2Enabled(mAppsControlDisallowedBySystem ? false : enabled)
-                .setButton2OnClickListener(mAppsControlDisallowedBySystem
-                        ? null : v -> handleForceStopButtonClick());
-    }
-
-    @VisibleForTesting
-    void checkForceStop() {
-        if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
-            // User can't force stop device admin.
-            Log.w(TAG, "User can't force stop device admin");
-            updateForceStopButton(false);
-        } else if (AppUtils.isInstant(mPackageInfo.applicationInfo)) {
-            updateForceStopButton(false);
-            mActionButtons.setButton2Visible(false);
-        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) {
-            // If the app isn't explicitly stopped, then always show the
-            // force stop button.
-            Log.w(TAG, "App is not explicitly stopped");
-            updateForceStopButton(true);
-        } else {
-            Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
-                    Uri.fromParts("package", mAppEntry.info.packageName, null));
-            intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
-            intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
-            intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid));
-            Log.d(TAG, "Sending broadcast to query restart status for "
-                    + mAppEntry.info.packageName);
-            getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
-                    mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
-        }
-    }
-
-    public static void startAppInfoFragment(Class<?> fragment, int title,
-            SettingsPreferenceFragment caller, AppEntry appEntry) {
-        // start new fragment to display extended information
-        Bundle args = new Bundle();
-        args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName);
-        args.putInt(ARG_PACKAGE_UID, appEntry.info.uid);
-
-        SettingsActivity sa = (SettingsActivity) caller.getActivity();
-        sa.startPreferencePanel(caller, fragment.getName(), args, title, null, caller,
-                SUB_INFO_FRAGMENT);
-    }
-
-    private void handleUninstallButtonClick() {
-        if (mAppEntry == null) {
-            setIntentAndFinish(true, true);
-            return;
-        }
-        final String packageName = mAppEntry.info.packageName;
-        if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
-            stopListeningToPackageRemove();
-            Activity activity = getActivity();
-            Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class);
-            uninstallDAIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME,
-                    mPackageName);
-            mMetricsFeatureProvider.action(
-                    activity, MetricsEvent.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN);
-            activity.startActivityForResult(uninstallDAIntent, REQUEST_REMOVE_DEVICE_ADMIN);
-            return;
-        }
-        EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(),
-                packageName, mUserId);
-        boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem ||
-                RestrictedLockUtils.hasBaseUserRestriction(getActivity(), packageName, mUserId);
-        if (admin != null && !uninstallBlockedBySystem) {
-            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), admin);
-        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-            if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
-                // If the system app has an update and this is the only user on the device,
-                // then offer to downgrade the app, otherwise only offer to disable the
-                // app for this user.
-                if (mUpdatedSysApp && isSingleUser()) {
-                    showDialogInner(DLG_SPECIAL_DISABLE, 0);
-                } else {
-                    showDialogInner(DLG_DISABLE, 0);
-                }
-            } else {
-                mMetricsFeatureProvider.action(
-                        getActivity(),
-                        MetricsEvent.ACTION_SETTINGS_ENABLE_APP);
-                new DisableChanger(this, mAppEntry.info,
-                        PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
-                        .execute((Object) null);
-            }
-        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
-            uninstallPkg(packageName, true, false);
-        } else {
-            uninstallPkg(packageName, false, false);
-        }
-    }
-
-    private void handleForceStopButtonClick() {
-        if (mAppEntry == null) {
-            setIntentAndFinish(true, true);
-            return;
-        }
-        if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
-            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
-                    getActivity(), mAppsControlDisallowedAdmin);
-        } else {
-            showDialogInner(DLG_FORCE_STOP, 0);
-            //forceStopPackage(mAppInfo.packageName);
-        }
-    }
-
-    /** Returns whether there is only one user on this device, not including the system-only user */
-    private boolean isSingleUser() {
-        final int userCount = mUserManager.getUserCount();
-        return userCount == 1
-                || (mUserManager.isSplitSystemUser() && userCount == 2);
-    }
-
-    private void addDynamicPrefs() {
-        if (UserManager.get(getContext()).isManagedProfile()) {
-            return;
-        }
-        addAppInstallerInfoPref(getPreferenceScreen());
-        maybeAddInstantAppButtons();
-    }
-
-    private void addAppInstallerInfoPref(PreferenceScreen screen) {
-        String installerPackageName =
-                AppStoreUtil.getInstallerPackageName(getContext(), mPackageName);
-
-        final CharSequence installerLabel = Utils.getApplicationLabel(getContext(),
-                installerPackageName);
-        if (installerLabel == null) {
-            return;
-        }
-        final int detailsStringId = AppUtils.isInstant(mPackageInfo.applicationInfo)
-                ? R.string.instant_app_details_summary
-                : R.string.app_install_details_summary;
-        PreferenceCategory category = new PreferenceCategory(getPrefContext());
-        category.setTitle(R.string.app_install_details_group_title);
-        screen.addPreference(category);
-        Preference pref = new Preference(getPrefContext());
-        pref.setTitle(R.string.app_install_details_title);
-        pref.setKey("app_info_store");
-        pref.setSummary(getString(detailsStringId, installerLabel));
-
-        Intent intent =
-                AppStoreUtil.getAppStoreLink(getContext(), installerPackageName, mPackageName);
-        if (intent != null) {
-            pref.setIntent(intent);
-        } else {
-            pref.setEnabled(false);
-        }
-        category.addPreference(pref);
-    }
-
-    @VisibleForTesting
-    void maybeAddInstantAppButtons() {
-        if (AppUtils.isInstant(mPackageInfo.applicationInfo)) {
-            LayoutPreference buttons = (LayoutPreference) findPreference(KEY_INSTANT_APP_BUTTONS);
-            mInstantAppButtonsController = mApplicationFeatureProvider
-                    .newInstantAppButtonsController(this,
-                            buttons.findViewById(R.id.instant_app_button_container),
-                            id -> showDialogInner(id, 0))
-                    .setPackageName(mPackageName)
-                    .show();
-        }
-    }
-
-    private void onPackageRemoved() {
-        getActivity().finishActivity(SUB_INFO_FRAGMENT);
-        getActivity().finishAndRemoveTask();
-    }
-
-    /**
-     * Elicit this class for testing. Test cannot be done in robolectric because it
-     * invokes the new API.
-     */
-    @VisibleForTesting
-    public static class PackageUtil {
-        /**
-         * Count how many users in device have installed package {@paramref packageName}
-         */
-        public static int countPackageInUsers(PackageManager packageManager, UserManager
-                userManager, String packageName) {
-            final List<UserInfo> userInfos = userManager.getUsers(true);
-            int count = 0;
-
-            for (final UserInfo userInfo : userInfos) {
-                try {
-                    // Use this API to check whether user has this package
-                    final ApplicationInfo info = packageManager.getApplicationInfoAsUser(
-                            packageName, PackageManager.GET_META_DATA, userInfo.id);
-                    if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
-                        count++;
-                    }
-                } catch(NameNotFoundException e) {
-                    Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id);
-                }
-            }
-
-            return count;
-        }
-    }
-
-    private static class DisableChanger extends AsyncTask<Object, Object, Object> {
-        final PackageManager mPm;
-        final WeakReference<AppInfoDashboardFragment> mActivity;
-        final ApplicationInfo mInfo;
-        final int mState;
-
-        DisableChanger(AppInfoDashboardFragment activity, ApplicationInfo info, int state) {
-            mPm = activity.mPm;
-            mActivity = new WeakReference<AppInfoDashboardFragment>(activity);
-            mInfo = info;
-            mState = state;
-        }
-
-        @Override
-        protected Object doInBackground(Object... params) {
-            mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
-            return null;
-        }
-    }
-
-    private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final boolean enabled = getResultCode() != Activity.RESULT_CANCELED;
-            Log.d(TAG, "Got broadcast response: Restart status for "
-                    + mAppEntry.info.packageName + " " + enabled);
-            updateForceStopButton(enabled);
-        }
-    };
-
-    private String getPackageName() {
-        if (mPackageName != null) {
-            return mPackageName;
-        }
-        final Bundle args = getArguments();
-        mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
-        if (mPackageName == null) {
-            Intent intent = (args == null) ?
-                    getActivity().getIntent() : (Intent) args.getParcelable("intent");
-            if (intent != null) {
-                mPackageName = intent.getData().getSchemeSpecificPart();
-            }
-        }
-        return mPackageName;
-    }
-
-    private void retrieveAppEntry() {
-        final Activity activity = getActivity();
-        if (activity == null) {
-            return;
-        }
-        if (mState == null) {
-            mState = ApplicationsState.getInstance(activity.getApplication());
-            mSession = mState.newSession(this, getLifecycle());
-        }
-        mUserId = UserHandle.myUserId();
-        mAppEntry = mState.getEntry(getPackageName(), UserHandle.myUserId());
-        if (mAppEntry != null) {
-            // Get application info again to refresh changed properties of application
-            try {
-                mPackageInfo = activity.getPackageManager().getPackageInfo(
-                        mAppEntry.info.packageName,
-                        PackageManager.MATCH_DISABLED_COMPONENTS |
-                                PackageManager.MATCH_ANY_USER |
-                                PackageManager.GET_SIGNATURES |
-                                PackageManager.GET_PERMISSIONS);
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
-            }
-        } else {
-            Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
-            mPackageInfo = null;
-        }
-    }
-
-    private void setIntentAndFinish(boolean finish, boolean appChanged) {
-        if (localLOGV) Log.i(TAG, "appChanged="+appChanged);
-        Intent intent = new Intent();
-        intent.putExtra(ManageApplications.APP_CHG, appChanged);
-        SettingsActivity sa = (SettingsActivity)getActivity();
-        sa.finishPreferencePanel(this, Activity.RESULT_OK, intent);
-        mFinishing = true;
-    }
-
-    private void showDialogInner(int id, int moveErrorCode) {
-        DialogFragment newFragment =
-                AppInfoBase.MyAlertDialogFragment.newInstance(id, moveErrorCode);
-        newFragment.setTargetFragment(this, 0);
-        newFragment.show(getFragmentManager(), "dialog " + id);
-    }
-
-    @Override
-    public void onRunningStateChanged(boolean running) {
-        // No op.
-    }
-
-    @Override
-    public void onRebuildComplete(ArrayList<AppEntry> apps) {
-        // No op.
-    }
-
-    @Override
-    public void onPackageIconChanged() {
-        // No op.
-    }
-
-    @Override
-    public void onAllSizesComputed() {
-        // No op.
-    }
-
-    @Override
-    public void onLauncherInfoChanged() {
-        // No op.
-    }
-
-    @Override
-    public void onLoadEntriesCompleted() {
-        // No op.
-    }
-
-    @Override
-    public void onPackageListChanged() {
-        if (!refreshUi()) {
-            setIntentAndFinish(true, true);
-        }
-    }
-
-    public static void startAppInfoFragment(Class<?> fragment, int titleRes,
-            String pkg, int uid, Fragment source, int request, int sourceMetricsCategory) {
-        startAppInfoFragment(fragment, titleRes, pkg, uid, source.getActivity(), request,
-                sourceMetricsCategory);
-    }
-
-    public static void startAppInfoFragment(Class<?> fragment, int titleRes,
-            String pkg, int uid, Activity source, int request, int sourceMetricsCategory) {
-        Bundle args = new Bundle();
-        args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkg);
-        args.putInt(AppInfoBase.ARG_PACKAGE_UID, uid);
-
-        Intent intent = Utils.onBuildStartFragmentIntent(source, fragment.getName(),
-                args, null, titleRes, null, false, sourceMetricsCategory);
-        source.startActivityForResultAsUser(intent, request,
-                new UserHandle(UserHandle.getUserId(uid)));
-    }
-
-    public static class MyAlertDialogFragment extends InstrumentedDialogFragment {
-
-        private static final String ARG_ID = "id";
-
-        @Override
-        public int getMetricsCategory() {
-            return MetricsEvent.DIALOG_APP_INFO_ACTION;
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            int id = getArguments().getInt(ARG_ID);
-            int errorCode = getArguments().getInt("moveError");
-            Dialog dialog = ((AppInfoBase) getTargetFragment()).createDialog(id, errorCode);
-            if (dialog == null) {
-                throw new IllegalArgumentException("unknown id " + id);
-            }
-            return dialog;
-        }
-
-        public static AppInfoBase.MyAlertDialogFragment newInstance(int id, int errorCode) {
-            AppInfoBase.MyAlertDialogFragment
-                    dialogFragment = new AppInfoBase.MyAlertDialogFragment();
-            Bundle args = new Bundle();
-            args.putInt(ARG_ID, id);
-            args.putInt("moveError", errorCode);
-            dialogFragment.setArguments(args);
-            return dialogFragment;
-        }
-    }
-
-    private void startListeningToPackageRemove() {
-        if (mListeningToPackageRemove) {
-            return;
-        }
-        mListeningToPackageRemove = true;
-        final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addDataScheme("package");
-        getContext().registerReceiver(mPackageRemovedReceiver, filter);
-    }
-
-    private void stopListeningToPackageRemove() {
-        if (!mListeningToPackageRemove) {
-            return;
-        }
-        mListeningToPackageRemove = false;
-        getContext().unregisterReceiver(mPackageRemovedReceiver);
-    }
-
-    private final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String packageName = intent.getData().getSchemeSpecificPart();
-            if (!mFinishing && (mAppEntry == null || mAppEntry.info == null
-                    || TextUtils.equals(mAppEntry.info.packageName, packageName))) {
-                onPackageRemoved();
-            }
-        }
-    };
-
-}
diff --git a/src/com/android/settings/applications/AppStoreUtil.java b/src/com/android/settings/applications/AppStoreUtil.java
index f9b95b0..13e5516 100644
--- a/src/com/android/settings/applications/AppStoreUtil.java
+++ b/src/com/android/settings/applications/AppStoreUtil.java
@@ -16,11 +16,9 @@
 
 package com.android.settings.applications;
 
-
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
-import android.net.Uri;
 import android.util.Log;
 
 // This class provides methods that help dealing with app stores.
@@ -43,9 +41,6 @@
         } catch (IllegalArgumentException e) {
             Log.e(LOG_TAG, "Exception while retrieving the package installer of " + packageName, e);
         }
-        if (installerPackageName == null) {
-            return null;
-        }
         return installerPackageName;
     }
 
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 2098bd6..f78459f 100755
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -75,6 +75,7 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
 import com.android.settings.applications.appinfo.DrawOverlayDetails;
 import com.android.settings.applications.appinfo.ExternalSourcesDetails;
 import com.android.settings.applications.appinfo.PictureInPictureDetails;
diff --git a/src/com/android/settings/applications/InstalledAppDetailsTop.java b/src/com/android/settings/applications/InstalledAppDetailsTop.java
index 174a86a..8090de0 100644
--- a/src/com/android/settings/applications/InstalledAppDetailsTop.java
+++ b/src/com/android/settings/applications/InstalledAppDetailsTop.java
@@ -20,6 +20,7 @@
 import android.util.FeatureFlagUtils;
 
 import com.android.settings.SettingsActivity;
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
 import com.android.settings.core.FeatureFlags;
 
 public class InstalledAppDetailsTop extends SettingsActivity {
diff --git a/src/com/android/settings/applications/RecentAppsPreferenceController.java b/src/com/android/settings/applications/RecentAppsPreferenceController.java
index c613a7b..ee954ac 100644
--- a/src/com/android/settings/applications/RecentAppsPreferenceController.java
+++ b/src/com/android/settings/applications/RecentAppsPreferenceController.java
@@ -40,6 +40,7 @@
 
 import com.android.settings.R;
 import com.android.settings.Utils;
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
 import com.android.settings.core.FeatureFlags;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.widget.AppPreference;
diff --git a/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java b/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java
new file mode 100644
index 0000000..b10d06c
--- /dev/null
+++ b/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java
@@ -0,0 +1,318 @@
+/*
+ * 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.appinfo;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
+import android.webkit.IWebViewUpdateService;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.ActionButtonPreference;
+import com.android.settings.wrapper.DevicePolicyManagerWrapper;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+public class AppActionButtonPreferenceController extends BasePreferenceController
+        implements AppInfoDashboardFragment.Callback {
+
+    private static final String TAG = "AppActionButtonControl";
+    private static final String KEY_ACTION_BUTTONS = "action_buttons";
+
+    @VisibleForTesting
+    ActionButtonPreference mActionButtons;
+    private final AppInfoDashboardFragment mParent;
+    private final String mPackageName;
+    private final HashSet<String> mHomePackages = new HashSet<>();
+    private final ApplicationFeatureProvider mApplicationFeatureProvider;
+
+    private int mUserId;
+    private DevicePolicyManagerWrapper mDpm;
+    private UserManager mUserManager;
+    private PackageManager mPm;
+
+    private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final boolean enabled = getResultCode() != Activity.RESULT_CANCELED;
+            Log.d(TAG, "Got broadcast response: Restart status for "
+                    + mParent.getAppEntry().info.packageName + " " + enabled);
+            updateForceStopButton(enabled);
+        }
+    };
+
+    public AppActionButtonPreferenceController(Context context, AppInfoDashboardFragment parent,
+            String packageName) {
+        super(context, KEY_ACTION_BUTTONS);
+        mParent = parent;
+        mPackageName = packageName;
+        mUserId = UserHandle.myUserId();
+        mApplicationFeatureProvider = FeatureFactory.getFactory(context)
+                .getApplicationFeatureProvider(context);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mActionButtons = ((ActionButtonPreference) screen.findPreference(KEY_ACTION_BUTTONS))
+                .setButton2Text(R.string.force_stop)
+                .setButton2Positive(false)
+                .setButton2Enabled(false);
+    }
+
+    @Override
+    public void refreshUi() {
+        if (mPm == null) {
+            mPm = mContext.getPackageManager();
+        }
+        if (mDpm == null) {
+            mDpm = new DevicePolicyManagerWrapper(
+                    (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE));
+        }
+        if (mUserManager == null) {
+            mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        }
+        final AppEntry appEntry = mParent.getAppEntry();
+        final PackageInfo packageInfo = mParent.getPackageInfo();
+
+        // Get list of "home" apps and trace through any meta-data references
+        final List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
+        mPm.getHomeActivities(homeActivities);
+        mHomePackages.clear();
+        for (int i = 0; i< homeActivities.size(); i++) {
+            final ResolveInfo ri = homeActivities.get(i);
+            final String activityPkg = ri.activityInfo.packageName;
+            mHomePackages.add(activityPkg);
+
+            // Also make sure to include anything proxying for the home app
+            final Bundle metadata = ri.activityInfo.metaData;
+            if (metadata != null) {
+                final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE);
+                if (signaturesMatch(metaPkg, activityPkg)) {
+                    mHomePackages.add(metaPkg);
+                }
+            }
+        }
+
+        checkForceStop(appEntry, packageInfo);
+        initUninstallButtons(appEntry, packageInfo);
+    }
+
+    @VisibleForTesting
+    void initUninstallButtons(AppEntry appEntry, PackageInfo packageInfo) {
+        final boolean isBundled = (appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+        boolean enabled;
+        if (isBundled) {
+            enabled = handleDisableable(appEntry, packageInfo);
+        } else {
+            enabled = initUninstallButtonForUserApp();
+        }
+        // If this is a device admin, it can't be uninstalled or disabled.
+        // We do this here so the text of the button is still set correctly.
+        if (isBundled && mDpm.packageHasActiveAdmins(packageInfo.packageName)) {
+            enabled = false;
+        }
+
+        // We don't allow uninstalling DO/PO on *any* users, because if it's a system app,
+        // "uninstall" is actually "downgrade to the system version + disable", and "downgrade"
+        // will clear data on all users.
+        if (Utils.isProfileOrDeviceOwner(mUserManager, mDpm, packageInfo.packageName)) {
+            enabled = false;
+        }
+
+        // Don't allow uninstalling the device provisioning package.
+        if (Utils.isDeviceProvisioningPackage(mContext.getResources(), appEntry.info.packageName)) {
+            enabled = false;
+        }
+
+        // If the uninstall intent is already queued, disable the uninstall button
+        if (mDpm.isUninstallInQueue(mPackageName)) {
+            enabled = false;
+        }
+
+        // Home apps need special handling.  Bundled ones we don't risk downgrading
+        // because that can interfere with home-key resolution.  Furthermore, we
+        // can't allow uninstallation of the only home app, and we don't want to
+        // allow uninstallation of an explicitly preferred one -- the user can go
+        // to Home settings and pick a different one, after which we'll permit
+        // uninstallation of the now-not-default one.
+        if (enabled && mHomePackages.contains(packageInfo.packageName)) {
+            if (isBundled) {
+                enabled = false;
+            } else {
+                ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
+                ComponentName currentDefaultHome  = mPm.getHomeActivities(homeActivities);
+                if (currentDefaultHome == null) {
+                    // No preferred default, so permit uninstall only when
+                    // there is more than one candidate
+                    enabled = (mHomePackages.size() > 1);
+                } else {
+                    // There is an explicit default home app -- forbid uninstall of
+                    // that one, but permit it for installed-but-inactive ones.
+                    enabled = !packageInfo.packageName.equals(currentDefaultHome.getPackageName());
+                }
+            }
+        }
+
+        if (RestrictedLockUtils.hasBaseUserRestriction(
+                mContext, UserManager.DISALLOW_APPS_CONTROL, mUserId)) {
+            enabled = false;
+        }
+
+        try {
+            final IWebViewUpdateService webviewUpdateService =
+                    IWebViewUpdateService.Stub.asInterface(
+                            ServiceManager.getService("webviewupdate"));
+            if (webviewUpdateService.isFallbackPackage(appEntry.info.packageName)) {
+                enabled = false;
+            }
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        mActionButtons.setButton1Enabled(enabled);
+        if (enabled) {
+            // Register listener
+            mActionButtons.setButton1OnClickListener(v -> mParent.handleUninstallButtonClick());
+        }
+    }
+
+    @VisibleForTesting
+    boolean initUninstallButtonForUserApp() {
+        boolean enabled = true;
+        final PackageInfo packageInfo = mParent.getPackageInfo();
+        if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0
+                && mUserManager.getUsers().size() >= 2) {
+            // When we have multiple users, there is a separate menu
+            // to uninstall for all users.
+            enabled = false;
+        } else if (AppUtils.isInstant(packageInfo.applicationInfo)) {
+            enabled = false;
+            mActionButtons.setButton1Visible(false);
+        }
+        mActionButtons.setButton1Text(R.string.uninstall_text).setButton1Positive(false);
+        return enabled;
+    }
+
+    @VisibleForTesting
+    boolean handleDisableable(AppEntry appEntry, PackageInfo packageInfo) {
+        boolean disableable = false;
+        // Try to prevent the user from bricking their phone
+        // by not allowing disabling of apps signed with the
+        // system cert and any launcher app in the system.
+        if (mHomePackages.contains(appEntry.info.packageName)
+                || Utils.isSystemPackage(mContext.getResources(), mPm, packageInfo)) {
+            // Disable button for core system applications.
+            mActionButtons
+                    .setButton1Text(R.string.disable_text)
+                    .setButton1Positive(false);
+        } else if (appEntry.info.enabled && appEntry.info.enabledSetting
+                != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+            mActionButtons
+                    .setButton1Text(R.string.disable_text)
+                    .setButton1Positive(false);
+            disableable = !mApplicationFeatureProvider.getKeepEnabledPackages()
+                    .contains(appEntry.info.packageName);
+        } else {
+            mActionButtons
+                    .setButton1Text(R.string.enable_text)
+                    .setButton1Positive(true);
+            disableable = true;
+        }
+
+        return disableable;
+    }
+
+    private void updateForceStopButton(boolean enabled) {
+        final boolean disallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(
+                mContext, UserManager.DISALLOW_APPS_CONTROL, mUserId);
+        mActionButtons
+                .setButton2Enabled(disallowedBySystem ? false : enabled)
+                .setButton2OnClickListener(
+                        disallowedBySystem ? null : v -> mParent.handleForceStopButtonClick());
+    }
+
+    void checkForceStop(AppEntry appEntry, PackageInfo packageInfo) {
+        if (mDpm.packageHasActiveAdmins(packageInfo.packageName)) {
+            // User can't force stop device admin.
+            Log.w(TAG, "User can't force stop device admin");
+            updateForceStopButton(false);
+        } else if (AppUtils.isInstant(packageInfo.applicationInfo)) {
+            updateForceStopButton(false);
+            mActionButtons.setButton2Visible(false);
+        } else if ((appEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) {
+            // If the app isn't explicitly stopped, then always show the
+            // force stop button.
+            Log.w(TAG, "App is not explicitly stopped");
+            updateForceStopButton(true);
+        } else {
+            final Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
+                    Uri.fromParts("package", appEntry.info.packageName, null));
+            intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { appEntry.info.packageName });
+            intent.putExtra(Intent.EXTRA_UID, appEntry.info.uid);
+            intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(appEntry.info.uid));
+            Log.d(TAG, "Sending broadcast to query restart status for "
+                    + appEntry.info.packageName);
+            mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
+                    mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
+        }
+    }
+
+    private boolean signaturesMatch(String pkg1, String pkg2) {
+        if (pkg1 != null && pkg2 != null) {
+            try {
+                return mPm.checkSignatures(pkg1, pkg2) >= PackageManager.SIGNATURE_MATCH;
+            } catch (Exception e) {
+                // e.g. named alternate package not found during lookup;
+                // this is an expected case sometimes
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
index 017afe7..ffe2bf3 100644
--- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
@@ -32,7 +32,6 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.Utils;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
 import com.android.settings.fuelgauge.BatteryEntry;
diff --git a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java
index 61f3e46..669bc5a 100644
--- a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java
@@ -34,7 +34,6 @@
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.datausage.AppDataUsage;
 import com.android.settings.datausage.DataUsageList;
 import com.android.settings.datausage.DataUsageUtils;
diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
new file mode 100755
index 0000000..57e2b0c
--- /dev/null
+++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
@@ -0,0 +1,831 @@
+/*
+ * 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.appinfo;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.DeviceAdminAdd;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.applications.manageapplications.ManageApplications;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settings.wrapper.DevicePolicyManagerWrapper;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Dashboard fragment to display application information from Settings. This activity presents
+ * extended information associated with a package like code, data, total size, permissions
+ * used by the application and also the set of default launchable activities.
+ * For system applications, an option to clear user data is displayed only if data size is > 0.
+ * System applications that do not want clear user data do not have this option.
+ * For non-system applications, there is no option to clear data. Instead there is an option to
+ * uninstall the application.
+ */
+public class AppInfoDashboardFragment extends DashboardFragment
+        implements ApplicationsState.Callbacks {
+
+    private static final String TAG = "AppInfoDashboard";
+
+    // Menu identifiers
+    private static final int UNINSTALL_ALL_USERS_MENU = 1;
+    private static final int UNINSTALL_UPDATES = 2;
+
+    // Result code identifiers
+    @VisibleForTesting
+    static final int REQUEST_UNINSTALL = 0;
+    private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
+
+    static final int SUB_INFO_FRAGMENT = 1;
+
+    static final int LOADER_CHART_DATA = 2;
+    static final int LOADER_STORAGE = 3;
+    static final int LOADER_BATTERY = 4;
+
+    // Dialog identifiers used in showDialog
+    private static final int DLG_BASE = 0;
+    private static final int DLG_FORCE_STOP = DLG_BASE + 1;
+    private static final int DLG_DISABLE = DLG_BASE + 2;
+    private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3;
+
+    private static final String KEY_HEADER = "header_view";
+    private static final String KEY_ADVANCED_APP_INFO_CATEGORY = "advanced_app_info";
+
+    public static final String ARG_PACKAGE_NAME = "package";
+    public static final String ARG_PACKAGE_UID = "uid";
+
+    private static final boolean localLOGV = false;
+
+    private EnforcedAdmin mAppsControlDisallowedAdmin;
+    private boolean mAppsControlDisallowedBySystem;
+
+    private ApplicationsState mState;
+    private ApplicationsState.Session mSession;
+    private ApplicationsState.AppEntry mAppEntry;
+    private PackageInfo mPackageInfo;
+    private int mUserId;
+    private String mPackageName;
+
+    private DevicePolicyManagerWrapper mDpm;
+    private UserManager mUserManager;
+    private PackageManager mPm;
+
+    private boolean mFinishing;
+    private boolean mListeningToPackageRemove;
+
+
+    private boolean mInitialized;
+    private boolean mShowUninstalled;
+    private LayoutPreference mHeader;
+    private boolean mUpdatedSysApp = false;
+    private boolean mDisableAfterUninstall;
+
+    private List<Callback> mCallbacks = new ArrayList<>();
+
+    private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController;
+    private AppActionButtonPreferenceController mAppActionButtonPreferenceController;
+
+    /**
+     * Callback to invoke when app info has been changed.
+     */
+    public interface Callback {
+        void refreshUi();
+    }
+
+    private boolean isDisabledUntilUsed() {
+        return mAppEntry.info.enabledSetting
+                == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
+    }
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mFinishing = false;
+        final Activity activity = getActivity();
+        mDpm = new DevicePolicyManagerWrapper(
+                (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE));
+        mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
+        mPm = activity.getPackageManager();
+
+        retrieveAppEntry();
+        startListeningToPackageRemove();
+
+        if (!ensurePackageInfoAvailable(activity)) {
+            return;
+        }
+
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.APPLICATIONS_INSTALLED_APP_DETAILS;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mAppsControlDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(getActivity(),
+                UserManager.DISALLOW_APPS_CONTROL, mUserId);
+        mAppsControlDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(getActivity(),
+                UserManager.DISALLOW_APPS_CONTROL, mUserId);
+
+        if (!refreshUi()) {
+            setIntentAndFinish(true, true);
+        }
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.app_info_settings;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        final String packageName = getPackageName();
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        final Lifecycle lifecycle = getLifecycle();
+
+        // The following are controllers for preferences that needs to refresh the preference state
+        // when app state changes.
+        controllers.add(new AppStoragePreferenceController(context, this, lifecycle));
+        controllers.add(new AppDataUsagePreferenceController(context, this, lifecycle));
+        controllers.add(new AppNotificationPreferenceController(context, this));
+        controllers.add(new AppOpenByDefaultPreferenceController(context, this));
+        controllers.add(new AppPermissionPreferenceController(context, this, packageName));
+        controllers.add(new AppVersionPreferenceController(context, this));
+        controllers.add(new InstantAppDomainsPreferenceController(context, this));
+        final AppInstallerInfoPreferenceController appInstallerInfoPreferenceController =
+                new AppInstallerInfoPreferenceController(context, this, packageName);
+        controllers.add(appInstallerInfoPreferenceController);
+        mAppActionButtonPreferenceController =
+                new AppActionButtonPreferenceController(context, this, packageName);
+        controllers.add(mAppActionButtonPreferenceController);
+
+        for (AbstractPreferenceController controller : controllers) {
+            mCallbacks.add((Callback) controller);
+        }
+
+        // The following are controllers for preferences that don't need to refresh the preference
+        // state when app state changes.
+        mInstantAppButtonPreferenceController =
+                new InstantAppButtonsPreferenceController(context, this, packageName);
+        controllers.add(mInstantAppButtonPreferenceController);
+        controllers.add(new AppBatteryPreferenceController(context, this, packageName, lifecycle));
+        controllers.add(new AppMemoryPreferenceController(context, this, lifecycle));
+        controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName));
+        controllers.add(new DefaultBrowserShortcutPreferenceController(context, packageName));
+        controllers.add(new DefaultPhoneShortcutPreferenceController(context, packageName));
+        controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName));
+        controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName));
+
+        final List<AbstractPreferenceController> advancedAppInfoControllers = new ArrayList<>();
+        advancedAppInfoControllers.add(new DrawOverlayDetailPreferenceController(context, this));
+        advancedAppInfoControllers.add(new WriteSystemSettingsPreferenceController(context, this));
+        advancedAppInfoControllers.add(
+                new PictureInPictureDetailPreferenceController(context, this, packageName));
+        advancedAppInfoControllers.add(
+                new ExternalSourceDetailPreferenceController(context, this, packageName));
+        controllers.addAll(advancedAppInfoControllers);
+        controllers.add(new PreferenceCategoryController(
+                context, KEY_ADVANCED_APP_INFO_CATEGORY, advancedAppInfoControllers));
+
+        controllers.add(new AppInstallerPreferenceCategoryController(
+                context, Arrays.asList(appInstallerInfoPreferenceController)));
+
+        return controllers;
+    }
+
+    ApplicationsState.AppEntry getAppEntry() {
+        if (mAppEntry == null) {
+            retrieveAppEntry();
+        }
+        return mAppEntry;
+    }
+
+    PackageInfo getPackageInfo() {
+        if (mAppEntry == null) {
+            retrieveAppEntry();
+        }
+        return mPackageInfo;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        if (mFinishing) {
+            return;
+        }
+        final Activity activity = getActivity();
+        mHeader = (LayoutPreference) findPreference(KEY_HEADER);
+        EntityHeaderController.newInstance(activity, this, mHeader.findViewById(R.id.entity_header))
+                .setRecyclerView(getListView(), getLifecycle())
+                .setPackageName(mPackageName)
+                .setHasAppInfoLink(false)
+                .setButtonActions(EntityHeaderController.ActionType.ACTION_APP_PREFERENCE,
+                        EntityHeaderController.ActionType.ACTION_NONE)
+                .styleActionBar(activity)
+                .bindHeaderButtons();
+
+    }
+
+    @Override
+    public void onPackageSizeChanged(String packageName) {
+        if (!TextUtils.equals(packageName, mPackageName)) {
+            Log.d(TAG, "Package change irrelevant, skipping");
+          return;
+        }
+        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(TAG, "Package info not available. Is this package already uninstalled?");
+            activity.finishAndRemoveTask();
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+        menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset)
+                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+        menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
+                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        if (mFinishing) {
+            return;
+        }
+        menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry));
+        mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+        final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES);
+        uninstallUpdatesItem.setVisible(mUpdatedSysApp && !mAppsControlDisallowedBySystem);
+        if (uninstallUpdatesItem.isVisible()) {
+            RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getActivity(),
+                    uninstallUpdatesItem, mAppsControlDisallowedAdmin);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case UNINSTALL_ALL_USERS_MENU:
+                uninstallPkg(mAppEntry.info.packageName, true, false);
+                return true;
+            case UNINSTALL_UPDATES:
+                uninstallPkg(mAppEntry.info.packageName, false, false);
+                return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case REQUEST_UNINSTALL:
+                // Refresh option menu
+                getActivity().invalidateOptionsMenu();
+
+                if (mDisableAfterUninstall) {
+                    mDisableAfterUninstall = false;
+                    new DisableChanger(this, mAppEntry.info,
+                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
+                            .execute((Object) null);
+                }
+                // continue with following operations
+            case REQUEST_REMOVE_DEVICE_ADMIN:
+                if (!refreshUi()) {
+                    setIntentAndFinish(true, true);
+                } else {
+                    startListeningToPackageRemove();
+                }
+                break;
+        }
+    }
+
+    // Utility method to set application label and icon.
+    private void setAppLabelAndIcon(PackageInfo pkgInfo) {
+        final View appSnippet = mHeader.findViewById(R.id.entity_header);
+        mState.ensureIcon(mAppEntry);
+        final Activity activity = getActivity();
+        final boolean isInstantApp = AppUtils.isInstant(mPackageInfo.applicationInfo);
+        final CharSequence summary =
+                isInstantApp ? null : getString(Utils.getInstallationStatus(mAppEntry.info));
+        EntityHeaderController.newInstance(activity, this, appSnippet)
+                .setLabel(mAppEntry)
+                .setIcon(mAppEntry)
+                .setSummary(summary)
+                .setIsInstantApp(isInstantApp)
+                .done(activity, false /* rebindActions */);
+    }
+
+    @VisibleForTesting
+    boolean shouldShowUninstallForAll(AppEntry appEntry) {
+        boolean showIt = true;
+        if (mUpdatedSysApp) {
+            showIt = false;
+        } else if (appEntry == null) {
+            showIt = false;
+        } else if ((appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+            showIt = false;
+        } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
+            showIt = false;
+        } else if (UserHandle.myUserId() != 0) {
+            showIt = false;
+        } else if (mUserManager.getUsers().size() < 2) {
+            showIt = false;
+        } else if (getNumberOfUserWithPackageInstalled(mPackageName) < 2
+                && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+            showIt = false;
+        } else if (AppUtils.isInstant(appEntry.info)) {
+            showIt = false;
+        }
+        return showIt;
+    }
+
+    @VisibleForTesting
+    boolean refreshUi() {
+        retrieveAppEntry();
+        if (mAppEntry == null) {
+            return false; // onCreate must have failed, make sure to exit
+        }
+
+        if (mPackageInfo == null) {
+            return false; // onCreate must have failed, make sure to exit
+        }
+
+
+        setAppLabelAndIcon(mPackageInfo);
+
+        // Update the preference summaries.
+        final Activity context = getActivity();
+        for (Callback callback : mCallbacks) {
+            callback.refreshUi();
+        }
+
+        if (!mInitialized) {
+            // First time init: are we displaying an uninstalled app?
+            mInitialized = true;
+            mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0;
+        } else {
+            // All other times: if the app no longer exists then we want
+            // to go away.
+            try {
+                final ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo(
+                        mAppEntry.info.packageName,
+                        PackageManager.MATCH_DISABLED_COMPONENTS
+                        | PackageManager.MATCH_ANY_USER);
+                if (!mShowUninstalled) {
+                    // If we did not start out with the app uninstalled, then
+                    // it transitioning to the uninstalled state for the current
+                    // user means we should go away as well.
+                    return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0;
+                }
+            } catch (NameNotFoundException e) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @VisibleForTesting
+    AlertDialog createDialog(int id, int errorCode) {
+        switch (id) {
+            case DLG_DISABLE:
+                return new AlertDialog.Builder(getActivity())
+                        .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
+                        .setPositiveButton(R.string.app_disable_dlg_positive,
+                                new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) {
+                                // Disable the app
+                                mMetricsFeatureProvider.action(getContext(),
+                                        MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
+                                new DisableChanger(AppInfoDashboardFragment.this, mAppEntry.info,
+                                        PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
+                                .execute((Object)null);
+                            }
+                        })
+                        .setNegativeButton(R.string.dlg_cancel, null)
+                        .create();
+            case DLG_SPECIAL_DISABLE:
+                return new AlertDialog.Builder(getActivity())
+                        .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
+                        .setPositiveButton(R.string.app_disable_dlg_positive,
+                                new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) {
+                                // Disable the app and ask for uninstall
+                                mMetricsFeatureProvider.action(getContext(),
+                                        MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
+                                uninstallPkg(mAppEntry.info.packageName,
+                                        false, true);
+                            }
+                        })
+                        .setNegativeButton(R.string.dlg_cancel, null)
+                        .create();
+            case DLG_FORCE_STOP:
+                return new AlertDialog.Builder(getActivity())
+                        .setTitle(getActivity().getText(R.string.force_stop_dlg_title))
+                        .setMessage(getActivity().getText(R.string.force_stop_dlg_text))
+                        .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) {
+                                // Force stop
+                                forceStopPackage(mAppEntry.info.packageName);
+                            }
+                        })
+                        .setNegativeButton(R.string.dlg_cancel, null)
+                        .create();
+        }
+        return mInstantAppButtonPreferenceController.createDialog(id);
+    }
+
+    private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
+        stopListeningToPackageRemove();
+         // Create new intent to launch Uninstaller activity
+        final Uri packageURI = Uri.parse("package:"+packageName);
+        final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
+        uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
+        mMetricsFeatureProvider.action(
+                getContext(), MetricsEvent.ACTION_SETTINGS_UNINSTALL_APP);
+        startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
+        mDisableAfterUninstall = andDisable;
+    }
+
+    private void forceStopPackage(String pkgName) {
+        mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_APP_FORCE_STOP, pkgName);
+        final ActivityManager am = (ActivityManager) getActivity().getSystemService(
+                Context.ACTIVITY_SERVICE);
+        Log.d(TAG, "Stopping package " + pkgName);
+        am.forceStopPackage(pkgName);
+        final int userId = UserHandle.getUserId(mAppEntry.info.uid);
+        mState.invalidatePackage(pkgName, userId);
+        final AppEntry newEnt = mState.getEntry(pkgName, userId);
+        if (newEnt != null) {
+            mAppEntry = newEnt;
+        }
+        mAppActionButtonPreferenceController.checkForceStop(mAppEntry, mPackageInfo);
+    }
+
+    public static void startAppInfoFragment(Class<?> fragment, int title,
+            SettingsPreferenceFragment caller, AppEntry appEntry) {
+        // start new fragment to display extended information
+        final Bundle args = new Bundle();
+        args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName);
+        args.putInt(ARG_PACKAGE_UID, appEntry.info.uid);
+
+        final SettingsActivity sa = (SettingsActivity) caller.getActivity();
+        sa.startPreferencePanel(caller, fragment.getName(), args, title, null, caller,
+                SUB_INFO_FRAGMENT);
+    }
+
+    void handleUninstallButtonClick() {
+        if (mAppEntry == null) {
+            setIntentAndFinish(true, true);
+            return;
+        }
+        final String packageName = mAppEntry.info.packageName;
+        if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
+            stopListeningToPackageRemove();
+            final Activity activity = getActivity();
+            final Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class);
+            uninstallDAIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME,
+                    mPackageName);
+            mMetricsFeatureProvider.action(
+                    activity, MetricsEvent.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN);
+            activity.startActivityForResult(uninstallDAIntent, REQUEST_REMOVE_DEVICE_ADMIN);
+            return;
+        }
+        final EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(),
+                packageName, mUserId);
+        final boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem ||
+                RestrictedLockUtils.hasBaseUserRestriction(getActivity(), packageName, mUserId);
+        if (admin != null && !uninstallBlockedBySystem) {
+            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), admin);
+        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+            if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
+                // If the system app has an update and this is the only user on the device,
+                // then offer to downgrade the app, otherwise only offer to disable the
+                // app for this user.
+                if (mUpdatedSysApp && isSingleUser()) {
+                    showDialogInner(DLG_SPECIAL_DISABLE, 0);
+                } else {
+                    showDialogInner(DLG_DISABLE, 0);
+                }
+            } else {
+                mMetricsFeatureProvider.action(
+                        getActivity(),
+                        MetricsEvent.ACTION_SETTINGS_ENABLE_APP);
+                new DisableChanger(this, mAppEntry.info,
+                        PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
+                        .execute((Object) null);
+            }
+        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+            uninstallPkg(packageName, true, false);
+        } else {
+            uninstallPkg(packageName, false, false);
+        }
+    }
+
+    void handleForceStopButtonClick() {
+        if (mAppEntry == null) {
+            setIntentAndFinish(true, true);
+            return;
+        }
+        if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
+            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
+                    getActivity(), mAppsControlDisallowedAdmin);
+        } else {
+            showDialogInner(DLG_FORCE_STOP, 0);
+            //forceStopPackage(mAppInfo.packageName);
+        }
+    }
+
+    /** Returns whether there is only one user on this device, not including the system-only user */
+    private boolean isSingleUser() {
+        final int userCount = mUserManager.getUserCount();
+        return userCount == 1 || (mUserManager.isSplitSystemUser() && userCount == 2);
+    }
+
+    private void onPackageRemoved() {
+        getActivity().finishActivity(SUB_INFO_FRAGMENT);
+        getActivity().finishAndRemoveTask();
+    }
+
+    @VisibleForTesting
+    int getNumberOfUserWithPackageInstalled(String packageName) {
+        final List<UserInfo> userInfos = mUserManager.getUsers(true);
+        int count = 0;
+
+        for (final UserInfo userInfo : userInfos) {
+            try {
+                // Use this API to check whether user has this package
+                final ApplicationInfo info = mPm.getApplicationInfoAsUser(
+                        packageName, PackageManager.GET_META_DATA, userInfo.id);
+                if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+                    count++;
+                }
+            } catch(NameNotFoundException e) {
+                Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id);
+            }
+        }
+
+        return count;
+    }
+
+    private static class DisableChanger extends AsyncTask<Object, Object, Object> {
+        final PackageManager mPm;
+        final WeakReference<AppInfoDashboardFragment> mActivity;
+        final ApplicationInfo mInfo;
+        final int mState;
+
+        DisableChanger(AppInfoDashboardFragment activity, ApplicationInfo info, int state) {
+            mPm = activity.mPm;
+            mActivity = new WeakReference<AppInfoDashboardFragment>(activity);
+            mInfo = info;
+            mState = state;
+        }
+
+        @Override
+        protected Object doInBackground(Object... params) {
+            mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
+            return null;
+        }
+    }
+
+    private String getPackageName() {
+        if (mPackageName != null) {
+            return mPackageName;
+        }
+        final Bundle args = getArguments();
+        mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
+        if (mPackageName == null) {
+            final Intent intent = (args == null) ?
+                    getActivity().getIntent() : (Intent) args.getParcelable("intent");
+            if (intent != null) {
+                mPackageName = intent.getData().getSchemeSpecificPart();
+            }
+        }
+        return mPackageName;
+    }
+
+    private void retrieveAppEntry() {
+        final Activity activity = getActivity();
+        if (activity == null) {
+            return;
+        }
+        if (mState == null) {
+            mState = ApplicationsState.getInstance(activity.getApplication());
+            mSession = mState.newSession(this, getLifecycle());
+        }
+        mUserId = UserHandle.myUserId();
+        mAppEntry = mState.getEntry(getPackageName(), UserHandle.myUserId());
+        if (mAppEntry != null) {
+            // Get application info again to refresh changed properties of application
+            try {
+                mPackageInfo = activity.getPackageManager().getPackageInfo(
+                        mAppEntry.info.packageName,
+                        PackageManager.MATCH_DISABLED_COMPONENTS |
+                                PackageManager.MATCH_ANY_USER |
+                                PackageManager.GET_SIGNATURES |
+                                PackageManager.GET_PERMISSIONS);
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
+            }
+        } else {
+            Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
+            mPackageInfo = null;
+        }
+    }
+
+    private void setIntentAndFinish(boolean finish, boolean appChanged) {
+        if (localLOGV) Log.i(TAG, "appChanged="+appChanged);
+        final Intent intent = new Intent();
+        intent.putExtra(ManageApplications.APP_CHG, appChanged);
+        final SettingsActivity sa = (SettingsActivity)getActivity();
+        sa.finishPreferencePanel(this, Activity.RESULT_OK, intent);
+        mFinishing = true;
+    }
+
+    void showDialogInner(int id, int moveErrorCode) {
+        final DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode);
+        newFragment.setTargetFragment(this, 0);
+        newFragment.show(getFragmentManager(), "dialog " + id);
+    }
+
+    @Override
+    public void onRunningStateChanged(boolean running) {
+        // No op.
+    }
+
+    @Override
+    public void onRebuildComplete(ArrayList<AppEntry> apps) {
+        // No op.
+    }
+
+    @Override
+    public void onPackageIconChanged() {
+        // No op.
+    }
+
+    @Override
+    public void onAllSizesComputed() {
+        // No op.
+    }
+
+    @Override
+    public void onLauncherInfoChanged() {
+        // No op.
+    }
+
+    @Override
+    public void onLoadEntriesCompleted() {
+        // No op.
+    }
+
+    @Override
+    public void onPackageListChanged() {
+        if (!refreshUi()) {
+            setIntentAndFinish(true, true);
+        }
+    }
+
+    public static class MyAlertDialogFragment extends InstrumentedDialogFragment {
+
+        private static final String ARG_ID = "id";
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsEvent.DIALOG_APP_INFO_ACTION;
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final int id = getArguments().getInt(ARG_ID);
+            final int errorCode = getArguments().getInt("moveError");
+            final Dialog dialog =
+                    ((AppInfoDashboardFragment) getTargetFragment()).createDialog(id, errorCode);
+            if (dialog == null) {
+                throw new IllegalArgumentException("unknown id " + id);
+            }
+            return dialog;
+        }
+
+        public static MyAlertDialogFragment newInstance(int id, int errorCode) {
+            final MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment();
+            final Bundle args = new Bundle();
+            args.putInt(ARG_ID, id);
+            args.putInt("moveError", errorCode);
+            dialogFragment.setArguments(args);
+            return dialogFragment;
+        }
+    }
+
+    private void startListeningToPackageRemove() {
+        if (mListeningToPackageRemove) {
+            return;
+        }
+        mListeningToPackageRemove = true;
+        final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        getContext().registerReceiver(mPackageRemovedReceiver, filter);
+    }
+
+    private void stopListeningToPackageRemove() {
+        if (!mListeningToPackageRemove) {
+            return;
+        }
+        mListeningToPackageRemove = false;
+        getContext().unregisterReceiver(mPackageRemovedReceiver);
+    }
+
+    private final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String packageName = intent.getData().getSchemeSpecificPart();
+            if (!mFinishing && (mAppEntry == null || mAppEntry.info == null
+                    || TextUtils.equals(mAppEntry.info.packageName, packageName))) {
+                onPackageRemoved();
+            }
+        }
+    };
+
+}
diff --git a/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java b/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java
index eac0a0c..105a01e 100644
--- a/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java
@@ -22,7 +22,6 @@
 import android.text.TextUtils;
 
 import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.core.BasePreferenceController;
 
 /*
diff --git a/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java b/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java
new file mode 100644
index 0000000..1fdc690
--- /dev/null
+++ b/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java
@@ -0,0 +1,68 @@
+/*
+ * 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.appinfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.applications.AppStoreUtil;
+import com.android.settingslib.applications.AppUtils;
+
+public class AppInstallerInfoPreferenceController extends AppInfoPreferenceControllerBase {
+
+    private static final String KEY_APP_INSTALLER_INFO = "app_info_store";
+
+    private final String mPackageName;
+    private final String mInstallerPackage;
+    private final CharSequence mInstallerLabel;
+
+    public AppInstallerInfoPreferenceController(Context context, AppInfoDashboardFragment parent,
+            String packageName) {
+        super(context, parent, KEY_APP_INSTALLER_INFO);
+        mPackageName = packageName;
+        mInstallerPackage = AppStoreUtil.getInstallerPackageName(mContext, mPackageName);
+        mInstallerLabel = Utils.getApplicationLabel(mContext, mInstallerPackage);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        if (UserManager.get(mContext).isManagedProfile()) {
+            return DISABLED_FOR_USER;
+        }
+        return mInstallerLabel!= null ? AVAILABLE : DISABLED_FOR_USER;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        final int detailsStringId = AppUtils.isInstant(mParent.getPackageInfo().applicationInfo)
+                ? R.string.instant_app_details_summary
+                : R.string.app_install_details_summary;
+        preference.setSummary(mContext.getString(detailsStringId, mInstallerLabel));
+
+        Intent intent = AppStoreUtil.getAppStoreLink(mContext, mInstallerPackage, mPackageName);
+        if (intent != null) {
+            preference.setIntent(intent);
+        } else {
+            preference.setEnabled(false);
+        }
+    }
+
+}
diff --git a/src/com/android/settings/applications/appinfo/AppInstallerPreferenceCategoryController.java b/src/com/android/settings/applications/appinfo/AppInstallerPreferenceCategoryController.java
new file mode 100644
index 0000000..0e6ffe8
--- /dev/null
+++ b/src/com/android/settings/applications/appinfo/AppInstallerPreferenceCategoryController.java
@@ -0,0 +1,35 @@
+/*
+ * 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.appinfo;
+
+import android.content.Context;
+
+import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.List;
+
+public class AppInstallerPreferenceCategoryController extends PreferenceCategoryController {
+
+    private static final String KEY_APP_INSTALLER_INFO_CATEGORY = "app_installer";
+
+    public AppInstallerPreferenceCategoryController(Context context,
+            List<AbstractPreferenceController> childrenControllers) {
+        super(context, KEY_APP_INSTALLER_INFO_CATEGORY, childrenControllers);
+    }
+
+}
diff --git a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
index 3943041..7b497a9 100644
--- a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
@@ -26,7 +26,6 @@
 
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.applications.ProcStatsData;
 import com.android.settings.applications.ProcStatsEntry;
 import com.android.settings.applications.ProcStatsPackageEntry;
diff --git a/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java b/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java
index 7eef370..1f19504 100644
--- a/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java
@@ -20,7 +20,6 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.notification.AppNotificationSettings;
 import com.android.settings.notification.NotificationBackend;
 import com.android.settingslib.applications.ApplicationsState;
diff --git a/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java b/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java
index a56e3fb..3f20381 100644
--- a/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java
@@ -26,7 +26,6 @@
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.applications.AppLaunchSettings;
 import com.android.settingslib.applications.AppUtils;
 import com.android.settingslib.applications.ApplicationsState;
diff --git a/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java b/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java
index 815e8d8..b844f78 100644
--- a/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java
@@ -26,7 +26,6 @@
 import android.util.Log;
 
 import com.android.settings.R;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settingslib.applications.PermissionsSummaryHelper;
 
 import java.util.ArrayList;
diff --git a/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java b/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java
index d737288..86383cb 100644
--- a/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java
@@ -28,7 +28,6 @@
 
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.applications.AppStorageSettings;
 import com.android.settings.applications.FetchPackageStorageAsyncLoader;
 import com.android.settingslib.applications.StorageStatsSource;
diff --git a/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java b/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java
index 82719f7..0cfeb008 100644
--- a/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java
@@ -21,7 +21,6 @@
 import android.text.BidiFormatter;
 
 import com.android.settings.R;
-import com.android.settings.applications.AppInfoDashboardFragment;
 
 public class AppVersionPreferenceController extends AppInfoPreferenceControllerBase {
 
diff --git a/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceController.java b/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceController.java
index 314d799..37a9edf 100644
--- a/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceController.java
@@ -25,7 +25,6 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppInfoDashboardFragment;
 
 public class DrawOverlayDetailPreferenceController extends AppInfoPreferenceControllerBase {
 
diff --git a/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceController.java b/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceController.java
index 4ac67ed..6fb6dc3 100644
--- a/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceController.java
@@ -22,7 +22,6 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.applications.AppStateInstallAppsBridge;
 
 public class ExternalSourceDetailPreferenceController extends AppInfoPreferenceControllerBase {
diff --git a/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java b/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java
index 0400066..87e5fdb 100644
--- a/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java
+++ b/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java
@@ -107,6 +107,9 @@
 
     @Override
     protected boolean refreshUi() {
+        if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
+            return false;
+        }
         if (mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                 UserHandle.of(UserHandle.myUserId()))) {
             mSwitchPref.setChecked(false);
diff --git a/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java
new file mode 100644
index 0000000..b9fe003
--- /dev/null
+++ b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java
@@ -0,0 +1,74 @@
+/*
+ * 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.appinfo;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.applications.instantapps.InstantAppButtonsController;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.applications.AppUtils;
+
+public class InstantAppButtonsPreferenceController extends BasePreferenceController {
+
+    private static final String KEY_INSTANT_APP_BUTTONS = "instant_app_buttons";
+
+    private final AppInfoDashboardFragment mParent;
+    private final String mPackageName;
+    private InstantAppButtonsController mInstantAppButtonsController;
+
+    public InstantAppButtonsPreferenceController(Context context, AppInfoDashboardFragment parent,
+            String packageName) {
+        super(context, KEY_INSTANT_APP_BUTTONS);
+        mParent = parent;
+        mPackageName = packageName;
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AppUtils.isInstant(mParent.getPackageInfo().applicationInfo)
+                ? AVAILABLE : DISABLED_FOR_USER;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        LayoutPreference buttons =
+                (LayoutPreference) screen.findPreference(KEY_INSTANT_APP_BUTTONS);
+        mInstantAppButtonsController = getApplicationFeatureProvider()
+                .newInstantAppButtonsController(mParent,
+                        buttons.findViewById(R.id.instant_app_button_container),
+                        id -> mParent.showDialogInner(id, 0))
+                .setPackageName(mPackageName)
+                .show();
+    }
+
+    public AlertDialog createDialog(int id) {
+        return mInstantAppButtonsController.createDialog(id);
+    }
+
+    @VisibleForTesting
+    ApplicationFeatureProvider getApplicationFeatureProvider() {
+        return FeatureFactory.getFactory(mContext).getApplicationFeatureProvider(mContext);
+    }
+}
diff --git a/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java
new file mode 100644
index 0000000..d89c538
--- /dev/null
+++ b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java
@@ -0,0 +1,59 @@
+/*
+ * 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.appinfo;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.Utils;
+import com.android.settings.applications.AppDomainsPreference;
+import com.android.settingslib.applications.AppUtils;
+
+import java.util.Set;
+
+public class InstantAppDomainsPreferenceController extends AppInfoPreferenceControllerBase {
+
+    private static final String KEY_INSTANT_APP_SUPPORTED_LINKS =
+            "instant_app_launch_supported_domain_urls";
+
+    private PackageManager mPackageManager;
+
+    public InstantAppDomainsPreferenceController(Context context, AppInfoDashboardFragment parent) {
+        super(context, parent, KEY_INSTANT_APP_SUPPORTED_LINKS);
+        mPackageManager = mContext.getPackageManager();
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AppUtils.isInstant(mParent.getPackageInfo().applicationInfo)
+                ? AVAILABLE : DISABLED_FOR_USER;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        final AppDomainsPreference instantAppDomainsPreference = (AppDomainsPreference) preference;
+        final Set<String> handledDomainSet =
+                Utils.getHandledDomains(mPackageManager, mParent.getPackageInfo().packageName);
+        final String[] handledDomains =
+                handledDomainSet.toArray(new String[handledDomainSet.size()]);
+        instantAppDomainsPreference.setTitles(handledDomains);
+        // Dummy values, unused in the implementation
+        instantAppDomainsPreference.setValues(new int[handledDomains.length]);
+    }
+
+}
diff --git a/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceController.java b/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceController.java
index aea6bae..1873683 100644
--- a/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceController.java
@@ -26,7 +26,6 @@
 import android.util.Log;
 
 import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppInfoDashboardFragment;
 
 public class PictureInPictureDetailPreferenceController extends AppInfoPreferenceControllerBase {
 
diff --git a/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceController.java b/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceController.java
index 55b181a..2a88d2f 100644
--- a/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceController.java
@@ -25,7 +25,6 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppInfoDashboardFragment;
 
 public class WriteSystemSettingsPreferenceController extends AppInfoPreferenceControllerBase {
 
diff --git a/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java b/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java
index 28e612c..42474a8 100644
--- a/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java
+++ b/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.UserHandle;
 import android.view.View;
 import android.widget.Button;
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 067e167..7371294 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -93,7 +93,7 @@
 import com.android.settings.applications.InstalledAppDetails;
 import com.android.settings.applications.NotificationApps;
 import com.android.settings.applications.UsageAccessDetails;
-import com.android.settings.applications.AppInfoDashboardFragment;
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
 import com.android.settings.applications.appinfo.DrawOverlayDetails;
 import com.android.settings.applications.appinfo.ExternalSourcesDetails;
 import com.android.settings.applications.appinfo.WriteSettingsDetails;
diff --git a/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java b/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java
index afc13b4..7a7530c 100644
--- a/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java
+++ b/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java
@@ -22,31 +22,29 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.core.AbstractPreferenceController;
 
-public class BackupSettingsActivityPreferenceController extends
-        AbstractPreferenceController implements PreferenceControllerMixin {
+public class BackupSettingsActivityPreferenceController extends BasePreferenceController {
+    private static final String TAG = "BackupSettingActivityPC";
+
     private static final String KEY_BACKUP_SETTINGS = "backup_settings";
-    private static final String TAG = "BackupSettingActivityPC" ;
 
     private final UserManager mUm;
     private final BackupManager mBackupManager;
 
     public BackupSettingsActivityPreferenceController(Context context) {
-        super(context);
+        super(context, KEY_BACKUP_SETTINGS);
         mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mBackupManager = new BackupManager(context);
     }
 
     @Override
-    public boolean isAvailable() {
-        return mUm.isAdminUser();
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_BACKUP_SETTINGS;
+    public int getAvailabilityStatus() {
+        return mUm.isAdminUser()
+                ? AVAILABLE
+                : DISABLED_UNSUPPORTED;
     }
 
     @Override
@@ -57,4 +55,4 @@
                 ? R.string.accessibility_feature_state_on
                 : R.string.accessibility_feature_state_off);
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java b/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java
index 8b07bcb..2d0ce60 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java
@@ -29,10 +29,9 @@
 import android.util.Log;
 
 import com.android.settings.R;
-import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
@@ -41,8 +40,8 @@
 /**
  * Controller that shows and updates the bluetooth device name
  */
-public class BluetoothDeviceNamePreferenceController extends AbstractPreferenceController
-        implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
+public class BluetoothDeviceNamePreferenceController extends BasePreferenceController implements
+        LifecycleObserver, OnStart, OnStop {
     private static final String TAG = "BluetoothNamePrefCtrl";
 
     public static final String KEY_DEVICE_NAME = "device_name";
@@ -62,12 +61,22 @@
             return;
         }
         mLocalAdapter = mLocalManager.getBluetoothAdapter();
-        lifecycle.addObserver(this);
+
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    /**
+     * Constructor exclusively used for Slice.
+     */
+    public BluetoothDeviceNamePreferenceController(Context context) {
+        this(context, (Lifecycle) null);
     }
 
     @VisibleForTesting
     BluetoothDeviceNamePreferenceController(Context context, LocalBluetoothAdapter localAdapter) {
-        super(context);
+        super(context, KEY_DEVICE_NAME);
         mLocalAdapter = localAdapter;
     }
 
@@ -89,8 +98,8 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        return mLocalAdapter != null;
+    public int getAvailabilityStatus() {
+        return mLocalAdapter != null ? AVAILABLE : DISABLED_UNSUPPORTED;
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java b/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java
index b64da26..69eefcf 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
+import android.text.TextUtils;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
@@ -30,29 +31,39 @@
 public class BluetoothDeviceRenamePreferenceController extends
         BluetoothDeviceNamePreferenceController {
 
-    public static final String PREF_KEY = "bt_rename_device";
-
     private final Fragment mFragment;
+    private String mPrefKey;
     private MetricsFeatureProvider mMetricsFeatureProvider;
 
-    public BluetoothDeviceRenamePreferenceController(Context context, Fragment fragment,
-            Lifecycle lifecycle) {
+    public BluetoothDeviceRenamePreferenceController(Context context, String prefKey,
+            Fragment fragment, Lifecycle lifecycle) {
         super(context, lifecycle);
+        mPrefKey = prefKey;
         mFragment = fragment;
         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
     }
 
+    /**
+     * Constructor exclusively used for Slice.
+     */
+    public BluetoothDeviceRenamePreferenceController(Context context, String prefKey) {
+        super(context, (Lifecycle) null);
+        mPrefKey = prefKey;
+        mFragment = null;
+    }
+
     @VisibleForTesting
-    BluetoothDeviceRenamePreferenceController(Context context, Fragment fragment,
+    BluetoothDeviceRenamePreferenceController(Context context, String prefKey, Fragment fragment,
             LocalBluetoothAdapter localAdapter) {
         super(context, localAdapter);
+        mPrefKey = prefKey;
         mFragment = fragment;
         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
     }
 
     @Override
     public String getPreferenceKey() {
-        return PREF_KEY;
+        return mPrefKey;
     }
 
     @Override
@@ -62,7 +73,7 @@
 
     @Override
     public boolean handlePreferenceTreeClick(Preference preference) {
-        if (PREF_KEY.equals(preference.getKey())) {
+        if (TextUtils.equals(mPrefKey, preference.getKey()) && mFragment != null) {
             mMetricsFeatureProvider.action(mContext,
                     MetricsProto.MetricsEvent.ACTION_BLUETOOTH_RENAME);
             LocalDeviceNameDialogFragment.newInstance()
diff --git a/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java
index d1492e4..331907b 100644
--- a/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java
@@ -38,6 +38,7 @@
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
 
+//TODO(b/69926683): remove this controller in Android P.
 public class BluetoothMasterSwitchPreferenceController extends AbstractPreferenceController
         implements PreferenceControllerMixin, OnSummaryChangeListener, LifecycleObserver, OnResume,
         OnPause, OnStart, OnStop {
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
index a9756a6..5e003fe 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
@@ -46,10 +46,9 @@
     static final String KEY_AVAIL_DEVICES = "available_devices";
     @VisibleForTesting
     static final String KEY_FOOTER_PREF = "footer_preference";
+    private static final String KEY_RENAME_DEVICES = "bt_pair_rename_devices";
 
     @VisibleForTesting
-    BluetoothDeviceNamePreferenceController mDeviceNamePrefController;
-    @VisibleForTesting
     BluetoothProgressCategory mAvailableDevicesCategory;
     @VisibleForTesting
     FooterPreference mFooterPreference;
@@ -195,10 +194,10 @@
 
     @Override
     protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
-        List<AbstractPreferenceController> controllers = new ArrayList<>();
-        mDeviceNamePrefController = new BluetoothDeviceNamePreferenceController(context,
-                getLifecycle());
-        controllers.add(mDeviceNamePrefController);
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(
+                new BluetoothDeviceRenamePreferenceController(context, KEY_RENAME_DEVICES, this,
+                        getLifecycle()));
 
         return controllers;
     }
diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java
index 72d8023..3acd477 100644
--- a/src/com/android/settings/bluetooth/BluetoothSettings.java
+++ b/src/com/android/settings/bluetooth/BluetoothSettings.java
@@ -73,6 +73,7 @@
     static final String KEY_PAIRED_DEVICES = "paired_devices";
     @VisibleForTesting
     static final String KEY_FOOTER_PREF = "footer_preference";
+    private static final String KEY_RENAME_DEVICES = "bt_rename_device";
 
     @VisibleForTesting
     PreferenceGroup mPairedDevicesCategory;
@@ -369,7 +370,9 @@
         controllers.add(mDeviceNamePrefController);
         controllers.add(mPairingPrefController);
         controllers.add(new BluetoothFilesPreferenceController(context));
-        controllers.add(new BluetoothDeviceRenamePreferenceController(context, this, lifecycle));
+        controllers.add(
+                new BluetoothDeviceRenamePreferenceController(context, KEY_RENAME_DEVICES, this,
+                        lifecycle));
 
         return controllers;
     }
diff --git a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
new file mode 100644
index 0000000..3482ee2
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
@@ -0,0 +1,161 @@
+/*
+ * 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.bluetooth;
+
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.SwitchWidgetController;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+/**
+ * PreferenceController to update of bluetooth {@link SwitchPreference}. It will
+ *
+ * 1. Invoke the user toggle
+ * 2. Listen to the update from {@link LocalBluetoothManager}
+ */
+public class BluetoothSwitchPreferenceController extends TogglePreferenceController
+        implements LifecycleObserver, OnStart, OnStop {
+
+    public static final String KEY_TOGGLE_BLUETOOTH = "toggle_bluetooth_switch";
+
+    private LocalBluetoothManager mBluetoothManager;
+    private SwitchPreference mBtPreference;
+    private BluetoothEnabler mBluetoothEnabler;
+    private RestrictionUtils mRestrictionUtils;
+    @VisibleForTesting
+    LocalBluetoothAdapter mBluetoothAdapter;
+
+    public BluetoothSwitchPreferenceController(Context context) {
+        this(context, Utils.getLocalBtManager(context), new RestrictionUtils());
+    }
+
+    @VisibleForTesting
+    public BluetoothSwitchPreferenceController(Context context,
+            LocalBluetoothManager bluetoothManager, RestrictionUtils restrictionUtils) {
+        super(context, KEY_TOGGLE_BLUETOOTH);
+        mBluetoothManager = bluetoothManager;
+        mRestrictionUtils = restrictionUtils;
+
+        if (mBluetoothManager != null) {
+            mBluetoothAdapter = mBluetoothManager.getBluetoothAdapter();
+        }
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mBtPreference = (SwitchPreference) screen.findPreference(KEY_TOGGLE_BLUETOOTH);
+        mBluetoothEnabler = new BluetoothEnabler(mContext,
+                new SwitchController(mBtPreference),
+                FeatureFactory.getFactory(mContext).getMetricsFeatureProvider(), mBluetoothManager,
+                MetricsEvent.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE,
+                mRestrictionUtils);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return mBluetoothAdapter != null ? AVAILABLE : DISABLED_UNSUPPORTED;
+    }
+
+    @Override
+    public void onStart() {
+        mBluetoothEnabler.resume(mContext);
+    }
+
+    @Override
+    public void onStop() {
+        mBluetoothEnabler.pause();
+    }
+
+    @Override
+    public boolean isChecked() {
+        return mBluetoothAdapter != null ? mBluetoothAdapter.isEnabled() : false;
+    }
+
+    @Override
+    public void setChecked(boolean isChecked) {
+        if (mBluetoothAdapter != null) {
+            mBluetoothAdapter.setBluetoothEnabled(isChecked);
+        }
+    }
+
+    /**
+     * Control the switch inside {@link SwitchPreference}
+     */
+    @VisibleForTesting
+    class SwitchController extends SwitchWidgetController implements
+            Preference.OnPreferenceChangeListener {
+        private SwitchPreference mSwitchPreference;
+
+        public SwitchController(SwitchPreference switchPreference) {
+            mSwitchPreference = switchPreference;
+        }
+
+        @Override
+        public void updateTitle(boolean isChecked) {
+        }
+
+        @Override
+        public void startListening() {
+            mSwitchPreference.setOnPreferenceChangeListener(this);
+        }
+
+        @Override
+        public void stopListening() {
+            mSwitchPreference.setOnPreferenceChangeListener(null);
+        }
+
+        @Override
+        public void setChecked(boolean checked) {
+            mSwitchPreference.setChecked(checked);
+        }
+
+        @Override
+        public boolean isChecked() {
+            return mSwitchPreference.isChecked();
+        }
+
+        @Override
+        public void setEnabled(boolean enabled) {
+            mSwitchPreference.setEnabled(enabled);
+        }
+
+        @Override
+        public boolean onPreferenceChange(Preference preference, Object newValue) {
+            if (mListener != null) {
+                return mListener.onSwitchToggled((Boolean) newValue);
+            }
+            return false;
+        }
+
+        @Override
+        public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
+            mBtPreference.setEnabled(admin == null);
+        }
+    }
+}
diff --git a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
old mode 100755
new mode 100644
index eae2f29..d13a85f
--- a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
+++ b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
@@ -172,7 +172,11 @@
         mProfileContainer.removeAllViews();
         for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
             CheckBox pref = createProfilePreference(profile);
-            mProfileContainer.addView(pref);
+            // MAP and PBAP profiles would be added based on permission access
+            if (!((profile instanceof PbapServerProfile) ||
+                (profile instanceof MapProfile))) {
+                mProfileContainer.addView(pref);
+            }
 
             if (profile instanceof A2dpProfile) {
                 BluetoothDevice device = mCachedDevice.getDevice();
@@ -191,6 +195,7 @@
         }
 
         final int pbapPermission = mCachedDevice.getPhonebookPermissionChoice();
+        Log.d(TAG, "addPreferencesForProfiles: pbapPermission = " + pbapPermission);
         // Only provide PBAP cabability if the client device has requested PBAP.
         if (pbapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
             final PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
@@ -200,6 +205,7 @@
 
         final MapProfile mapProfile = mManager.getProfileManager().getMapProfile();
         final int mapPermission = mCachedDevice.getMessagePermissionChoice();
+        Log.d(TAG, "addPreferencesForProfiles: mapPermission = " + mapPermission);
         if (mapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
             CheckBox mapPreference = createProfilePreference(mapProfile);
             mProfileContainer.addView(mapPreference);
@@ -251,15 +257,6 @@
     private void onProfileClicked(LocalBluetoothProfile profile, CheckBox profilePref) {
         BluetoothDevice device = mCachedDevice.getDevice();
 
-        if (KEY_PBAP_SERVER.equals(profilePref.getTag())) {
-            final int newPermission = mCachedDevice.getPhonebookPermissionChoice()
-                == CachedBluetoothDevice.ACCESS_ALLOWED ? CachedBluetoothDevice.ACCESS_REJECTED
-                : CachedBluetoothDevice.ACCESS_ALLOWED;
-            mCachedDevice.setPhonebookPermissionChoice(newPermission);
-            profilePref.setChecked(newPermission == CachedBluetoothDevice.ACCESS_ALLOWED);
-            return;
-        }
-
         if (!profilePref.isChecked()) {
             // Recheck it, until the dialog is done.
             profilePref.setChecked(true);
@@ -268,6 +265,12 @@
             if (profile instanceof MapProfile) {
                 mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_ALLOWED);
             }
+            if (profile instanceof PbapServerProfile) {
+                mCachedDevice.setPhonebookPermissionChoice(BluetoothDevice.ACCESS_ALLOWED);
+                refreshProfilePreference(profilePref, profile);
+                // PBAP server is not preffered profile and cannot initiate connection, so return
+                return;
+            }
             if (profile.isPreferred(device)) {
                 // profile is preferred but not connected: disable auto-connect
                 if (profile instanceof PanProfile) {
@@ -301,10 +304,17 @@
         DialogInterface.OnClickListener disconnectListener =
                 new DialogInterface.OnClickListener() {
             public void onClick(DialogInterface dialog, int which) {
-                device.disconnect(profile);
-                profile.setPreferred(device.getDevice(), false);
-                if (profile instanceof MapProfile) {
-                    device.setMessagePermissionChoice(BluetoothDevice.ACCESS_REJECTED);
+
+                // Disconnect only when user has selected OK otherwise ignore
+                if (which == DialogInterface.BUTTON_POSITIVE) {
+                    device.disconnect(profile);
+                    profile.setPreferred(device.getDevice(), false);
+                    if (profile instanceof MapProfile) {
+                        device.setMessagePermissionChoice(BluetoothDevice.ACCESS_REJECTED);
+                    }
+                    if (profile instanceof PbapServerProfile) {
+                        device.setPhonebookPermissionChoice(BluetoothDevice.ACCESS_REJECTED);
+                    }
                 }
                 refreshProfilePreference(findProfile(profile.toString()), profile);
             }
@@ -342,6 +352,19 @@
         for (LocalBluetoothProfile profile : mCachedDevice.getRemovedProfiles()) {
             CheckBox profilePref = findProfile(profile.toString());
             if (profilePref != null) {
+
+                if (profile instanceof PbapServerProfile) {
+                    final int pbapPermission = mCachedDevice.getPhonebookPermissionChoice();
+                    Log.d(TAG, "refreshProfiles: pbapPermission = " + pbapPermission);
+                    if (pbapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN)
+                        continue;
+                }
+                if (profile instanceof MapProfile) {
+                    final int mapPermission = mCachedDevice.getMessagePermissionChoice();
+                    Log.d(TAG, "refreshProfiles: mapPermission = " + mapPermission);
+                    if (mapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN)
+                        continue;
+                }
                 Log.d(TAG, "Removing " + profile.toString() + " from profile list");
                 mProfileContainer.removeView(profilePref);
             }
diff --git a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
index ea93fef..a4f6e5c 100644
--- a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
@@ -24,6 +24,7 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.bluetooth.BluetoothFilesPreferenceController;
 import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController;
+import com.android.settings.bluetooth.BluetoothSwitchPreferenceController;
 import com.android.settings.bluetooth.Utils;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.deviceinfo.UsbBackend;
@@ -83,10 +84,8 @@
         mUsbPrefController = new UsbModePreferenceController(context, new UsbBackend(context));
         lifecycle.addObserver(mUsbPrefController);
         controllers.add(mUsbPrefController);
-        final BluetoothMasterSwitchPreferenceController bluetoothPreferenceController =
-                new BluetoothMasterSwitchPreferenceController(
-                        context, Utils.getLocalBtManager(context), this,
-                        (SettingsActivity) getActivity());
+        final BluetoothSwitchPreferenceController bluetoothPreferenceController =
+                new BluetoothSwitchPreferenceController(context);
         lifecycle.addObserver(bluetoothPreferenceController);
         controllers.add(bluetoothPreferenceController);
 
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
index a0b5cb8..3cccc15 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
@@ -19,6 +19,7 @@
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
 import android.support.v7.preference.PreferenceScreen;
+
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.bluetooth.BluetoothDeviceUpdater;
 import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
@@ -42,26 +43,31 @@
     @VisibleForTesting
     PreferenceGroup mPreferenceGroup;
     private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
+    private ConnectedUsbDeviceUpdater mConnectedUsbDeviceUpdater;
 
     public ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle) {
         super(fragment.getContext());
-        init(lifecycle, new ConnectedBluetoothDeviceUpdater(fragment, this));
+        init(lifecycle, new ConnectedBluetoothDeviceUpdater(fragment, this),
+                new ConnectedUsbDeviceUpdater(fragment.getContext(), this));
     }
 
     @VisibleForTesting
     ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle,
-            BluetoothDeviceUpdater bluetoothDeviceUpdater) {
+            BluetoothDeviceUpdater bluetoothDeviceUpdater,
+            ConnectedUsbDeviceUpdater connectedUsbDeviceUpdater) {
         super(fragment.getContext());
-        init(lifecycle, bluetoothDeviceUpdater);
+        init(lifecycle, bluetoothDeviceUpdater, connectedUsbDeviceUpdater);
     }
 
     @Override
     public void onStart() {
         mBluetoothDeviceUpdater.registerCallback();
+        mConnectedUsbDeviceUpdater.registerCallback();
     }
 
     @Override
     public void onStop() {
+        mConnectedUsbDeviceUpdater.unregisterCallback();
         mBluetoothDeviceUpdater.unregisterCallback();
     }
 
@@ -70,8 +76,10 @@
         super.displayPreference(screen);
         mPreferenceGroup = (PreferenceGroup) screen.findPreference(KEY);
         mPreferenceGroup.setVisible(false);
+
         mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
         mBluetoothDeviceUpdater.forceUpdate();
+        mConnectedUsbDeviceUpdater.initUsbPreference(screen.getContext());
     }
 
     @Override
@@ -100,10 +108,12 @@
         }
     }
 
-    private void init(Lifecycle lifecycle, BluetoothDeviceUpdater bluetoothDeviceUpdater) {
+    private void init(Lifecycle lifecycle, BluetoothDeviceUpdater bluetoothDeviceUpdater,
+            ConnectedUsbDeviceUpdater connectedUsbDeviceUpdater) {
         if (lifecycle != null) {
             lifecycle.addObserver(this);
         }
         mBluetoothDeviceUpdater = bluetoothDeviceUpdater;
+        mConnectedUsbDeviceUpdater = connectedUsbDeviceUpdater;
     }
 }
diff --git a/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdater.java b/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdater.java
new file mode 100644
index 0000000..0468b0f
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdater.java
@@ -0,0 +1,92 @@
+/*
+ * 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.connecteddevice;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.deviceinfo.UsbBackend;
+import com.android.settings.deviceinfo.UsbModeChooserActivity;
+import com.android.settings.widget.GearPreference;
+
+/**
+ * Controller to maintain connected usb device
+ */
+public class ConnectedUsbDeviceUpdater {
+    private Context mContext;
+    private UsbBackend mUsbBackend;
+    private DevicePreferenceCallback mDevicePreferenceCallback;
+    @VisibleForTesting
+    GearPreference mUsbPreference;
+    @VisibleForTesting
+    UsbConnectionBroadcastReceiver mUsbReceiver;
+
+    private UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
+            (connected) -> {
+                if (connected) {
+                    mUsbPreference.setSummary(
+                            UsbModePreferenceController.getSummary(mUsbBackend.getCurrentMode()));
+                    mDevicePreferenceCallback.onDeviceAdded(mUsbPreference);
+                } else {
+                    mDevicePreferenceCallback.onDeviceRemoved(mUsbPreference);
+                }
+            };
+
+    public ConnectedUsbDeviceUpdater(Context context,
+            DevicePreferenceCallback devicePreferenceCallback) {
+        this(context, devicePreferenceCallback, new UsbBackend(context));
+    }
+
+    @VisibleForTesting
+    ConnectedUsbDeviceUpdater(Context context, DevicePreferenceCallback devicePreferenceCallback,
+            UsbBackend usbBackend) {
+        mContext = context;
+        mDevicePreferenceCallback = devicePreferenceCallback;
+        mUsbBackend = usbBackend;
+        mUsbReceiver = new UsbConnectionBroadcastReceiver(context, mUsbConnectionListener);
+    }
+
+    public void registerCallback() {
+        // This method could handle multiple register
+        mUsbReceiver.register();
+    }
+
+    public void unregisterCallback() {
+        mUsbReceiver.unregister();
+    }
+
+    public void initUsbPreference(Context context) {
+        mUsbPreference = new GearPreference(context, null /* AttributeSet */);
+        mUsbPreference.setTitle(R.string.usb_pref);
+        mUsbPreference.setIcon(R.drawable.ic_usb);
+        mUsbPreference.setSelectable(false);
+        mUsbPreference.setOnGearClickListener((GearPreference p) -> {
+            final Intent intent = new Intent(mContext, UsbModeChooserActivity.class);
+            mContext.startActivity(intent);
+        });
+
+        forceUpdate();
+    }
+
+    private void forceUpdate() {
+        // Register so we can get the connection state from sticky intent.
+        //TODO(b/70336520): Use an API to get data instead of sticky intent
+        mUsbReceiver.register();
+        mUsbConnectionListener.onUsbConnectionChanged(mUsbReceiver.isConnected());
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java b/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java
new file mode 100644
index 0000000..07a7691
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java
@@ -0,0 +1,76 @@
+/*
+ * 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.connecteddevice;
+
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbManager;
+
+/**
+ * Receiver to receive usb update and use {@link UsbConnectionListener} to invoke callback
+ */
+public class UsbConnectionBroadcastReceiver extends BroadcastReceiver {
+    private Context mContext;
+    private UsbConnectionListener mUsbConnectionListener;
+    private boolean mListeningToUsbEvents;
+    private boolean mConnected;
+
+    public UsbConnectionBroadcastReceiver(Context context,
+            UsbConnectionListener usbConnectionListener) {
+        mContext = context;
+        mUsbConnectionListener = usbConnectionListener;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        mConnected = intent != null
+                && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
+        if (mUsbConnectionListener != null) {
+            mUsbConnectionListener.onUsbConnectionChanged(mConnected);
+        }
+    }
+
+    public void register() {
+        if (!mListeningToUsbEvents) {
+            final IntentFilter intentFilter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+            final Intent intent = mContext.registerReceiver(this, intentFilter);
+            mConnected = intent != null
+                    && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
+            mListeningToUsbEvents = true;
+        }
+    }
+
+    public void unregister() {
+        if (mListeningToUsbEvents) {
+            mContext.unregisterReceiver(this);
+            mListeningToUsbEvents = false;
+        }
+    }
+
+    public boolean isConnected() {
+        return mConnected;
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when usb connection is changed.
+     */
+    interface UsbConnectionListener {
+        void onUsbConnectionChanged(boolean connected);
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/UsbModePreferenceController.java b/src/com/android/settings/connecteddevice/UsbModePreferenceController.java
index a6cb9be..8693520 100644
--- a/src/com/android/settings/connecteddevice/UsbModePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/UsbModePreferenceController.java
@@ -15,17 +15,12 @@
  */
 package com.android.settings.connecteddevice;
 
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.hardware.usb.UsbManager;
-import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
-import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.deviceinfo.UsbBackend;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -44,19 +39,21 @@
     public UsbModePreferenceController(Context context, UsbBackend usbBackend) {
         super(context);
         mUsbBackend = usbBackend;
-        mUsbReceiver = new UsbConnectionBroadcastReceiver();
+        mUsbReceiver = new UsbConnectionBroadcastReceiver(mContext, (connected) -> {
+            updateSummary(mUsbPreference);
+        });
     }
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
         mUsbPreference = screen.findPreference(KEY_USB_MODE);
-        updataSummary(mUsbPreference);
+        updateSummary(mUsbPreference);
     }
 
     @Override
     public void updateState(Preference preference) {
-        updataSummary(preference);
+        updateSummary(preference);
     }
 
     @Override
@@ -79,8 +76,7 @@
         mUsbReceiver.register();
     }
 
-    @VisibleForTesting
-    int getSummary(int mode) {
+    public static int getSummary(int mode) {
         switch (mode) {
             case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_NONE:
                 return R.string.usb_summary_charging_only;
@@ -96,11 +92,11 @@
         return 0;
     }
 
-    private void updataSummary(Preference preference) {
-        updataSummary(preference, mUsbBackend.getCurrentMode());
+    private void updateSummary(Preference preference) {
+        updateSummary(preference, mUsbBackend.getCurrentMode());
     }
 
-    private void updataSummary(Preference preference, int mode) {
+    private void updateSummary(Preference preference, int mode) {
         if (preference != null) {
             if (mUsbReceiver.isConnected()) {
                 preference.setEnabled(true);
@@ -112,40 +108,4 @@
         }
     }
 
-    private class UsbConnectionBroadcastReceiver extends BroadcastReceiver {
-        private boolean mListeningToUsbEvents;
-        private boolean mConnected;
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            boolean connected = intent != null
-                    && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
-            if (connected != mConnected) {
-                mConnected = connected;
-                updataSummary(mUsbPreference);
-            }
-        }
-
-        public void register() {
-            if (!mListeningToUsbEvents) {
-                IntentFilter intentFilter = new IntentFilter(UsbManager.ACTION_USB_STATE);
-                Intent intent = mContext.registerReceiver(this, intentFilter);
-                mConnected = intent != null
-                        && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
-                mListeningToUsbEvents = true;
-            }
-        }
-
-        public void unregister() {
-            if (mListeningToUsbEvents) {
-                mContext.unregisterReceiver(this);
-                mListeningToUsbEvents = false;
-            }
-        }
-
-        public boolean isConnected() {
-            return mConnected;
-        }
-    }
-
 }
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index acb20d5..b7c73f3 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -47,7 +47,7 @@
 import com.android.settings.applications.ProcessStatsUi;
 import com.android.settings.applications.UsageAccessDetails;
 import com.android.settings.applications.VrListenerSettings;
-import com.android.settings.applications.AppInfoDashboardFragment;
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
 import com.android.settings.applications.appinfo.DrawOverlayDetails;
 import com.android.settings.applications.appinfo.ExternalSourcesDetails;
 import com.android.settings.applications.appinfo.PictureInPictureDetails;
@@ -265,6 +265,7 @@
             Settings.SoundSettingsActivity.class.getName(),
             Settings.StorageDashboardActivity.class.getName(),
             Settings.PowerUsageSummaryActivity.class.getName(),
+            Settings.PowerUsageSummaryLegacyActivity.class.getName(),
             Settings.UserAndAccountDashboardActivity.class.getName(),
             Settings.SecuritySettingsActivity.class.getName(),
             Settings.AccessibilitySettingsActivity.class.getName(),
diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccess.java b/src/com/android/settings/datausage/UnrestrictedDataAccess.java
index 5b55ada..e8a7bbf 100644
--- a/src/com/android/settings/datausage/UnrestrictedDataAccess.java
+++ b/src/com/android/settings/datausage/UnrestrictedDataAccess.java
@@ -32,7 +32,7 @@
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.applications.AppStateBaseBridge;
 import com.android.settings.applications.InstalledAppDetails;
-import com.android.settings.applications.AppInfoDashboardFragment;
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
 import com.android.settings.core.FeatureFlags;
 import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState;
 import com.android.settings.overlay.FeatureFactory;
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 8f114fc..e736798 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -446,6 +446,7 @@
         controllers.add(new ResizableActivityPreferenceController(context));
         controllers.add(new FreeformWindowsPreferenceController(context));
         controllers.add(new ShortcutManagerThrottlingPreferenceController(context));
+        controllers.add(new EnableGnssRawMeasFullTrackingPreferenceController(context));
         return controllers;
     }
 
diff --git a/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceController.java b/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceController.java
new file mode 100644
index 0000000..09770f6
--- /dev/null
+++ b/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceController.java
@@ -0,0 +1,87 @@
+/*
+ * 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.development;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+public class EnableGnssRawMeasFullTrackingPreferenceController extends
+        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
+        PreferenceControllerMixin {
+
+    private static final String ENABLE_GNSS_RAW_MEAS_FULL_TRACKING_KEY =
+            "enable_gnss_raw_meas_full_tracking";
+
+    static final int SETTING_VALUE_ON = 1;
+    static final int SETTING_VALUE_OFF = 0;
+
+    private SwitchPreference mPreference;
+
+    public EnableGnssRawMeasFullTrackingPreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return ENABLE_GNSS_RAW_MEAS_FULL_TRACKING_KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mPreference = (SwitchPreference) screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean isEnabled = (Boolean) newValue;
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
+                isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
+
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        final int enableGnssRawMeasFullTrackingMode =
+                Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_OFF);
+        mPreference.setChecked(enableGnssRawMeasFullTrackingMode != SETTING_VALUE_OFF);
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchEnabled() {
+        mPreference.setEnabled(true);
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_OFF);
+        mPreference.setEnabled(false);
+        mPreference.setChecked(false);
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/AdditionalSystemUpdatePreferenceController.java b/src/com/android/settings/deviceinfo/AdditionalSystemUpdatePreferenceController.java
index 06bdb3f..f91ed4e 100644
--- a/src/com/android/settings/deviceinfo/AdditionalSystemUpdatePreferenceController.java
+++ b/src/com/android/settings/deviceinfo/AdditionalSystemUpdatePreferenceController.java
@@ -17,26 +17,23 @@
 
 import android.content.Context;
 
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.core.AbstractPreferenceController;
 
-public class AdditionalSystemUpdatePreferenceController extends
-        AbstractPreferenceController implements PreferenceControllerMixin {
+public class AdditionalSystemUpdatePreferenceController extends BasePreferenceController {
 
     private static final String KEY_UPDATE_SETTING = "additional_system_update_settings";
 
     public AdditionalSystemUpdatePreferenceController(Context context) {
-        super(context);
+        super(context, KEY_UPDATE_SETTING);
     }
 
     @Override
-    public boolean isAvailable() {
+    public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(
-                com.android.settings.R.bool.config_additional_system_update_setting_enable);
+                com.android.settings.R.bool.config_additional_system_update_setting_enable)
+                ? AVAILABLE
+                : DISABLED_UNSUPPORTED;
     }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_UPDATE_SETTING;
-    }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java b/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java
index d8a64a8..92c33d8 100644
--- a/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java
+++ b/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java
@@ -30,11 +30,9 @@
 
 import com.android.settings.R;
 import com.android.settings.Utils;
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settings.core.BasePreferenceController;
 
-public class SystemUpdatePreferenceController extends AbstractPreferenceController implements
-        PreferenceControllerMixin {
+public class SystemUpdatePreferenceController extends BasePreferenceController {
 
     private static final String TAG = "SysUpdatePrefContr";
 
@@ -42,19 +40,16 @@
 
     private final UserManager mUm;
 
-    public SystemUpdatePreferenceController(Context context, UserManager um) {
-        super(context);
-        mUm = um;
+    public SystemUpdatePreferenceController(Context context) {
+        super(context, KEY_SYSTEM_UPDATE_SETTINGS);
+        mUm = UserManager.get(context);
     }
 
     @Override
-    public boolean isAvailable() {
-        return mUm.isAdminUser();
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_SYSTEM_UPDATE_SETTINGS;
+    public int getAvailabilityStatus() {
+        return mUm.isAdminUser()
+                ? AVAILABLE
+                : DISABLED_UNSUPPORTED;
     }
 
     @Override
@@ -62,14 +57,14 @@
         super.displayPreference(screen);
         if (isAvailable()) {
             Utils.updatePreferenceToSpecificActivityOrRemove(mContext, screen,
-                    KEY_SYSTEM_UPDATE_SETTINGS,
+                    getPreferenceKey(),
                     Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
         }
     }
 
     @Override
     public boolean handlePreferenceTreeClick(Preference preference) {
-        if (KEY_SYSTEM_UPDATE_SETTINGS.equals(preference.getKey())) {
+        if (TextUtils.equals(getPreferenceKey(), preference.getKey())) {
             CarrierConfigManager configManager =
                     (CarrierConfigManager) mContext.getSystemService(CARRIER_CONFIG_SERVICE);
             PersistableBundle b = configManager.getConfig();
@@ -108,4 +103,4 @@
             mContext.getApplicationContext().sendBroadcast(intent);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
index 15ca87b..35b8bd1 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
@@ -68,6 +68,8 @@
     @VisibleForTesting
     final static int SERVICE_STATE_VALUE_ID = R.id.service_state_value;
     @VisibleForTesting
+    final static int SIGNAL_STRENGTH_LABEL_ID = R.id.signal_strength_label;
+    @VisibleForTesting
     final static int SIGNAL_STRENGTH_VALUE_ID = R.id.signal_strength_value;
     @VisibleForTesting
     final static int CELLULAR_NETWORK_TYPE_VALUE_ID = R.id.network_type_value;
@@ -262,6 +264,21 @@
     }
 
     private void updateSignalStrength(SignalStrength signalStrength) {
+        final int subscriptionId = mSubscriptionInfo.getSubscriptionId();
+        final PersistableBundle carrierConfig =
+                mCarrierConfigManager.getConfigForSubId(subscriptionId);
+        // by default we show the signal strength
+        boolean showSignalStrength = true;
+        if (carrierConfig != null) {
+            showSignalStrength = carrierConfig.getBoolean(
+                    CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL);
+        }
+        if (!showSignalStrength) {
+            mDialog.removeSettingFromScreen(SIGNAL_STRENGTH_LABEL_ID);
+            mDialog.removeSettingFromScreen(SIGNAL_STRENGTH_VALUE_ID);
+            return;
+        }
+
         final int state = getCurrentServiceState().getState();
 
         if ((ServiceState.STATE_OUT_OF_SERVICE == state) ||
@@ -327,9 +344,14 @@
 
     private void updateIccidNumber() {
         final int subscriptionId = mSubscriptionInfo.getSubscriptionId();
-        final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subscriptionId);
-        final boolean showIccId = carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL);
+        final PersistableBundle carrierConfig =
+                mCarrierConfigManager.getConfigForSubId(subscriptionId);
+        // do not show iccid by default
+        boolean showIccId = false;
+        if (carrierConfig != null) {
+            showIccId = carrierConfig.getBoolean(
+                    CarrierConfigManager.KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL);
+        }
         if (!showIccId) {
             mDialog.removeSettingFromScreen(ICCID_INFO_LABEL_ID);
             mDialog.removeSettingFromScreen(ICCID_INFO_VALUE_ID);
diff --git a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
index f7a2b9a..d0f4080 100644
--- a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
@@ -20,16 +20,18 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.PowerManager;
 import android.support.annotation.VisibleForTesting;
 
 import com.android.settings.Utils;
 
 /**
  * Use this broadcastReceiver to listen to the battery change, and it will invoke
- * {@link OnBatteryChangedListener} if any of the following happens:
+ * {@link OnBatteryChangedListener} if any of the followings has been changed:
  *
- * 1. Battery level has been changed
- * 2. Battery status has been changed
+ * 1. Battery level(e.g. 100%->99%)
+ * 2. Battery status(e.g. plugged->unplugged)
+ * 3. Battery saver(e.g. off->on)
  */
 public class BatteryBroadcastReceiver extends BroadcastReceiver {
 
@@ -58,8 +60,11 @@
     }
 
     public void register() {
-        final Intent intent = mContext.registerReceiver(this,
-                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+
+        final Intent intent = mContext.registerReceiver(this, intentFilter);
         updateBatteryStatus(intent, true /* forceUpdate */);
     }
 
@@ -68,15 +73,18 @@
     }
 
     private void updateBatteryStatus(Intent intent, boolean forceUpdate) {
-        if (intent != null && mBatteryListener != null && Intent.ACTION_BATTERY_CHANGED.equals(
-                intent.getAction())) {
-            String batteryLevel = Utils.getBatteryPercentage(intent);
-            String batteryStatus = Utils.getBatteryStatus(
-                    mContext.getResources(), intent);
-            if (forceUpdate || !batteryLevel.equals(mBatteryLevel) || !batteryStatus.equals(
-                    mBatteryStatus)) {
-                mBatteryLevel = batteryLevel;
-                mBatteryStatus = batteryStatus;
+        if (intent != null && mBatteryListener != null) {
+            if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+                final String batteryLevel = Utils.getBatteryPercentage(intent);
+                final String batteryStatus = Utils.getBatteryStatus(
+                        mContext.getResources(), intent);
+                if (forceUpdate || !batteryLevel.equals(mBatteryLevel) || !batteryStatus.equals(
+                        mBatteryStatus)) {
+                    mBatteryLevel = batteryLevel;
+                    mBatteryStatus = batteryStatus;
+                    mBatteryListener.onBatteryChanged();
+                }
+            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) {
                 mBatteryListener.onBatteryChanged();
             }
         }
diff --git a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java
index d1b47b2..819b128 100644
--- a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java
+++ b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java
@@ -23,27 +23,26 @@
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.List;
 
-public class GesturesSettingPreferenceController extends AbstractPreferenceController
-        implements PreferenceControllerMixin {
-
-    private static final String KEY_GESTURES_SETTINGS = "gesture_settings";
-
+public class GesturesSettingPreferenceController extends BasePreferenceController {
     private final AssistGestureFeatureProvider mFeatureProvider;
     private List<AbstractPreferenceController> mGestureControllers;
 
+    private static final String KEY_GESTURES_SETTINGS = "gesture_settings";
+
     public GesturesSettingPreferenceController(Context context) {
-        super(context);
+        super(context, KEY_GESTURES_SETTINGS);
         mFeatureProvider = FeatureFactory.getFactory(context).getAssistGestureFeatureProvider();
     }
 
     @Override
-    public boolean isAvailable() {
+    public int getAvailabilityStatus() {
         if (mGestureControllers == null) {
             mGestureControllers = GestureSettings.buildPreferenceControllers(mContext,
                     null /* lifecycle */, new AmbientDisplayConfiguration(mContext));
@@ -52,12 +51,9 @@
         for (AbstractPreferenceController controller : mGestureControllers) {
             isAvailable = isAvailable || controller.isAvailable();
         }
-        return isAvailable;
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_GESTURES_SETTINGS;
+        return isAvailable
+                ? AVAILABLE
+                : DISABLED_UNSUPPORTED;
     }
 
     @Override
@@ -83,5 +79,4 @@
         }
         preference.setSummary(summary);
     }
-
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/location/LocationMode.java b/src/com/android/settings/location/LocationMode.java
index 34f082b..5931f9e 100644
--- a/src/com/android/settings/location/LocationMode.java
+++ b/src/com/android/settings/location/LocationMode.java
@@ -95,6 +95,11 @@
                 }
 
                 @Override
+                protected boolean isPageSearchEnabled(Context context) {
+                    return context.getResources().getBoolean(R.bool.config_location_mode_available);
+                }
+
+                @Override
                 public List<AbstractPreferenceController> getPreferenceControllers(Context
                         context) {
                     return buildPreferenceControllers(context, null /* lifecycle */);
diff --git a/src/com/android/settings/location/RecentLocationRequestPreferenceController.java b/src/com/android/settings/location/RecentLocationRequestPreferenceController.java
index 8c4fa579..461f6e3 100644
--- a/src/com/android/settings/location/RecentLocationRequestPreferenceController.java
+++ b/src/com/android/settings/location/RecentLocationRequestPreferenceController.java
@@ -25,7 +25,7 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.applications.InstalledAppDetails;
-import com.android.settings.applications.AppInfoDashboardFragment;
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
 import com.android.settings.core.FeatureFlags;
 import com.android.settings.widget.AppPreference;
 import com.android.settingslib.core.lifecycle.Lifecycle;
diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java
index f8408fc..1ee20d3 100644
--- a/src/com/android/settings/notification/ZenModeSettings.java
+++ b/src/com/android/settings/notification/ZenModeSettings.java
@@ -97,23 +97,21 @@
                 enabledCategories = getEnabledCategories(policy);
             }
 
+            // no sound categories can bypass dnd
             int numCategories = enabledCategories.size();
             if (numCategories == 0) {
-                return mContext.getString(R.string.zen_mode_behavior_no_sound);
+                return mContext.getString(R.string.zen_mode_behavior_total_silence);
             }
 
-            String s = enabledCategories.get(0).toLowerCase();
-            for (int i = 1; i < numCategories; i++) {
-                if (i == numCategories - 1) {
-                    s = mContext.getString(R.string.join_many_items_last,
-                            s, enabledCategories.get(i).toLowerCase());
-                } else {
-                    s = mContext.getString(R.string.join_many_items_middle,
-                            s, enabledCategories.get(i).toLowerCase());
-                }
+            // only alarms and media/system can bypass dnd
+            if (numCategories == 2 &&
+                    isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_ALARMS) &&
+                    isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER)) {
+                return mContext.getString(R.string.zen_mode_behavior_alarms_only);
             }
 
-            return mContext.getString(R.string.zen_mode_behavior_no_sound_except, s);
+            // custom
+            return mContext.getString(R.string.zen_mode_behavior_summary_custom);
         }
 
         String getAutomaticRulesSummary() {
diff --git a/src/com/android/settings/search/DatabaseIndexingUtils.java b/src/com/android/settings/search/DatabaseIndexingUtils.java
index 207d09f..94ec650 100644
--- a/src/com/android/settings/search/DatabaseIndexingUtils.java
+++ b/src/com/android/settings/search/DatabaseIndexingUtils.java
@@ -43,7 +43,7 @@
 
     private static final String TAG = "IndexingUtil";
 
-    private static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER =
+    public static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER =
             "SEARCH_INDEX_DATA_PROVIDER";
 
     /**
diff --git a/src/com/android/settings/search/XmlParserUtils.java b/src/com/android/settings/search/XmlParserUtils.java
index b4ffc53..27c5cd3 100644
--- a/src/com/android/settings/search/XmlParserUtils.java
+++ b/src/com/android/settings/search/XmlParserUtils.java
@@ -71,6 +71,10 @@
         return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_keywords);
     }
 
+    public static String getController(Context context, AttributeSet attrs) {
+        return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_controller);
+    }
+
     public static int getDataIcon(Context context, AttributeSet attrs) {
         final TypedArray ta = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.Preference);
diff --git a/src/com/android/settings/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
similarity index 67%
rename from src/com/android/settings/SettingsSliceProvider.java
rename to src/com/android/settings/slices/SettingsSliceProvider.java
index 845dacd..22035d2 100644
--- a/src/com/android/settings/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -14,28 +14,31 @@
  * limitations under the License
  */
 
-package com.android.settings;
+package com.android.settings.slices;
 
 import android.app.PendingIntent;
-import android.app.slice.Slice;
-import android.app.slice.SliceProvider;
+
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
-import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 
+import com.android.settings.R;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceProvider;
+import androidx.app.slice.builders.ListBuilder;
+
 public class SettingsSliceProvider extends SliceProvider {
     public static final String SLICE_AUTHORITY = "com.android.settings.slices";
 
     public static final String PATH_WIFI = "wifi";
     public static final String ACTION_WIFI_CHANGED =
             "com.android.settings.slice.action.WIFI_CHANGED";
-    // TODO -- Associate slice URI with search result instead of separate hardcoded thing
-    public static final String[] WIFI_SEARCH_TERMS = {"wi-fi", "wifi", "internet"};
 
+    // TODO -- Associate slice URI with search result instead of separate hardcoded thing
     public static Uri getUri(String path) {
         return new Uri.Builder()
                 .scheme(ContentResolver.SCHEME_CONTENT)
@@ -44,7 +47,7 @@
     }
 
     @Override
-    public boolean onCreate() {
+    public boolean onCreateSliceProvider() {
         return true;
     }
 
@@ -53,15 +56,15 @@
         String path = sliceUri.getPath();
         switch (path) {
             case "/" + PATH_WIFI:
-                return createWifi(sliceUri);
-
+                return createWifiSlice(sliceUri);
         }
         throw new IllegalArgumentException("Unrecognized slice uri: " + sliceUri);
     }
 
-    private Slice createWifi(Uri uri) {
+
+    // TODO (b/70622039) remove this when the proper wifi slice is enabled.
+    private Slice createWifiSlice(Uri sliceUri) {
         // Get wifi state
-        String[] toggleHints;
         WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
         int wifiState = wifiManager.getWifiState();
         boolean wifiEnabled = false;
@@ -74,7 +77,6 @@
             case WifiManager.WIFI_STATE_ENABLED:
             case WifiManager.WIFI_STATE_ENABLING:
                 state = wifiManager.getConnectionInfo().getSSID();
-                WifiInfo.removeDoubleQuotes(state);
                 wifiEnabled = true;
                 break;
             case WifiManager.WIFI_STATE_UNKNOWN:
@@ -82,28 +84,17 @@
                 state = ""; // just don't show anything?
                 break;
         }
-        if (wifiEnabled) {
-            toggleHints = new String[] {Slice.HINT_TOGGLE, Slice.HINT_SELECTED};
-        } else {
-            toggleHints = new String[] {Slice.HINT_TOGGLE};
-        }
-        // Construct the slice
-        Slice.Builder b = new Slice.Builder(uri);
-        b.addSubSlice(new Slice.Builder(b)
-                .addAction(getIntent("android.settings.WIFI_SETTINGS"),
-                        new Slice.Builder(b)
-                                .addText(getContext().getString(R.string.wifi_settings), null)
-                                .addText(state, null)
-                                .addIcon(Icon.createWithResource(getContext(),
-                                        R.drawable.ic_settings_wireless), null, Slice.HINT_HIDDEN)
-                                .addHints(Slice.HINT_TITLE)
-                                .build())
-                .addAction(getBroadcastIntent(ACTION_WIFI_CHANGED),
-                        new Slice.Builder(b)
-                                .addHints(toggleHints)
-                                .build())
-                .build());
-        return b.build();
+
+        boolean finalWifiEnabled = wifiEnabled;
+        return new ListBuilder(sliceUri)
+                .setColor(R.color.material_blue_500)
+                .add(b -> b
+                        .setTitle(getContext().getString(R.string.wifi_settings))
+                        .setTitleItem(Icon.createWithResource(getContext(), R.drawable.wifi_signal))
+                        .setSubtitle(state)
+                        .addToggle(getBroadcastIntent(ACTION_WIFI_CHANGED), finalWifiEnabled)
+                        .setContentIntent(getIntent(Intent.ACTION_MAIN)))
+                .build();
     }
 
     private PendingIntent getIntent(String action) {
diff --git a/src/com/android/settings/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java
similarity index 85%
rename from src/com/android/settings/SliceBroadcastReceiver.java
rename to src/com/android/settings/slices/SliceBroadcastReceiver.java
index f43e3a3..b6f2ab9 100644
--- a/src/com/android/settings/SliceBroadcastReceiver.java
+++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java
@@ -14,9 +14,9 @@
  * limitations under the License
  */
 
-package com.android.settings;
+package com.android.settings.slices;
 
-import static com.android.settings.SettingsSliceProvider.ACTION_WIFI_CHANGED;
+import static com.android.settings.slices.SettingsSliceProvider.ACTION_WIFI_CHANGED;
 
 import android.app.slice.Slice;
 import android.content.BroadcastReceiver;
@@ -42,8 +42,8 @@
                 // Wait a bit for wifi to update (TODO: is there a better way to do this?)
                 Handler h = new Handler();
                 h.postDelayed(() -> {
-                        Uri uri = SettingsSliceProvider.getUri(SettingsSliceProvider.PATH_WIFI);
-                        context.getContentResolver().notifyChange(uri, null);
+                    Uri uri = SettingsSliceProvider.getUri(SettingsSliceProvider.PATH_WIFI);
+                    context.getContentResolver().notifyChange(uri, null);
                 }, 1000);
                 break;
         }
diff --git a/src/com/android/settings/slices/SliceData.java b/src/com/android/settings/slices/SliceData.java
new file mode 100644
index 0000000..528f23c
--- /dev/null
+++ b/src/com/android/settings/slices/SliceData.java
@@ -0,0 +1,188 @@
+/*
+ * 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.slices;
+
+import android.net.Uri;
+import android.text.TextUtils;
+
+/**
+ * TODO (b/67996923) Add SlicesIndexingManager
+ * Data class representing a slice stored by {@link SlicesIndexingManager}.
+ * Note that {@link #key} is treated as a primary key for this class and determines equality.
+ */
+public class SliceData {
+
+    private final String key;
+
+    private final String title;
+
+    private final String summary;
+
+    private final String screenTitle;
+
+    private final int iconResource;
+
+    private final String fragmentClassName;
+
+    private final Uri uri;
+
+    private final String preferenceController;
+
+    public String getKey() {
+        return key;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getSummary() {
+        return summary;
+    }
+
+    public String getScreenTitle() {
+        return screenTitle;
+    }
+
+    public int getIconResource() {
+        return iconResource;
+    }
+
+    public String getFragmentClassName() {
+        return fragmentClassName;
+    }
+
+    public Uri getUri() {
+        return uri;
+    }
+
+    public String getPreferenceController() {
+        return preferenceController;
+    }
+
+    private SliceData(Builder builder) {
+        key = builder.mKey;
+        title = builder.mTitle;
+        summary = builder.mSummary;
+        screenTitle = builder.mScreenTitle;
+        iconResource = builder.mIconResource;
+        fragmentClassName = builder.mFragmentClassName;
+        uri = builder.mUri;
+        preferenceController = builder.mPrefControllerClassName;
+    }
+
+    @Override
+    public int hashCode() {
+        return key.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof SliceData)) {
+            return false;
+        }
+        SliceData newObject = (SliceData) obj;
+        return TextUtils.equals(key, newObject.key);
+    }
+
+    static class Builder {
+        private String mKey;
+
+        private String mTitle;
+
+        private String mSummary;
+
+        private String mScreenTitle;
+
+        private int mIconResource;
+
+        private String mFragmentClassName;
+
+        private Uri mUri;
+
+        private String mPrefControllerClassName;
+
+        public Builder setKey(String key) {
+            mKey = key;
+            return this;
+        }
+
+        public Builder setTitle(String title) {
+            mTitle = title;
+            return this;
+        }
+
+        public Builder setSummary(String summary) {
+            mSummary = summary;
+            return this;
+        }
+
+        public Builder setScreenTitle(String screenTitle) {
+            mScreenTitle = screenTitle;
+            return this;
+        }
+
+        public Builder setIcon(int iconResource) {
+            mIconResource = iconResource;
+            return this;
+        }
+
+        public Builder setPreferenceControllerClassName(String controllerClassName) {
+            mPrefControllerClassName = controllerClassName;
+            return this;
+        }
+
+        public Builder setFragmentName(String fragmentClassName) {
+            mFragmentClassName = fragmentClassName;
+            return this;
+        }
+
+        public Builder setUri(Uri uri) {
+            mUri = uri;
+            return this;
+        }
+
+        public SliceData build() {
+            if (TextUtils.isEmpty(mKey)) {
+                throw new IllegalStateException("Key cannot be empty");
+            }
+
+            if (TextUtils.isEmpty(mTitle)) {
+                throw new IllegalStateException("Title cannot be empty");
+            }
+
+            if (TextUtils.isEmpty(mFragmentClassName)) {
+                throw new IllegalStateException("Fragment Name cannot be empty");
+            }
+
+            if (TextUtils.isEmpty(mPrefControllerClassName)) {
+                throw new IllegalStateException("Preference Controller cannot be empty");
+            }
+
+            if (mUri == null) {
+                throw new IllegalStateException("Uri cannot be null");
+            }
+
+            return new SliceData(this);
+        }
+
+        public String getKey() {
+            return mKey;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/com/android/settings/slices/SlicesDatabaseHelper.java b/src/com/android/settings/slices/SlicesDatabaseHelper.java
new file mode 100644
index 0000000..a74fc81
--- /dev/null
+++ b/src/com/android/settings/slices/SlicesDatabaseHelper.java
@@ -0,0 +1,122 @@
+package com.android.settings.slices;
+
+import android.content.Context;
+
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Defines the schema for the Slices database.
+ */
+public class SlicesDatabaseHelper extends SQLiteOpenHelper {
+
+    private static final String TAG = "SlicesDatabaseHelper";
+
+    private static final String DATABASE_NAME = "slices_index.db";
+    private static final String SHARED_PREFS_TAG = "slices_shared_prefs";
+
+    private static final int DATABASE_VERSION = 1;
+
+    public interface Tables {
+        String TABLE_SLICES_INDEX = "slices_index";
+    }
+
+    public interface IndexColumns {
+        /**
+         * Primary key of the DB. Preference key from preference controllers.
+         */
+        String KEY = "key";
+
+        /**
+         * Title of the Setting.
+         */
+        String TITLE = "title";
+
+        /**
+         * Summary / Subtitle for the setting.
+         */
+        String SUBTITLE = "subtitle";
+
+        /**
+         * Title of the Setting screen on which the Setting lives.
+         */
+        String SCREENTITLE = "screentitle";
+
+        /**
+         * Resource ID for the icon of the setting. Should be 0 for no icon.
+         */
+        String ICON_RESOURCE = "icon";
+
+        /**
+         * Classname of the fragment name of the page that hosts the setting.
+         */
+        String FRAGMENT = "fragment";
+
+        /**
+         * Class name of the controller backing the setting. Must be a
+         * {@link com.android.settings.core.BasePreferenceController}.
+         */
+        String CONTROLLER = "controller";
+    }
+
+    private static final String CREATE_SLICES_TABLE =
+            "CREATE VIRTUAL TABLE " + Tables.TABLE_SLICES_INDEX + " USING fts4" +
+                    "(" +
+                    IndexColumns.KEY +
+                    ", " +
+                    IndexColumns.TITLE +
+                    ", " +
+                    IndexColumns.SUBTITLE +
+                    ", " +
+                    IndexColumns.SCREENTITLE +
+                    ", " +
+                    IndexColumns.ICON_RESOURCE +
+                    ", " +
+                    IndexColumns.FRAGMENT +
+                    ", " +
+                    IndexColumns.CONTROLLER +
+                    ");";
+
+    private final Context mContext;
+
+    public SlicesDatabaseHelper(Context context) {
+        super(context, DATABASE_NAME, null /* CursorFactor */, DATABASE_VERSION);
+        mContext = context;
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+        createDatabases(db);
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+        if (oldVersion < DATABASE_VERSION) {
+            Log.d(TAG, "Reconstructing DB from " + oldVersion + "to " + newVersion);
+            reconstruct(db);
+        }
+    }
+
+    @VisibleForTesting
+    void reconstruct(SQLiteDatabase db) {
+        mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
+                .edit()
+                .clear()
+                .commit();
+        dropTables(db);
+        createDatabases(db);
+    }
+
+    private void createDatabases(SQLiteDatabase db) {
+        db.execSQL(CREATE_SLICES_TABLE);
+        Log.d(TAG, "Created databases");
+    }
+
+
+    private void dropTables(SQLiteDatabase db) {
+        db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SLICES_INDEX);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/system/SystemDashboardFragment.java b/src/com/android/settings/system/SystemDashboardFragment.java
index c01bfcc..323a2d4 100644
--- a/src/com/android/settings/system/SystemDashboardFragment.java
+++ b/src/com/android/settings/system/SystemDashboardFragment.java
@@ -16,8 +16,12 @@
 package com.android.settings.system;
 
 import android.content.Context;
+import android.os.Bundle;
 import android.os.UserManager;
 import android.provider.SearchIndexableResource;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceScreen;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
@@ -41,6 +45,17 @@
     private static final String KEY_RESET = "reset_dashboard";
 
     @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final PreferenceScreen screen = getPreferenceScreen();
+        // We do not want to display an advanced button if only one setting is hidden
+        if (getVisiblePreferenceCount(screen) == screen.getInitialExpandedChildrenCount() + 1) {
+            screen.setInitialExpandedChildrenCount(Integer.MAX_VALUE);
+        }
+    }
+
+    @Override
     public int getMetricsCategory() {
         return MetricsProto.MetricsEvent.SETTINGS_SYSTEM_CATEGORY;
     }
@@ -67,13 +82,26 @@
 
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
-        controllers.add(new SystemUpdatePreferenceController(context, UserManager.get(context)));
+        controllers.add(new SystemUpdatePreferenceController(context));
         controllers.add(new AdditionalSystemUpdatePreferenceController(context));
         controllers.add(new BackupSettingsActivityPreferenceController(context));
         controllers.add(new GesturesSettingPreferenceController(context));
         return controllers;
     }
 
+    private int getVisiblePreferenceCount(PreferenceGroup group) {
+        int visibleCount = 0;
+        for (int i = 0; i < group.getPreferenceCount(); i++) {
+            final Preference preference = group.getPreference(i);
+            if (preference instanceof PreferenceGroup) {
+                visibleCount += getVisiblePreferenceCount((PreferenceGroup) preference);
+            } else if (preference.isVisible()) {
+                visibleCount++;
+            }
+        }
+        return visibleCount;
+    }
+
     /**
      * For Search.
      */
@@ -88,17 +116,18 @@
                 }
 
                 @Override
-                public List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+                public List<AbstractPreferenceController> getPreferenceControllers(
+                        Context context) {
                     return buildPreferenceControllers(context);
                 }
 
                 @Override
                 public List<String> getNonIndexableKeys(Context context) {
                     List<String> keys = super.getNonIndexableKeys(context);
-                    keys.add((new BackupSettingsActivityPreferenceController(context)
-                            .getPreferenceKey()));
+                    keys.add((new BackupSettingsActivityPreferenceController(
+                            context).getPreferenceKey()));
                     keys.add(KEY_RESET);
                     return keys;
                 }
             };
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/widget/EntityHeaderController.java b/src/com/android/settings/widget/EntityHeaderController.java
index 5fa7586..0d07e67 100644
--- a/src/com/android/settings/widget/EntityHeaderController.java
+++ b/src/com/android/settings/widget/EntityHeaderController.java
@@ -45,7 +45,7 @@
 import com.android.settings.applications.AppInfoBase;
 import com.android.settings.applications.InstalledAppDetails;
 import com.android.settings.applications.LayoutPreference;
-import com.android.settings.applications.AppInfoDashboardFragment;
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
 import com.android.settings.core.FeatureFlags;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.applications.ApplicationsState;
diff --git a/tests/robotests/Android.mk b/tests/robotests/Android.mk
index 97e5e04..7271884 100644
--- a/tests/robotests/Android.mk
+++ b/tests/robotests/Android.mk
@@ -14,7 +14,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
     junit \
-    platform-robolectric-3.4.2-prebuilt \
+    platform-robolectric-3.5.1-prebuilt \
     telephony-common
 
 LOCAL_INSTRUMENTATION_FOR := Settings
@@ -42,4 +42,4 @@
 
 LOCAL_ROBOTEST_TIMEOUT := 36000
 
-include prebuilts/misc/common/robolectric/3.4.2/run_robotests.mk
+include prebuilts/misc/common/robolectric/3.5.1/run_robotests.mk
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index 6d3ec9a..ebcea43 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -1,4 +1,4 @@
-com.android.settings.applications.AppInfoDashboardFragment
+com.android.settings.applications.appinfo.AppInfoDashboardFragment
 com.android.settings.bluetooth.DevicePickerFragment
 com.android.settings.bluetooth.BluetoothDeviceDetailsFragment
 com.android.settings.bluetooth.BluetoothPairingDetail
diff --git a/tests/robotests/res/xml-mcc999/about_legal.xml b/tests/robotests/res/xml-mcc999/about_legal.xml
index 53a2b89..3e008cb 100644
--- a/tests/robotests/res/xml-mcc999/about_legal.xml
+++ b/tests/robotests/res/xml-mcc999/about_legal.xml
@@ -30,5 +30,6 @@
 
     <Preference
             android:key="pref_key_1"
-            android:title="bears_bears_bears"/>
+            android:title="bears_bears_bears"
+            settings:controller="mind_flayer"/>
 </PreferenceScreen>
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/HelpTrampolineTest.java b/tests/robotests/src/com/android/settings/HelpTrampolineTest.java
index e10b878..a6bcf03 100644
--- a/tests/robotests/src/com/android/settings/HelpTrampolineTest.java
+++ b/tests/robotests/src/com/android/settings/HelpTrampolineTest.java
@@ -50,7 +50,7 @@
         final Intent intent = new Intent().setClassName(
                 RuntimeEnvironment.application.getPackageName(), HelpTrampoline.class.getName());
 
-        Robolectric.buildActivity(HelpTrampoline.class).withIntent(intent).create().get();
+        Robolectric.buildActivity(HelpTrampoline.class, intent).create().get();
 
         assertThat(ShadowHelpUtils.isGetHelpIntentCalled()).isFalse();
     }
@@ -60,8 +60,8 @@
         final Intent intent = new Intent().setClassName(
                 RuntimeEnvironment.application.getPackageName(), HelpTrampoline.class.getName())
                 .putExtra(Intent.EXTRA_TEXT, "help_url_upgrading");
-        final ShadowActivity shadow = shadowOf(Robolectric.buildActivity(HelpTrampoline.class)
-                .withIntent(intent).create().get());
+        final ShadowActivity shadow =
+            shadowOf(Robolectric.buildActivity(HelpTrampoline.class, intent).create().get());
         final Intent launchedIntent = shadow.getNextStartedActivity();
 
         assertThat(ShadowHelpUtils.isGetHelpIntentCalled()).isTrue();
diff --git a/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java
deleted file mode 100644
index d710d7c..0000000
--- a/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AlertDialog;
-import android.app.AppOpsManager;
-import android.app.Fragment;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.os.UserManager;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceManager;
-import android.support.v7.preference.PreferenceScreen;
-import android.view.View;
-
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.TestConfig;
-import com.android.settings.applications.instantapps.InstantAppButtonsController;
-import com.android.settings.applications.instantapps.InstantAppButtonsController.ShowDialogDelegate;
-import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.widget.ActionButtonPreferenceTest;
-import com.android.settings.wrapper.DevicePolicyManagerWrapper;
-import com.android.settingslib.Utils;
-import com.android.settingslib.applications.AppUtils;
-import com.android.settingslib.applications.ApplicationsState.AppEntry;
-import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.HashSet;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(
-    manifest = TestConfig.MANIFEST_PATH,
-    sdk = TestConfig.SDK_VERSION
-)
-public final class AppInfoDashboardFragmentTest {
-
-    private static final String PACKAGE_NAME = "test_package_name";
-
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private Context mContext;
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private UserManager mUserManager;
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private SettingsActivity mActivity;
-    @Mock
-    private DevicePolicyManagerWrapper mDevicePolicyManager;
-    @Mock
-    private PackageManager mPackageManager;
-    @Mock
-    private AppOpsManager mAppOpsManager;
-
-    private FakeFeatureFactory mFeatureFactory;
-    private AppInfoDashboardFragment mAppDetail;
-    private Context mShadowContext;
-
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mFeatureFactory = FakeFeatureFactory.setupForTest();
-        mShadowContext = RuntimeEnvironment.application;
-        mAppDetail = spy(new AppInfoDashboardFragment());
-        doReturn(mActivity).when(mAppDetail).getActivity();
-        doReturn(mShadowContext).when(mAppDetail).getContext();
-        doReturn(mPackageManager).when(mActivity).getPackageManager();
-        doReturn(mAppOpsManager).when(mActivity).getSystemService(Context.APP_OPS_SERVICE);
-        mAppDetail.mActionButtons = ActionButtonPreferenceTest.createMock();
-
-        // Default to not considering any apps to be instant (individual tests can override this).
-        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
-                (InstantAppDataProvider) (i -> false));
-    }
-
-    @Test
-    public void shouldShowUninstallForAll_installForOneOtherUserOnly_shouldReturnTrue() {
-        when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false);
-        when(mUserManager.getUsers().size()).thenReturn(2);
-        ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager);
-        ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager);
-        final ApplicationInfo info = new ApplicationInfo();
-        info.enabled = true;
-        final AppEntry appEntry = mock(AppEntry.class);
-        appEntry.info = info;
-        final PackageInfo packageInfo = mock(PackageInfo.class);
-        ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo);
-
-        assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isTrue();
-    }
-
-    @Test
-    public void shouldShowUninstallForAll_installForSelfOnly_shouldReturnFalse() {
-        when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false);
-        when(mUserManager.getUsers().size()).thenReturn(2);
-        ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager);
-        ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager);
-        final ApplicationInfo info = new ApplicationInfo();
-        info.flags = ApplicationInfo.FLAG_INSTALLED;
-        info.enabled = true;
-        final AppEntry appEntry = mock(AppEntry.class);
-        appEntry.info = info;
-        final PackageInfo packageInfo = mock(PackageInfo.class);
-        ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo);
-
-        assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isFalse();
-    }
-
-    @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();
-    }
-
-    @Test
-    public void packageSizeChange_isOtherPackage_shouldNotRefreshUi() {
-        ReflectionHelpers.setField(mAppDetail, "mPackageName", PACKAGE_NAME);
-        mAppDetail.onPackageSizeChanged("Not_" + PACKAGE_NAME);
-
-        verify(mAppDetail, never()).refreshUi();
-    }
-
-    @Test
-    public void packageSizeChange_isOwnPackage_shouldRefreshUi() {
-        doReturn(Boolean.TRUE).when(mAppDetail).refreshUi();
-        ReflectionHelpers.setField(mAppDetail, "mPackageName", PACKAGE_NAME);
-
-        mAppDetail.onPackageSizeChanged(PACKAGE_NAME);
-
-        verify(mAppDetail).refreshUi();
-    }
-
-    // Tests that we don't show the "uninstall for all users" button for instant apps.
-    @Test
-    public void instantApps_noUninstallForAllButton() {
-        // Make this app appear to be instant.
-        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
-                (InstantAppDataProvider) (i -> true));
-        when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false);
-        when(mUserManager.getUsers().size()).thenReturn(2);
-
-        final ApplicationInfo info = new ApplicationInfo();
-        info.enabled = true;
-        final AppEntry appEntry = mock(AppEntry.class);
-        appEntry.info = info;
-        final PackageInfo packageInfo = mock(PackageInfo.class);
-
-        ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager);
-        ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager);
-        ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo);
-
-        assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isFalse();
-    }
-
-    // Tests that we don't show the uninstall button for instant apps"
-    @Test
-    public void instantApps_noUninstallButton() {
-        // Make this app appear to be instant.
-        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
-                (InstantAppDataProvider) (i -> true));
-        final ApplicationInfo info = new ApplicationInfo();
-        info.flags = ApplicationInfo.FLAG_INSTALLED;
-        info.enabled = true;
-        final AppEntry appEntry = mock(AppEntry.class);
-        appEntry.info = info;
-        final PackageInfo packageInfo = mock(PackageInfo.class);
-        packageInfo.applicationInfo = info;
-
-        ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager);
-        ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry);
-        ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo);
-
-        mAppDetail.initUninstallButtonForUserApp();
-        verify(mAppDetail.mActionButtons).setButton1Visible(false);
-    }
-
-    // Tests that we don't show the force stop button for instant apps (they aren't allowed to run
-    // when they aren't in the foreground).
-    @Test
-    public void instantApps_noForceStop() {
-        // Make this app appear to be instant.
-        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
-                (InstantAppDataProvider) (i -> true));
-        final PackageInfo packageInfo = mock(PackageInfo.class);
-        final AppEntry appEntry = mock(AppEntry.class);
-        final ApplicationInfo info = new ApplicationInfo();
-        appEntry.info = info;
-
-        ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager);
-        ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo);
-        ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry);
-
-        mAppDetail.checkForceStop();
-        verify(mAppDetail.mActionButtons).setButton2Visible(false);
-    }
-
-    @Test
-    public void instantApps_buttonControllerHandlesDialog() {
-        InstantAppButtonsController mockController = mock(InstantAppButtonsController.class);
-        ReflectionHelpers.setField(
-                mAppDetail, "mInstantAppButtonsController", mockController);
-        // Make sure first that button controller is not called for supported dialog id
-        AlertDialog mockDialog = mock(AlertDialog.class);
-        when(mockController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP))
-                .thenReturn(mockDialog);
-        assertThat(mAppDetail.createDialog(InstantAppButtonsController.DLG_CLEAR_APP, 0))
-                .isEqualTo(mockDialog);
-        verify(mockController).createDialog(InstantAppButtonsController.DLG_CLEAR_APP);
-    }
-
-    // A helper class for testing the InstantAppButtonsController - it lets us look up the
-    // preference associated with a key for instant app buttons and get back a mock
-    // LayoutPreference (to avoid a null pointer exception).
-    public static class InstalledAppDetailsWithMockInstantButtons extends InstalledAppDetails {
-        @Mock
-        private LayoutPreference mInstantButtons;
-
-        public InstalledAppDetailsWithMockInstantButtons() {
-            super();
-            MockitoAnnotations.initMocks(this);
-        }
-
-        @Override
-        public Preference findPreference(CharSequence key) {
-            if (key == "instant_app_buttons") {
-                return mInstantButtons;
-            }
-            return super.findPreference(key);
-        }
-    }
-
-    @Test
-    public void instantApps_instantSpecificButtons() {
-        // Make this app appear to be instant.
-        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
-                (InstantAppDataProvider) (i -> true));
-        final PackageInfo packageInfo = mock(PackageInfo.class);
-
-        final InstalledAppDetailsWithMockInstantButtons
-                fragment = new InstalledAppDetailsWithMockInstantButtons();
-        ReflectionHelpers.setField(fragment, "mPackageInfo", packageInfo);
-        ReflectionHelpers.setField(fragment, "mApplicationFeatureProvider",
-                mFeatureFactory.applicationFeatureProvider);
-
-        final InstantAppButtonsController buttonsController =
-                mock(InstantAppButtonsController.class);
-        when(buttonsController.setPackageName(nullable(String.class)))
-                .thenReturn(buttonsController);
-        when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController(
-                nullable(Fragment.class), nullable(View.class), nullable(ShowDialogDelegate.class)))
-                .thenReturn(buttonsController);
-
-        fragment.maybeAddInstantAppButtons();
-        verify(buttonsController).setPackageName(nullable(String.class));
-        verify(buttonsController).show();
-    }
-
-    @Test
-    public void instantApps_removeCorrectPref() {
-        PreferenceScreen mockPreferenceScreen = mock(PreferenceScreen.class);
-        PreferenceManager mockPreferenceManager = mock(PreferenceManager.class);
-        AppDomainsPreference mockAppDomainsPref = mock(AppDomainsPreference.class);
-        PackageInfo mockPackageInfo = mock(PackageInfo.class);
-        PackageManager mockPackageManager = mock(PackageManager.class);
-        ReflectionHelpers.setField(
-                mAppDetail, "mInstantAppDomainsPreference", mockAppDomainsPref);
-        ReflectionHelpers.setField(
-                mAppDetail, "mPreferenceManager", mockPreferenceManager);
-        ReflectionHelpers.setField(
-                mAppDetail, "mPackageInfo", mockPackageInfo);
-        ReflectionHelpers.setField(
-                mAppDetail, "mPm", mockPackageManager);
-        when(mockPreferenceManager.getPreferenceScreen()).thenReturn(mockPreferenceScreen);
-
-        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
-                (InstantAppDataProvider) (i -> false));
-        mAppDetail.prepareInstantAppPrefs();
-
-        // For the non instant case we remove the app domain pref, and leave the launch pref
-        verify(mockPreferenceScreen).removePreference(mockAppDomainsPref);
-
-        // For the instant app case we remove the launch preff, and leave the app domain pref
-        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
-                (InstantAppDataProvider) (i -> true));
-
-        mAppDetail.prepareInstantAppPrefs();
-        // Will be 1 still due to above call
-        verify(mockPreferenceScreen, times(1))
-                .removePreference(mockAppDomainsPref);
-    }
-
-    @Test
-    public void onActivityResult_uninstalledUpdates_shouldInvalidateOptionsMenu() {
-        doReturn(true).when(mAppDetail).refreshUi();
-
-        mAppDetail.onActivityResult(InstalledAppDetails.REQUEST_UNINSTALL, 0, mock(Intent.class));
-
-        verify(mActivity).invalidateOptionsMenu();
-    }
-
-    @Test
-    public void handleDisableable_appIsHomeApp_buttonShouldNotWork() {
-        final ApplicationInfo info = new ApplicationInfo();
-        info.packageName = "pkg";
-        info.enabled = true;
-        final AppEntry appEntry = mock(AppEntry.class);
-        appEntry.info = info;
-        final HashSet<String> homePackages = new HashSet<>();
-        homePackages.add(info.packageName);
-
-        ReflectionHelpers.setField(mAppDetail, "mHomePackages", homePackages);
-        ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry);
-
-        assertThat(mAppDetail.handleDisableable()).isFalse();
-        verify(mAppDetail.mActionButtons).setButton1Text(R.string.disable_text);
-    }
-
-    @Test
-    @Config(shadows = ShadowUtils.class)
-    public void handleDisableable_appIsEnabled_buttonShouldWork() {
-        final ApplicationInfo info = new ApplicationInfo();
-        info.packageName = "pkg";
-        info.enabled = true;
-        info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-
-        final AppEntry appEntry = mock(AppEntry.class);
-        appEntry.info = info;
-        when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn(
-                new HashSet<>());
-
-        ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider",
-                mFeatureFactory.applicationFeatureProvider);
-        ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry);
-
-        assertThat(mAppDetail.handleDisableable()).isTrue();
-        verify(mAppDetail.mActionButtons).setButton1Text(R.string.disable_text);
-    }
-
-    @Test
-    @Config(shadows = ShadowUtils.class)
-    public void handleDisableable_appIsDisabled_buttonShouldShowEnable() {
-        final ApplicationInfo info = new ApplicationInfo();
-        info.packageName = "pkg";
-        info.enabled = false;
-        info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-
-        final AppEntry appEntry = mock(AppEntry.class);
-        appEntry.info = info;
-        when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn(
-                new HashSet<>());
-
-        ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider",
-                mFeatureFactory.applicationFeatureProvider);
-        ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry);
-
-        assertThat(mAppDetail.handleDisableable()).isTrue();
-        verify(mAppDetail.mActionButtons).setButton1Text(R.string.enable_text);
-        verify(mAppDetail.mActionButtons).setButton1Positive(true);
-    }
-
-    @Test
-    @Config(shadows = ShadowUtils.class)
-    public void handleDisableable_appIsEnabledAndInKeepEnabledWhitelist_buttonShouldNotWork() {
-        final ApplicationInfo info = new ApplicationInfo();
-        info.packageName = "pkg";
-        info.enabled = true;
-        info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-
-        final AppEntry appEntry = mock(AppEntry.class);
-        appEntry.info = info;
-
-        final HashSet<String> packages = new HashSet<>();
-        packages.add(info.packageName);
-        when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn(
-                packages);
-
-        ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider",
-                mFeatureFactory.applicationFeatureProvider);
-        ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry);
-
-        assertThat(mAppDetail.handleDisableable()).isFalse();
-        verify(mAppDetail.mActionButtons).setButton1Text(R.string.disable_text);
-    }
-
-    @Test
-    public void initUninstallButtonForUserApp_shouldSetNegativeButton() {
-        final ApplicationInfo info = new ApplicationInfo();
-        info.flags = ApplicationInfo.FLAG_INSTALLED;
-        info.enabled = true;
-        final PackageInfo packageInfo = mock(PackageInfo.class);
-        packageInfo.applicationInfo = info;
-        ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager);
-        ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo);
-
-        mAppDetail.initUninstallButtonForUserApp();
-
-        verify(mAppDetail.mActionButtons).setButton1Positive(false);
-    }
-
-    @Implements(Utils.class)
-    public static class ShadowUtils {
-        @Implementation
-        public static boolean isSystemPackage(Resources resources, PackageManager pm,
-                PackageInfo pkg) {
-            return false;
-        }
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java
new file mode 100644
index 0000000..17b7a22
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java
@@ -0,0 +1,320 @@
+/*
+ * 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.appinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doNothing;
+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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.widget.ActionButtonPreference;
+import com.android.settings.widget.ActionButtonPreferenceTest;
+import com.android.settings.wrapper.DevicePolicyManagerWrapper;
+import com.android.settingslib.Utils;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
+
+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.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class AppActionButtonPreferenceControllerTest {
+
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private DevicePolicyManagerWrapper mDevicePolicyManager;
+    @Mock
+    private AppInfoDashboardFragment mFragment;
+
+    private Context mContext;
+    private AppActionButtonPreferenceController mController;
+    private FakeFeatureFactory mFeatureFactory;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
+        mContext = spy(RuntimeEnvironment.application);
+        mController = spy(new AppActionButtonPreferenceController(mContext, mFragment, "Package1"));
+        mController.mActionButtons = ActionButtonPreferenceTest.createMock();
+        ReflectionHelpers.setField(mController, "mUserManager", mUserManager);
+        ReflectionHelpers.setField(mController, "mDpm", mDevicePolicyManager);
+        ReflectionHelpers.setField(mController, "mApplicationFeatureProvider",
+                mFeatureFactory.applicationFeatureProvider);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+    }
+
+    @Test
+    public void displayPreference_shouldInitializeForceStopButton() {
+        final PreferenceScreen screen = mock(PreferenceScreen.class);
+        final ActionButtonPreference preference = spy(new ActionButtonPreference(mContext));
+        when(screen.findPreference(mController.getPreferenceKey())).thenReturn(preference);
+
+        mController.displayPreference(screen);
+
+        verify(preference).setButton2Positive(false);
+        verify(preference).setButton2Text(R.string.force_stop);
+        verify(preference).setButton2Enabled(false);
+    }
+
+    @Test
+    public void refreshUi_shouldRefreshButton() {
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
+        final ApplicationInfo info = new ApplicationInfo();
+        appEntry.info = info;
+        doNothing().when(mController).checkForceStop(appEntry, packageInfo);
+        doNothing().when(mController).initUninstallButtons(appEntry, packageInfo);
+        when(mFragment.getAppEntry()).thenReturn(appEntry);
+        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
+
+        mController.refreshUi();
+
+        verify(mController).checkForceStop(appEntry, packageInfo);
+        verify(mController).initUninstallButtons(appEntry, packageInfo);
+    }
+
+    @Test
+    public void initUninstallButtonForUserApp_shouldSetNegativeButton() {
+        final ApplicationInfo info = new ApplicationInfo();
+        info.flags = ApplicationInfo.FLAG_INSTALLED;
+        info.enabled = true;
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        packageInfo.applicationInfo = info;
+        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
+
+        assertThat(mController.initUninstallButtonForUserApp()).isTrue();
+        verify(mController.mActionButtons).setButton1Positive(false);
+    }
+
+    // Tests that we don't show the uninstall button for instant apps"
+    @Test
+    public void initUninstallButtonForUserApp_instantApps_noUninstallButton() {
+        // Make this app appear to be instant.
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> true));
+        final ApplicationInfo info = new ApplicationInfo();
+        info.flags = ApplicationInfo.FLAG_INSTALLED;
+        info.enabled = true;
+        final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
+        appEntry.info = info;
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        packageInfo.applicationInfo = info;
+        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
+
+        assertThat(mController.initUninstallButtonForUserApp()).isFalse();
+        verify(mController.mActionButtons).setButton1Visible(false);
+    }
+
+    @Test
+    public void initUninstallButtonForUserApp_notInstalledForCurrentUser_shouldDisableButton() {
+        final ApplicationInfo info = new ApplicationInfo();
+        info.enabled = true;
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        packageInfo.applicationInfo = info;
+        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
+        final int userID1 = 1;
+        final int userID2 = 2;
+        final List<UserInfo> userInfos = new ArrayList<>();
+        userInfos.add(new UserInfo(userID1, "User1", UserInfo.FLAG_PRIMARY));
+        userInfos.add(new UserInfo(userID2, "User2", UserInfo.FLAG_GUEST));
+        when(mUserManager.getUsers(true)).thenReturn(userInfos);
+
+        assertThat(mController.initUninstallButtonForUserApp()).isFalse();
+    }
+
+    // Tests that we don't show the force stop button for instant apps (they aren't allowed to run
+    // when they aren't in the foreground).
+    @Test
+    public void checkForceStop_instantApps_shouldNotShowForceStop() {
+        // Make this app appear to be instant.
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> true));
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
+        final ApplicationInfo info = new ApplicationInfo();
+        appEntry.info = info;
+
+        mController.checkForceStop(appEntry, packageInfo);
+
+        verify(mController.mActionButtons).setButton2Visible(false);
+    }
+
+    @Test
+    public void checkForceStop_hasActiveAdmin_shouldDisableForceStop() {
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> false));
+        final String packageName = "Package1";
+        final PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = packageName;
+        final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
+        when(mDevicePolicyManager.packageHasActiveAdmins(packageName)).thenReturn(true);
+
+        mController.checkForceStop(appEntry, packageInfo);
+
+        verify(mController.mActionButtons).setButton2Enabled(false);
+    }
+
+    @Test
+    public void checkForceStop_appRunning_shouldEnableForceStop() {
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> false));
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
+        final ApplicationInfo info = new ApplicationInfo();
+        appEntry.info = info;
+
+        mController.checkForceStop(appEntry, packageInfo);
+
+        verify(mController.mActionButtons).setButton2Enabled(true);
+    }
+
+    @Test
+    public void checkForceStop_appStopped_shouldQueryPackageRestart() {
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> false));
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
+        final ApplicationInfo info = new ApplicationInfo();
+        appEntry.info = info;
+        info.flags = ApplicationInfo.FLAG_STOPPED;
+        info.packageName = "com.android.setting";
+
+        mController.checkForceStop(appEntry, packageInfo);
+
+        verify(mContext).sendOrderedBroadcastAsUser(argThat(intent-> intent != null
+                        && intent.getAction().equals(Intent.ACTION_QUERY_PACKAGE_RESTART)),
+                any(UserHandle.class), nullable(String.class), any(BroadcastReceiver.class),
+                nullable(Handler.class), anyInt(), nullable(String.class), nullable(Bundle.class));
+    }
+
+    @Test
+    public void handleDisableable_appIsHomeApp_buttonShouldNotWork() {
+        final ApplicationInfo info = new ApplicationInfo();
+        info.packageName = "pkg";
+        info.enabled = true;
+        final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
+        appEntry.info = info;
+        final HashSet<String> homePackages = new HashSet<>();
+        homePackages.add(info.packageName);
+        ReflectionHelpers.setField(mController, "mHomePackages", homePackages);
+
+        assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isFalse();
+        verify(mController.mActionButtons).setButton1Text(R.string.disable_text);
+    }
+
+    @Test
+    @Config(shadows = ShadowUtils.class)
+    public void handleDisableable_appIsEnabled_buttonShouldWork() {
+        final ApplicationInfo info = new ApplicationInfo();
+        info.packageName = "pkg";
+        info.enabled = true;
+        info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
+        appEntry.info = info;
+        when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages())
+                .thenReturn(new HashSet<>());
+
+        assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isTrue();
+        verify(mController.mActionButtons).setButton1Text(R.string.disable_text);
+    }
+
+    @Test
+    @Config(shadows = ShadowUtils.class)
+    public void handleDisableable_appIsDisabled_buttonShouldShowEnable() {
+        final ApplicationInfo info = new ApplicationInfo();
+        info.packageName = "pkg";
+        info.enabled = false;
+        info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
+        appEntry.info = info;
+        when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages())
+                .thenReturn(new HashSet<>());
+
+        assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isTrue();
+        verify(mController.mActionButtons).setButton1Text(R.string.enable_text);
+        verify(mController.mActionButtons).setButton1Positive(true);
+    }
+
+    @Test
+    @Config(shadows = ShadowUtils.class)
+    public void handleDisableable_appIsEnabledAndInKeepEnabledWhitelist_buttonShouldNotWork() {
+        final ApplicationInfo info = new ApplicationInfo();
+        info.packageName = "pkg";
+        info.enabled = true;
+        info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
+        appEntry.info = info;
+        final HashSet<String> packages = new HashSet<>();
+        packages.add(info.packageName);
+        when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages())
+                .thenReturn(packages);
+
+        assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isFalse();
+        verify(mController.mActionButtons).setButton1Text(R.string.disable_text);
+    }
+
+    @Implements(Utils.class)
+    public static class ShadowUtils {
+        @Implementation
+        public static boolean isSystemPackage(Resources resources, PackageManager pm,
+                PackageInfo pkg) {
+            return false;
+        }
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java
index 3516445..91833f5 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java
@@ -40,7 +40,6 @@
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.settings.SettingsActivity;
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java
index b02e01e..76160ee 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java
@@ -37,7 +37,6 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.datausage.AppDataUsage;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java
new file mode 100644
index 0000000..87b82ad
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java
@@ -0,0 +1,241 @@
+/*
+ * 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.appinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+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;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+
+import com.android.settings.SettingsActivity;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.DevicePolicyManagerWrapper;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(
+    manifest = TestConfig.MANIFEST_PATH,
+    sdk = TestConfig.SDK_VERSION
+)
+public final class AppInfoDashboardFragmentTest {
+
+    private static final String PACKAGE_NAME = "test_package_name";
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private UserManager mUserManager;
+    @Mock
+    private SettingsActivity mActivity;
+    @Mock
+    private DevicePolicyManagerWrapper mDevicePolicyManager;
+    @Mock
+    private PackageManager mPackageManager;
+
+    private AppInfoDashboardFragment mFragment;
+    private Context mShadowContext;
+
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mShadowContext = RuntimeEnvironment.application;
+        mFragment = spy(new AppInfoDashboardFragment());
+        doReturn(mActivity).when(mFragment).getActivity();
+        doReturn(mShadowContext).when(mFragment).getContext();
+        doReturn(mPackageManager).when(mActivity).getPackageManager();
+
+        // Default to not considering any apps to be instant (individual tests can override this).
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> false));
+    }
+
+    @Test
+    public void shouldShowUninstallForAll_installForOneOtherUserOnly_shouldReturnTrue() {
+        when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false);
+        when(mUserManager.getUsers().size()).thenReturn(2);
+        ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager);
+        ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager);
+        final ApplicationInfo info = new ApplicationInfo();
+        info.enabled = true;
+        final AppEntry appEntry = mock(AppEntry.class);
+        appEntry.info = info;
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo);
+
+        assertThat(mFragment.shouldShowUninstallForAll(appEntry)).isTrue();
+    }
+
+    @Test
+    public void shouldShowUninstallForAll_installForSelfOnly_shouldReturnFalse() {
+        when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false);
+        when(mUserManager.getUsers().size()).thenReturn(2);
+        ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager);
+        ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager);
+        final ApplicationInfo info = new ApplicationInfo();
+        info.flags = ApplicationInfo.FLAG_INSTALLED;
+        info.enabled = true;
+        final AppEntry appEntry = mock(AppEntry.class);
+        appEntry.info = info;
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo);
+
+        assertThat(mFragment.shouldShowUninstallForAll(appEntry)).isFalse();
+    }
+
+    @Test
+    public void launchFragment_hasNoPackageInfo_shouldFinish() {
+        ReflectionHelpers.setField(mFragment, "mPackageInfo", null);
+
+        assertThat(mFragment.ensurePackageInfoAvailable(mActivity)).isFalse();
+        verify(mActivity).finishAndRemoveTask();
+    }
+
+    @Test
+    public void launchFragment_hasPackageInfo_shouldReturnTrue() {
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo);
+
+        assertThat(mFragment.ensurePackageInfoAvailable(mActivity)).isTrue();
+        verify(mActivity, never()).finishAndRemoveTask();
+    }
+
+    @Test
+    public void packageSizeChange_isOtherPackage_shouldNotRefreshUi() {
+        ReflectionHelpers.setField(mFragment, "mPackageName", PACKAGE_NAME);
+        mFragment.onPackageSizeChanged("Not_" + PACKAGE_NAME);
+
+        verify(mFragment, never()).refreshUi();
+    }
+
+    @Test
+    public void packageSizeChange_isOwnPackage_shouldRefreshUi() {
+        doReturn(Boolean.TRUE).when(mFragment).refreshUi();
+        ReflectionHelpers.setField(mFragment, "mPackageName", PACKAGE_NAME);
+
+        mFragment.onPackageSizeChanged(PACKAGE_NAME);
+
+        verify(mFragment).refreshUi();
+    }
+
+    // Tests that we don't show the "uninstall for all users" button for instant apps.
+    @Test
+    public void instantApps_noUninstallForAllButton() {
+        // Make this app appear to be instant.
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> true));
+        when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false);
+        when(mUserManager.getUsers().size()).thenReturn(2);
+
+        final ApplicationInfo info = new ApplicationInfo();
+        info.enabled = true;
+        final AppEntry appEntry = mock(AppEntry.class);
+        appEntry.info = info;
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+
+        ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager);
+        ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager);
+        ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo);
+
+        assertThat(mFragment.shouldShowUninstallForAll(appEntry)).isFalse();
+    }
+
+    @Test
+    public void onActivityResult_uninstalledUpdates_shouldInvalidateOptionsMenu() {
+        doReturn(true).when(mFragment).refreshUi();
+
+        mFragment.onActivityResult(mFragment.REQUEST_UNINSTALL, 0, mock(Intent.class));
+
+        verify(mActivity).invalidateOptionsMenu();
+    }
+
+    @Test
+    public void getNumberOfUserWithPackageInstalled_twoUsersInstalled_shouldReturnTwo()
+            throws PackageManager.NameNotFoundException{
+        final String packageName = "Package1";
+        final int userID1 = 1;
+        final int userID2 = 2;
+        final List<UserInfo> userInfos = new ArrayList<>();
+        userInfos.add(new UserInfo(userID1, "User1", UserInfo.FLAG_PRIMARY));
+        userInfos.add(new UserInfo(userID2, "yue", UserInfo.FLAG_GUEST));
+        when(mUserManager.getUsers(true)).thenReturn(userInfos);
+        ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager);
+        final ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.flags = ApplicationInfo.FLAG_INSTALLED;
+        when(mPackageManager.getApplicationInfoAsUser(
+                packageName, PackageManager.GET_META_DATA, userID1))
+                .thenReturn(appInfo);
+        when(mPackageManager.getApplicationInfoAsUser(
+                packageName, PackageManager.GET_META_DATA, userID2))
+                .thenReturn(appInfo);
+        ReflectionHelpers.setField(mFragment, "mPm", mPackageManager);
+
+        assertThat(mFragment.getNumberOfUserWithPackageInstalled(packageName)).isEqualTo(2);
+    }
+
+    @Test
+    public void getNumberOfUserWithPackageInstalled_oneUserInstalled_shouldReturnOne()
+            throws PackageManager.NameNotFoundException{
+        final String packageName = "Package1";
+        final int userID1 = 1;
+        final int userID2 = 2;
+        final List<UserInfo> userInfos = new ArrayList<>();
+        userInfos.add(new UserInfo(userID1, "User1", UserInfo.FLAG_PRIMARY));
+        userInfos.add(new UserInfo(userID2, "yue", UserInfo.FLAG_GUEST));
+        when(mUserManager.getUsers(true)).thenReturn(userInfos);
+        ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager);
+        final ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.flags = ApplicationInfo.FLAG_INSTALLED;
+        when(mPackageManager.getApplicationInfoAsUser(
+                packageName, PackageManager.GET_META_DATA, userID1))
+                .thenReturn(appInfo);
+        when(mPackageManager.getApplicationInfoAsUser(
+                packageName, PackageManager.GET_META_DATA, userID2))
+                .thenThrow(new PackageManager.NameNotFoundException());
+        ReflectionHelpers.setField(mFragment, "mPm", mPackageManager);
+
+        assertThat(mFragment.getNumberOfUserWithPackageInstalled(packageName)).isEqualTo(1);
+
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBaseTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBaseTest.java
index 25dcab3..51b6ddf 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBaseTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBaseTest.java
@@ -32,7 +32,6 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.notification.AppNotificationSettings;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settingslib.applications.ApplicationsState;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java
new file mode 100644
index 0000000..d8d11bc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.appinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+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;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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 AppInstallerInfoPreferenceControllerTest {
+
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ApplicationInfo mAppInfo;
+    @Mock
+    private AppInfoDashboardFragment mFragment;
+    @Mock
+    private Preference mPreference;
+
+    private Context mContext;
+    private AppInstallerInfoPreferenceController mController;
+
+    @Before
+    public void setUp() throws PackageManager.NameNotFoundException {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        final String installerPackage = "Installer1";
+        when(mPackageManager.getInstallerPackageName(anyString())).thenReturn(installerPackage);
+        when(mPackageManager.getApplicationInfo(eq(installerPackage), anyInt()))
+                .thenReturn(mAppInfo);
+        mController = new AppInstallerInfoPreferenceController(mContext, mFragment, "Package1");
+    }
+
+    @Test
+    public void getAvailabilityStatus_managedProfile_shouldReturnDisabled() {
+        when(mUserManager.isManagedProfile()).thenReturn(true);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
+    }
+
+    @Test
+    public void getAvailabilityStatus_noAppLabel_shouldReturnDisabled() {
+        when(mUserManager.isManagedProfile()).thenReturn(false);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
+    }
+
+    @Test
+    public void getAvailabilityStatus_hasAppLabel_shouldReturnAvailable() {
+        when(mUserManager.isManagedProfile()).thenReturn(false);
+        when(mAppInfo.loadLabel(mPackageManager)).thenReturn("Label1");
+        mController = new AppInstallerInfoPreferenceController(mContext, mFragment, "Package1");
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
+    }
+
+    @Test
+    public void updateState_shouldSetSummary() {
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        packageInfo.applicationInfo = mAppInfo;
+        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setSummary(any());
+    }
+
+    @Test
+    public void updateState_noAppStoreLink_shouldDisablePreference() {
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        packageInfo.applicationInfo = mAppInfo;
+        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
+        when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(null);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setEnabled(false);
+    }
+
+    @Test
+    public void updateState_hasAppStoreLink_shouldSetPreferenceIntent() {
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        packageInfo.applicationInfo = mAppInfo;
+        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
+        final ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.packageName = "Pkg1";
+        resolveInfo.activityInfo.name = "Name1";
+        when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference, never()).setEnabled(false);
+        verify(mPreference).setIntent(any(Intent.class));
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java
index d74e301..47844c5 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java
@@ -34,7 +34,6 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.applications.ProcStatsData;
 import com.android.settings.applications.ProcessStatsDetail;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java
index 482f33c..0b747a8 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java
@@ -30,7 +30,6 @@
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.notification.AppNotificationSettings;
 import com.android.settings.notification.NotificationBackend;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java
index b708232..c5003cc 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java
@@ -32,7 +32,6 @@
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.applications.AppLaunchSettings;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settingslib.applications.AppUtils;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java
index f9f8d98..f0b415c 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java
@@ -34,7 +34,6 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settingslib.applications.ApplicationsState;
 
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java
index 729914a..c069517 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java
@@ -32,7 +32,6 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.applications.AppStorageSettings;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java
index 7418489..d6ecf3e 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java
@@ -25,7 +25,6 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java
index 358e50d..e44fdfb 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java
@@ -31,7 +31,6 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.applications.DefaultAppSettings;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceControllerTest.java
index a7468b5..18a29e3 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceControllerTest.java
@@ -32,7 +32,6 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceControllerTest.java
index d500be9..7e542f7 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceControllerTest.java
@@ -28,7 +28,6 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourcesDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourcesDetailsTest.java
new file mode 100644
index 0000000..ce38a56
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourcesDetailsTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.appinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.os.UserManager;
+
+import com.android.settings.TestConfig;
+import com.android.settings.applications.AppStateInstallAppsBridge;
+import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ExternalSourcesDetailsTest {
+
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private RestrictedSwitchPreference mSwitchPref;
+    @Mock
+    private PackageInfo mPackageInfo;
+
+    private ExternalSourcesDetails mFragment;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mFragment = new ExternalSourcesDetails();
+        ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager);
+        ReflectionHelpers.setField(mFragment, "mSwitchPref", mSwitchPref);
+    }
+
+    @Test
+    public void refreshUi_noPackageInfo_shouldReturnFalseAndNoCrash() {
+        mFragment.refreshUi();
+
+        assertThat(mFragment.refreshUi()).isFalse();
+        // should not crash
+    }
+
+    @Test
+    public void refreshUi_noApplicationInfo_shouldReturnFalseAndNoCrash() {
+        ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo);
+
+        mFragment.refreshUi();
+
+        assertThat(mFragment.refreshUi()).isFalse();
+        // should not crash
+    }
+
+    @Test
+    public void refreshUi_hasApplicationInfo_shouldReturnTrue() {
+        ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo);
+        mPackageInfo.applicationInfo = new ApplicationInfo();
+        final AppStateInstallAppsBridge appBridge = mock(AppStateInstallAppsBridge.class);
+        ReflectionHelpers.setField(mFragment, "mAppBridge", appBridge);
+        when(appBridge.createInstallAppsStateFor(nullable(String.class), anyInt()))
+                .thenReturn(mock(InstallAppsState.class));
+
+        mFragment.refreshUi();
+
+        assertThat(mFragment.refreshUi()).isTrue();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java
new file mode 100644
index 0000000..eb8a082
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.appinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.nullable;
+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.app.AlertDialog;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.view.View;
+
+import com.android.settings.TestConfig;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.applications.instantapps.InstantAppButtonsController;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
+
+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.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class InstantAppButtonsPreferenceControllerTest {
+
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ApplicationInfo mAppInfo;
+    @Mock
+    private AppInfoDashboardFragment mFragment;
+
+    private Context mContext;
+    private InstantAppButtonsPreferenceController mController;
+    private FakeFeatureFactory mFeatureFactory;
+
+    @Before
+    public void setUp() throws PackageManager.NameNotFoundException {
+        MockitoAnnotations.initMocks(this);
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
+        mContext = spy(RuntimeEnvironment.application);
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        packageInfo.applicationInfo = mAppInfo;
+        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
+        mController =
+                spy(new InstantAppButtonsPreferenceController(mContext, mFragment, "Package1"));
+    }
+
+    @Test
+    public void getAvailabilityStatus_notInstantApp_shouldReturnDisabled() {
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> false));
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
+    }
+
+    @Test
+    public void getAvailabilityStatus_isInstantApp_shouldReturnAvailable() {
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> true));
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
+    }
+
+    @Test
+    public void displayPreference_shouldSetPreferenceTitle() {
+        final PreferenceScreen screen = mock(PreferenceScreen.class);
+        final LayoutPreference preference = mock(LayoutPreference.class);
+        when(screen.findPreference(mController.getPreferenceKey())).thenReturn(preference);
+        when(mController.getApplicationFeatureProvider())
+                .thenReturn(mFeatureFactory.applicationFeatureProvider);
+        final InstantAppButtonsController buttonsController =
+                mock(InstantAppButtonsController.class);
+        when(buttonsController.setPackageName(nullable(String.class)))
+                .thenReturn(buttonsController);
+        when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController(
+                nullable(Fragment.class), nullable(View.class),
+                nullable(InstantAppButtonsController.ShowDialogDelegate.class)))
+                .thenReturn(buttonsController);
+
+        mController.displayPreference(screen);
+
+        verify(buttonsController).setPackageName(nullable(String.class));
+        verify(buttonsController).show();
+    }
+
+    @Test
+    public void createDialog_shouldReturnDialogFromButtonController() {
+        final InstantAppButtonsController buttonsController =
+                mock(InstantAppButtonsController.class);
+        ReflectionHelpers.setField(
+                mController, "mInstantAppButtonsController", buttonsController);
+        final AlertDialog mockDialog = mock(AlertDialog.class);
+        when(buttonsController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP))
+                .thenReturn(mockDialog);
+
+        assertThat(mController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP))
+                .isEqualTo(mockDialog);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java
new file mode 100644
index 0000000..bb0b42a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.appinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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.pm.ApplicationInfo;
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.ArraySet;
+
+import com.android.settings.TestConfig;
+import com.android.settings.applications.AppDomainsPreference;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
+
+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.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class InstantAppDomainsPreferenceControllerTest {
+
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ApplicationInfo mAppInfo;
+    @Mock
+    private AppInfoDashboardFragment mFragment;
+    @Mock
+    private AppDomainsPreference mPreference;
+
+    private Context mContext;
+    private InstantAppDomainsPreferenceController mController;
+
+    @Before
+    public void setUp() throws PackageManager.NameNotFoundException {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        final PackageInfo packageInfo = mock(PackageInfo.class);
+        packageInfo.applicationInfo = mAppInfo;
+        packageInfo.packageName = "Package1";
+        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
+        mController = new InstantAppDomainsPreferenceController(mContext, mFragment);
+    }
+
+    @Test
+    public void getAvailabilityStatus_notInstantApp_shouldReturnDisabled() {
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> false));
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
+    }
+
+    @Test
+    public void getAvailabilityStatus_isInstantApp_shouldReturnAvailable() {
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (i -> true));
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
+    }
+
+    @Test
+    public void updateState_shouldSetPreferenceTitle() {
+        final String[] domain = { "Domain1" };
+        final ArraySet<String> domains = new ArraySet<>();
+        domains.add(domain[0]);
+        final List<IntentFilterVerificationInfo> infoList = new ArrayList<>();
+        final IntentFilterVerificationInfo info =
+                new IntentFilterVerificationInfo("Package1", domains);
+        infoList.add(info);
+
+        when(mPackageManager.getIntentFilterVerifications("Package1")).thenReturn(infoList);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setTitles(domain);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceControllerTest.java
index 7d81168..cf37b36 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceControllerTest.java
@@ -29,7 +29,6 @@
 
 import com.android.settings.R;
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceControllerTest.java
index fabcbb2..08133f0 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceControllerTest.java
@@ -32,7 +32,6 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.TestConfig;
-import com.android.settings.applications.AppInfoDashboardFragment;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceControllerTest.java
index cde95cd..62a0d42 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceControllerTest.java
@@ -47,6 +47,7 @@
 public class BluetoothDeviceRenamePreferenceControllerTest {
 
     private static final String DEVICE_NAME = "Nightshade";
+    private static final String PREF_KEY = "bt_rename_devices";
 
     @Mock
     private LocalBluetoothAdapter mLocalAdapter;
@@ -66,10 +67,10 @@
 
         mContext = spy(RuntimeEnvironment.application);
         mPreference = new Preference(mContext);
-        mPreference.setKey(BluetoothDeviceRenamePreferenceController.PREF_KEY);
+        mPreference.setKey(PREF_KEY);
 
         mController = new BluetoothDeviceRenamePreferenceController(
-                mContext, mFragment, mLocalAdapter);
+                mContext, PREF_KEY, mFragment, mLocalAdapter);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceControllerTest.java
new file mode 100644
index 0000000..aa9d266
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceControllerTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+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 BluetoothSwitchPreferenceControllerTest {
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private LocalBluetoothManager mBluetoothManager;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private SwitchPreference mPreference;
+    @Mock
+    private RestrictionUtils mRestrictionUtils;
+    @Mock
+    private LocalBluetoothAdapter mLocalBluetoothAdapter;
+
+    private Context mContext;
+    private BluetoothSwitchPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application.getApplicationContext());
+        FakeFeatureFactory.setupForTest();
+
+        mController = new BluetoothSwitchPreferenceController(
+                mContext, mBluetoothManager, mRestrictionUtils);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
+    }
+
+    @Test
+    public void testGetAvailabilityStatus_adapterNull_returnDisabled() {
+        mController.mBluetoothAdapter = null;
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.DISABLED_UNSUPPORTED);
+    }
+
+    @Test
+    public void testGetAvailabilityStatus_adapterExisted_returnAvailable() {
+        mController.mBluetoothAdapter = mLocalBluetoothAdapter;
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.AVAILABLE);
+    }
+
+    @Test
+    public void testOnStart_shouldRegisterPreferenceChangeListener() {
+        mController.displayPreference(mScreen);
+        mController.onStart();
+
+        verify(mPreference).setOnPreferenceChangeListener(
+                any(BluetoothSwitchPreferenceController.SwitchController.class));
+    }
+
+    @Test
+    public void testOnStop_shouldRegisterPreferenceChangeListener() {
+        mController.displayPreference(mScreen);
+        mController.onStart();
+
+        mController.onStop();
+
+        verify(mPreference).setOnPreferenceChangeListener(null);
+    }
+
+    @Test
+    public void testIsChecked_adapterNull_returnFalse() {
+        mController.mBluetoothAdapter = null;
+
+        assertThat(mController.isChecked()).isFalse();
+    }
+
+    @Test
+    public void testIsChecked_adapterExisted_returnFromAdapter() {
+        mController.mBluetoothAdapter = mLocalBluetoothAdapter;
+        doReturn(true).when(mLocalBluetoothAdapter).isEnabled();
+
+        assertThat(mController.isChecked()).isTrue();
+    }
+
+    @Test
+    public void testSetChecked_adapterExisted() {
+        mController.mBluetoothAdapter = mLocalBluetoothAdapter;
+
+        mController.setChecked(true);
+
+        verify(mLocalBluetoothAdapter).setBluetoothEnabled(true);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
index f9efc0b..aa5eb67 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
@@ -37,6 +37,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
@@ -45,13 +46,17 @@
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class ConnectedDeviceGroupControllerTest {
+    private static final String PREFERENCE_KEY_1 = "pref_key_1";
+
     @Mock
     private DashboardFragment mDashboardFragment;
     @Mock
     private ConnectedBluetoothDeviceUpdater mConnectedBluetoothDeviceUpdater;
     @Mock
-    private PreferenceScreen mPreferenceScreen;
+    private ConnectedUsbDeviceUpdater mConnectedUsbDeviceUpdater;
     @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private PreferenceManager mPreferenceManager;
 
     private PreferenceGroup mPreferenceGroup;
@@ -66,30 +71,33 @@
 
         mContext = RuntimeEnvironment.application;
         mPreference = new Preference(mContext);
+        mPreference.setKey(PREFERENCE_KEY_1);
         mLifecycle = new Lifecycle(() -> mLifecycle);
         mPreferenceGroup = spy(new PreferenceScreen(mContext, null));
         doReturn(mPreferenceManager).when(mPreferenceGroup).getPreferenceManager();
         doReturn(mContext).when(mDashboardFragment).getContext();
 
         mConnectedDeviceGroupController = new ConnectedDeviceGroupController(mDashboardFragment,
-                mLifecycle, mConnectedBluetoothDeviceUpdater);
+                mLifecycle, mConnectedBluetoothDeviceUpdater, mConnectedUsbDeviceUpdater);
         mConnectedDeviceGroupController.mPreferenceGroup = mPreferenceGroup;
     }
 
     @Test
-    public void testOnDeviceAdded_firstAdd_becomeVisible() {
+    public void testOnDeviceAdded_firstAdd_becomeVisibleAndPreferenceAdded() {
         mConnectedDeviceGroupController.onDeviceAdded(mPreference);
 
         assertThat(mPreferenceGroup.isVisible()).isTrue();
+        assertThat(mPreferenceGroup.findPreference(PREFERENCE_KEY_1)).isEqualTo(mPreference);
     }
 
     @Test
-    public void testOnDeviceRemoved_lastRemove_becomeInvisible() {
+    public void testOnDeviceRemoved_lastRemove_becomeInvisibleAndPreferenceRemoved() {
         mPreferenceGroup.addPreference(mPreference);
 
         mConnectedDeviceGroupController.onDeviceRemoved(mPreference);
 
         assertThat(mPreferenceGroup.isVisible()).isFalse();
+        assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
     }
 
     @Test
@@ -117,9 +125,11 @@
         // register the callback in onStart()
         mLifecycle.handleLifecycleEvent(android.arch.lifecycle.Lifecycle.Event.ON_START);
         verify(mConnectedBluetoothDeviceUpdater).registerCallback();
+        verify(mConnectedUsbDeviceUpdater).registerCallback();
 
         // unregister the callback in onStop()
         mLifecycle.handleLifecycleEvent(android.arch.lifecycle.Lifecycle.Event.ON_STOP);
         verify(mConnectedBluetoothDeviceUpdater).unregisterCallback();
+        verify(mConnectedUsbDeviceUpdater).unregisterCallback();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdaterTest.java
new file mode 100644
index 0000000..16cd3a7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdaterTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.connecteddevice;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.deviceinfo.UsbBackend;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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 ConnectedUsbDeviceUpdaterTest {
+    private Context mContext;
+    private ConnectedUsbDeviceUpdater mDeviceUpdater;
+
+    @Mock
+    private UsbConnectionBroadcastReceiver mUsbReceiver;
+    @Mock
+    private DevicePreferenceCallback mDevicePreferenceCallback;
+    @Mock
+    private UsbBackend mUsbBackend;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mDeviceUpdater = new ConnectedUsbDeviceUpdater(mContext, mDevicePreferenceCallback,
+                mUsbBackend);
+        mDeviceUpdater.mUsbReceiver = mUsbReceiver;
+    }
+
+    @Test
+    public void testInitUsbPreference_preferenceInit() {
+        mDeviceUpdater.initUsbPreference(mContext);
+
+        assertThat(mDeviceUpdater.mUsbPreference.getTitle()).isEqualTo("USB");
+        assertThat(mDeviceUpdater.mUsbPreference.getIcon()).isEqualTo(mContext.getDrawable(
+                R.drawable.ic_usb));
+        assertThat(mDeviceUpdater.mUsbPreference.isSelectable()).isFalse();
+    }
+
+    @Test
+    public void testInitUsbPreference_usbConnected_preferenceAdded() {
+        doReturn(true).when(mUsbReceiver).isConnected();
+
+        mDeviceUpdater.initUsbPreference(mContext);
+
+        verify(mDevicePreferenceCallback).onDeviceAdded(mDeviceUpdater.mUsbPreference);
+    }
+
+    @Test
+    public void testInitUsbPreference_usbDisconnected_preferenceRemoved() {
+        doReturn(false).when(mUsbReceiver).isConnected();
+
+        mDeviceUpdater.initUsbPreference(mContext);
+
+        verify(mDevicePreferenceCallback).onDeviceRemoved(mDeviceUpdater.mUsbPreference);
+    }
+
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiverTest.java
new file mode 100644
index 0000000..06bd5b7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiverTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.connecteddevice;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbManager;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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 java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class UsbConnectionBroadcastReceiverTest {
+    private Context mContext;
+    private UsbConnectionBroadcastReceiver mReceiver;
+    private ShadowApplication mShadowApplication;
+
+    @Mock
+    private UsbConnectionBroadcastReceiver.UsbConnectionListener mListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mShadowApplication = ShadowApplication.getInstance();
+        mContext = RuntimeEnvironment.application;
+        mReceiver = new UsbConnectionBroadcastReceiver(mContext, mListener);
+    }
+
+    @Test
+    public void testOnReceive_usbConnected_invokeCallback() {
+        final Intent intent = new Intent();
+        intent.putExtra(UsbManager.USB_CONNECTED, true);
+
+        mReceiver.onReceive(mContext, intent);
+
+        verify(mListener).onUsbConnectionChanged(true);
+    }
+
+    @Test
+    public void testOnReceive_usbDisconnected_invokeCallback() {
+        final Intent intent = new Intent();
+        intent.putExtra(UsbManager.USB_CONNECTED, false);
+
+        mReceiver.onReceive(mContext, intent);
+
+        verify(mListener).onUsbConnectionChanged(false);
+    }
+
+    @Test
+    public void testRegister_invokeMethodTwice_registerOnce() {
+        mReceiver.register();
+        mReceiver.register();
+
+        final List<BroadcastReceiver> receivers = mShadowApplication.getReceiversForIntent(
+                new Intent(UsbManager.ACTION_USB_STATE));
+        assertHasOneUsbConnectionBroadcastReceiver(receivers);
+    }
+
+    @Test
+    public void testUnregister_invokeMethodTwice_unregisterOnce() {
+        mReceiver.register();
+        mReceiver.unregister();
+        mReceiver.unregister();
+
+        final List<BroadcastReceiver> receivers = mShadowApplication.getReceiversForIntent(
+                new Intent(UsbManager.ACTION_USB_STATE));
+        assertHasNoUsbConnectionBroadcastReceiver(receivers);
+    }
+
+    private void assertHasOneUsbConnectionBroadcastReceiver(List<BroadcastReceiver> receivers) {
+        boolean hasReceiver = false;
+        for (final BroadcastReceiver receiver : receivers) {
+            if (receiver instanceof UsbConnectionBroadcastReceiver) {
+                // If hasReceiver is true, then we're at the second copy of it so fail.
+                assertWithMessage(
+                        "Only one instance of UsbConnectionBroadcastReceiver should be "
+                                + "registered").that(
+                        hasReceiver).isFalse();
+                hasReceiver = true;
+            }
+        }
+        assertThat(hasReceiver).isTrue();
+    }
+
+    private void assertHasNoUsbConnectionBroadcastReceiver(List<BroadcastReceiver> receivers) {
+        for (final BroadcastReceiver receiver : receivers) {
+            assertThat(receiver instanceof UsbConnectionBroadcastReceiver).isFalse();
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java b/tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java
new file mode 100644
index 0000000..ed4e815
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java
@@ -0,0 +1,275 @@
+package com.android.settings.core;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.provider.SearchIndexableResource;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.search.DatabaseIndexingUtils;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchIndexableResources;
+import com.android.settings.search.XmlParserUtils;
+import com.android.settings.security.SecuritySettings;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class XmlControllerAttributeTest {
+
+    // List of classes that are too hard to mock in order to retrieve xml information.
+    private final List<Class> illegalClasses = new ArrayList<>(
+            Arrays.asList(
+                    SecuritySettings.class
+            ));
+
+    // List of XML that could be retrieved from the illegalClasses list.
+    private final List<Integer> whitelistXml = new ArrayList<>(
+            Arrays.asList(
+                    R.xml.security_settings_misc,
+                    R.xml.security_settings_lockscreen_profile,
+                    R.xml.security_settings_lockscreen,
+                    R.xml.security_settings_chooser,
+                    R.xml.security_settings_pattern_profile,
+                    R.xml.security_settings_pin_profile,
+                    R.xml.security_settings_password_profile,
+                    R.xml.security_settings_pattern,
+                    R.xml.security_settings_pin,
+                    R.xml.security_settings_password,
+                    R.xml.security_settings,
+                    R.xml.security_settings_status
+            ));
+
+    private static final String NO_VALID_CONSTRUCTOR_ERROR =
+            "Controllers added in XML need a constructor following either:"
+                    + "\n\tClassName(Context)\n\tClassName(Context, String)"
+                    + "\nThese controllers are missing a valid constructor:\n";
+
+    private static final String NOT_BASE_PREF_CONTROLLER_ERROR =
+            "Controllers added in XML need to extend com.android.settings.core"
+                    + ".BasePreferenceController\nThese controllers do not:\n";
+
+    private static final String BAD_CLASSNAME_ERROR =
+            "The following controllers set in the XML did not have valid class names:\n";
+
+    private static final String BAD_CONSTRUCTOR_ERROR =
+            "The constructor provided by the following classes were insufficient to instantiate "
+                    + "the object. It could be due to being an interface, abstract, or an "
+                    + "IllegalAccessException. Please fix the following classes:\n";
+
+    Context mContext;
+
+    private Set<Class> mProviderClassesCopy;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mProviderClassesCopy = new HashSet<>(SearchIndexableResources.providerValues());
+    }
+
+    @After
+    public void cleanUp() {
+        SearchIndexableResources.providerValues().clear();
+        SearchIndexableResources.providerValues().addAll(mProviderClassesCopy);
+    }
+
+    @Test
+    public void testAllIndexableXML_onlyValidBasePreferenceControllersAdded() {
+        Set<Integer> xmlSet = getIndexableXml();
+        xmlSet.addAll(whitelistXml);
+
+        List<String> xmlControllers = new ArrayList<>();
+        Set<String> invalidConstructors = new HashSet<>();
+        Set<String> invalidClassHierarchy = new HashSet<>();
+        Set<String> badClassNameControllers = new HashSet<>();
+        Set<String> badConstructorControllers = new HashSet<>();
+
+        for (int resId : xmlSet) {
+            xmlControllers.addAll(getXmlControllers(resId));
+        }
+
+        for (String controllerClassName : xmlControllers) {
+            Class<?> clazz = getClassFromClassName(controllerClassName);
+
+            if (clazz == null) {
+                badClassNameControllers.add(controllerClassName);
+                continue;
+            }
+
+            Constructor<?> constructor = getConstructorFromClass(clazz);
+
+            if (constructor == null) {
+                invalidConstructors.add(controllerClassName);
+                continue;
+            }
+
+            Object controller = getObjectFromConstructor(constructor);
+            if (controller == null) {
+                badConstructorControllers.add(controllerClassName);
+                continue;
+            }
+
+            if (!(controller instanceof BasePreferenceController)) {
+                invalidClassHierarchy.add(controllerClassName);
+            }
+        }
+
+        final String invalidConstructorError = buildErrorMessage(NO_VALID_CONSTRUCTOR_ERROR,
+                invalidConstructors);
+        final String invalidClassHierarchyError = buildErrorMessage(NOT_BASE_PREF_CONTROLLER_ERROR,
+                invalidClassHierarchy);
+        final String badClassNameError = buildErrorMessage(BAD_CLASSNAME_ERROR,
+                badClassNameControllers);
+        final String badConstructorError = buildErrorMessage(BAD_CONSTRUCTOR_ERROR,
+                badConstructorControllers);
+
+        assertWithMessage(invalidConstructorError).that(invalidConstructors).isEmpty();
+        assertWithMessage(invalidClassHierarchyError).that(invalidClassHierarchy).isEmpty();
+        assertWithMessage(badClassNameError).that(badClassNameControllers).isEmpty();
+        assertWithMessage(badConstructorError).that(badConstructorControllers).isEmpty();
+    }
+
+    private Set<Integer> getIndexableXml() {
+        Set<Integer> xmlResSet = new HashSet();
+
+        Collection<Class> indexableClasses = SearchIndexableResources.providerValues();
+        indexableClasses.removeAll(illegalClasses);
+
+        for (Class clazz : indexableClasses) {
+
+            Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
+                    clazz);
+
+            if (provider == null) {
+                continue;
+            }
+
+            List<SearchIndexableResource> resources = provider.getXmlResourcesToIndex(mContext,
+                    true);
+
+            if (resources == null) {
+                continue;
+            }
+
+            for (SearchIndexableResource resource : resources) {
+                // Add '0's anyway. It won't break the test.
+                xmlResSet.add(resource.xmlResId);
+            }
+        }
+        return xmlResSet;
+    }
+
+    private List<String> getXmlControllers(int xmlResId) {
+        List<String> xmlControllers = new ArrayList<>();
+
+        XmlResourceParser parser;
+        try {
+            parser = mContext.getResources().getXml(xmlResId);
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+                // Parse next until start tag is found
+            }
+
+            final int outerDepth = parser.getDepth();
+            final AttributeSet attrs = Xml.asAttributeSet(parser);
+            String controllerClassName;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+
+                controllerClassName = XmlParserUtils.getController(mContext, attrs);
+                // If controller is not indexed, then it is not compatible with
+                if (!TextUtils.isEmpty(controllerClassName)) {
+                    xmlControllers.add(controllerClassName);
+                }
+            }
+        } catch (Exception e) {
+            // Assume an issue with robolectric resources
+        }
+        return xmlControllers;
+    }
+
+    private String buildErrorMessage(String errorSummary, Set<String> errorClasses) {
+        final StringBuilder error = new StringBuilder(errorSummary);
+        for (String c : errorClasses) {
+            error.append(c).append("\n");
+        }
+        return error.toString();
+    }
+
+    private Class<?> getClassFromClassName(String className) {
+        Class<?> clazz = null;
+        try {
+            clazz = Class.forName(className);
+        } catch (ClassNotFoundException e) {
+        }
+        return clazz;
+    }
+
+    private Constructor<?> getConstructorFromClass(Class<?> clazz) {
+        Constructor<?> constructor = null;
+        try {
+            constructor = clazz.getConstructor(Context.class);
+        } catch (NoSuchMethodException e) {
+        }
+
+        if (constructor != null) {
+            return constructor;
+        }
+
+        try {
+            constructor = clazz.getConstructor(Context.class, String.class);
+        } catch (NoSuchMethodException e) {
+        }
+
+        return constructor;
+    }
+
+    private Object getObjectFromConstructor(Constructor<?> constructor) {
+        Object controller = null;
+
+        try {
+            controller = constructor.newInstance(mContext);
+        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
+                IllegalArgumentException e) {
+        }
+
+        if (controller != null) {
+            return controller;
+        }
+
+        try {
+            controller = constructor.newInstance(mContext, "key");
+        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
+                IllegalArgumentException e) {
+        }
+
+        return controller;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceControllerTest.java
new file mode 100644
index 0000000..95fd111
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceControllerTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.development;
+
+
+import static com.android.settings.development.EnableGnssRawMeasFullTrackingPreferenceController
+        .SETTING_VALUE_OFF;
+import static com.android.settings.development.EnableGnssRawMeasFullTrackingPreferenceController
+        .SETTING_VALUE_ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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 EnableGnssRawMeasFullTrackingPreferenceControllerTest {
+
+    @Mock
+    private SwitchPreference mPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private Context mContext;
+    private EnableGnssRawMeasFullTrackingPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = new EnableGnssRawMeasFullTrackingPreferenceController(mContext);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void onPreferenceChange_settingEnabled_enableGnssRawMeasFullTrackingShouldBeOn() {
+        mController.onPreferenceChange(mPreference, true /* new value */);
+
+        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, -1 /* default */);
+
+        assertThat(mode).isEqualTo(SETTING_VALUE_ON);
+    }
+
+    @Test
+    public void onPreferenceChange_settingDisabled_enableGnssRawMeasFullTrackingShouldBeOff() {
+        mController.onPreferenceChange(mPreference, false /* new value */);
+
+        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, -1 /* default */);
+
+        assertThat(mode).isEqualTo(SETTING_VALUE_OFF);
+    }
+
+    @Test
+    public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_OFF);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void updateState_settingEnabled_preferenceShouldBeChecked() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_ON);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(true);
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() {
+        mController.onDeveloperOptionsSwitchDisabled();
+
+        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, -1 /* default */);
+
+        assertThat(mode).isEqualTo(SETTING_VALUE_OFF);
+        verify(mPreference).setChecked(false);
+        verify(mPreference).setEnabled(false);
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchEnabled_shouldEnablePreference() {
+        mController.onDeveloperOptionsSwitchEnabled();
+
+        verify(mPreference).setEnabled(true);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java
index 05670e2..1fd5430 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java
@@ -57,7 +57,9 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mController = new SystemUpdatePreferenceController(mContext, mUserManager);
+
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        mController = new SystemUpdatePreferenceController(mContext);
         mPreference = new Preference(RuntimeEnvironment.application);
         mPreference.setKey(mController.getPreferenceKey());
         when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
@@ -82,7 +84,7 @@
 
         mController.updateNonIndexableKeys(keys);
 
-        assertThat(keys.size()).isEqualTo(1);
+        assertThat(keys).hasSize(1);
     }
 
     @Test
@@ -94,8 +96,8 @@
 
     @Test
     public void updateState_shouldSetToAndroidVersion() {
-        mController = new SystemUpdatePreferenceController(
-                RuntimeEnvironment.application, mUserManager);
+        mController = new SystemUpdatePreferenceController(RuntimeEnvironment.application);
+
         mController.updateState(mPreference);
 
         assertThat(mPreference.getSummary())
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java
index fd48162..2f896ac 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java
@@ -35,10 +35,13 @@
 import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController
         .SERVICE_STATE_VALUE_ID;
 import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController
+        .SIGNAL_STRENGTH_LABEL_ID;
+import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController
         .SIGNAL_STRENGTH_VALUE_ID;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -157,6 +160,9 @@
     @Test
     public void initialize_updateDataStateWithPowerOff_shouldUpdateSettingAndResetSignalStrength() {
         when(mServiceState.getState()).thenReturn(ServiceState.STATE_POWER_OFF);
+        when(mPersistableBundle.getBoolean(
+                CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL)).thenReturn(
+                true);
 
         mController.initialize();
 
@@ -172,6 +178,9 @@
         final int signalAsu = 50;
         doReturn(signalDbm).when(mController).getDbm(mSignalStrength);
         doReturn(signalAsu).when(mController).getAsuLevel(mSignalStrength);
+        when(mPersistableBundle.getBoolean(
+                CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL)).thenReturn(
+                true);
 
         mController.initialize();
 
@@ -226,6 +235,30 @@
     }
 
     @Test
+    public void initialize_doNotShowSignalStrength_shouldRemoveSignalStrengthSetting() {
+        when(mPersistableBundle.getBoolean(
+                CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL)).thenReturn(
+                false);
+
+        mController.initialize();
+
+        verify(mDialog).removeSettingFromScreen(SIGNAL_STRENGTH_LABEL_ID);
+        verify(mDialog).removeSettingFromScreen(SIGNAL_STRENGTH_VALUE_ID);
+    }
+
+    @Test
+    public void initialize_showSignalStrengthAndIccId_shouldShowSignalStrengthAndIccIdSetting() {
+        // getConfigForSubId is nullable, so make sure the default behavior is correct
+        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(null);
+
+        mController.initialize();
+
+        verify(mDialog).setText(eq(SIGNAL_STRENGTH_VALUE_ID), any());
+        verify(mDialog).removeSettingFromScreen(ICCID_INFO_LABEL_ID);
+        verify(mDialog).removeSettingFromScreen(ICCID_INFO_VALUE_ID);
+    }
+
+    @Test
     public void initialize_showIccid_shouldSetIccidToSetting() {
         final String iccid = "12351351231241";
         when(mPersistableBundle.getBoolean(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
index a163a43..c75dbf4 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.BatteryManager;
+import android.os.PowerManager;
 
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
@@ -83,6 +84,14 @@
     }
 
     @Test
+    public void testOnReceive_powerSaveModeChanged_listenerInvoked() {
+        mBatteryBroadcastReceiver.onReceive(mContext,
+                new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
+
+        verify(mBatteryListener).onBatteryChanged();
+    }
+
+    @Test
     public void testOnReceive_batteryDataNotChanged_listenerNotInvoked() {
         final String batteryLevel = Utils.getBatteryPercentage(mChargingIntent);
         final String batteryStatus = Utils.getBatteryStatus(mContext.getResources(),
diff --git a/tests/robotests/src/com/android/settings/location/LocationModeTest.java b/tests/robotests/src/com/android/settings/location/LocationModeTest.java
new file mode 100644
index 0000000..0e7a9d7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/location/LocationModeTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.settings.location;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.XmlTestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class LocationModeTest {
+
+    private Context mContext;
+    private LocationMode mFragment;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mFragment = new LocationMode();
+    }
+
+    @Test
+    public void testSearchIndexProvider_shouldIndexResource() {
+        final List<SearchIndexableResource> indexRes =
+                mFragment.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(mContext,
+                        true /* enabled */);
+
+        assertThat(indexRes).isNotNull();
+        assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId());
+    }
+
+    @Test
+    @Config(qualifiers = "mcc999")
+    public void testSearchIndexProvider_ifPageDisabled_shouldNotIndexResource() {
+        final List<String> niks = LocationMode.SEARCH_INDEX_DATA_PROVIDER
+                .getNonIndexableKeys(mContext);
+        final int xmlId = mFragment.getPreferenceScreenResId();
+
+        final List<String> keys = XmlTestUtils.getKeysFromPreferenceXml(mContext, xmlId);
+        assertThat(niks).containsAllIn(keys);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/location/RecentLocationRequestPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/RecentLocationRequestPreferenceControllerTest.java
index 5d7cca4..a1268d0 100644
--- a/tests/robotests/src/com/android/settings/location/RecentLocationRequestPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/location/RecentLocationRequestPreferenceControllerTest.java
@@ -41,7 +41,7 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.TestConfig;
 import com.android.settings.applications.InstalledAppDetails;
-import com.android.settings.applications.AppInfoDashboardFragment;
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
 import com.android.settings.core.FeatureFlags;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.widget.AppPreference;
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java
index fa2c6b9..80233d8 100644
--- a/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
 import android.app.NotificationManager;
@@ -53,7 +54,7 @@
     }
 
     @Test
-    public void testGetBehaviorSettingSummary_sameOrderAsTargetPage() {
+    public void testGetBehaviorSettingSummary_customBehavior() {
         NotificationManager.Policy policy = new NotificationManager.Policy(
                 NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS
                         | NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS
@@ -63,18 +64,31 @@
         final String result = mBuilder.getBehaviorSettingSummary(policy,
                 Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
-        String alarms = mContext.getString(R.string.zen_mode_alarms).toLowerCase();
-        String reminders = mContext.getString(R.string.zen_mode_reminders).toLowerCase();
-        String events = mContext.getString(R.string.zen_mode_events).toLowerCase();
-        String media = mContext.getString(R.string.zen_mode_media_system_other).toLowerCase();
+        String custom = mContext.getString(R.string.zen_mode_behavior_summary_custom);
+        assertEquals(custom, result);
+    }
 
-        assertThat(result).contains(alarms);
-        assertThat(result).contains(reminders);
-        assertThat(result).contains(events);
-        assertThat(result).contains(media);
-        assertTrue(result.indexOf(alarms) < result.indexOf(media)
-                && result.indexOf(media) < result.indexOf(reminders)
-                && result.indexOf(reminders) < result.indexOf(events));
+    @Test
+    public void testGetBehaviorSettingSummary_totalSilence() {
+        NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+        final String result = mBuilder.getBehaviorSettingSummary(policy,
+                Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        String totalSilence = mContext.getString(R.string.zen_mode_behavior_total_silence);
+        assertEquals(totalSilence, result);
+    }
+
+    @Test
+    public void testGetBehaviorSettingSummary_alarmsAndMedia() {
+        NotificationManager.Policy policy = new NotificationManager.Policy(
+                        NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
+                        | NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER,
+                0, 0);
+        final String result = mBuilder.getBehaviorSettingSummary(policy,
+                Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        String alarmsAndMedia = mContext.getString(R.string.zen_mode_behavior_alarms_only);
+        assertEquals(alarmsAndMedia, result);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java
index 20a05e3..8628678 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java
@@ -131,9 +131,7 @@
     public void testLaunchConfirmationActivity_internal_shouldPropagateTheme() {
         Intent intent = new Intent()
                 .putExtra(WizardManagerHelper.EXTRA_THEME, WizardManagerHelper.THEME_GLIF_V2);
-        Activity activity = Robolectric.buildActivity(Activity.class)
-                .withIntent(intent)
-                .get();
+        Activity activity = Robolectric.buildActivity(Activity.class, intent).get();
         ChooseLockSettingsHelper helper = getChooseLockSettingsHelper(activity);
         helper.launchConfirmationActivity(123, "test title", true, 0 /* userId */);
 
diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java
index a8372d9..f84f9a2 100644
--- a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java
+++ b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java
@@ -43,13 +43,13 @@
             "SettingsPreferenceFragment should implement Indexable, but these do not:\n";
     private static final String NOT_CONTAINING_PROVIDER_OBJECT_ERROR =
             "Indexable should have public field "
-                    + DatabaseIndexingManager.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER
+                    + DatabaseIndexingUtils.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER
                     + " but these are not:\n";
     private static final String NOT_SHARING_PREF_CONTROLLERS_BETWEEN_FRAG_AND_PROVIDER =
             "DashboardFragment should share pref controllers with its SearchIndexProvider, but "
                     + " these are not: \n";
     private static final String NOT_IN_INDEXABLE_PROVIDER_REGISTRY =
-            "Class containing " + DatabaseIndexingManager.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER
+            "Class containing " + DatabaseIndexingUtils.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER
                     + " must be added to " + SearchIndexableResources.class.getName()
                     + " but these are not: \n";
     private static final String NOT_PROVIDING_VALID_RESOURCE_ERROR =
@@ -173,7 +173,7 @@
     private boolean hasSearchIndexProvider(Class clazz) {
         try {
             final Field f = clazz.getField(
-                    DatabaseIndexingManager.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER);
+                    DatabaseIndexingUtils.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER);
             return f != null;
         } catch (NoClassDefFoundError e) {
             // Cannot find class def, ignore
diff --git a/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java
index 6050b32..2bec503 100644
--- a/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java
+++ b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java
@@ -129,6 +129,16 @@
     }
 
     @Test
+    @Config(qualifiers = "mcc999")
+    public void testControllerAttribute_returnsValidData() {
+        XmlResourceParser parser = getChildByType(R.xml.about_legal, "Preference");
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+        String controller = XmlParserUtils.getController(mContext, attrs);
+        assertThat(controller).isEqualTo("mind_flayer");
+    }
+
+    @Test
     public void testDataSummaryInvalid_ReturnsNull() {
         XmlResourceParser parser = getParentPrimedParser(R.xml.display_settings);
         final AttributeSet attrs = Xml.asAttributeSet(parser);
diff --git a/tests/robotests/src/com/android/settings/slices/SliceDataTest.java b/tests/robotests/src/com/android/settings/slices/SliceDataTest.java
new file mode 100644
index 0000000..0e4acca
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/slices/SliceDataTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.slices;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SliceDataTest {
+
+    private final String KEY = "KEY";
+    private final String TITLE = "title";
+    private final String SUMMARY = "summary";
+    private final String SCREEN_TITLE = "screen title";
+    private final String FRAGMENT_NAME = "fragment name";
+    private final int ICON = 1234; // I declare a thumb war
+    private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
+    private final String PREF_CONTROLLER = "com.android.settings.slices.tester";
+
+    @Test
+    public void testBuilder_buildsMatchingObject() {
+        SliceData.Builder builder = new SliceData.Builder()
+                .setKey(KEY)
+                .setTitle(TITLE)
+                .setSummary(SUMMARY)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIcon(ICON)
+                .setFragmentName(FRAGMENT_NAME)
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER);
+
+        SliceData data = builder.build();
+
+        assertThat(data.getKey()).isEqualTo(KEY);
+        assertThat(data.getTitle()).isEqualTo(TITLE);
+        assertThat(data.getSummary()).isEqualTo(SUMMARY);
+        assertThat(data.getScreenTitle()).isEqualTo(SCREEN_TITLE);
+        assertThat(data.getIconResource()).isEqualTo(ICON);
+        assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME);
+        assertThat(data.getUri()).isEqualTo(URI);
+        assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testBuilder_noKey_throwsIllegalStateException() {
+        new SliceData.Builder()
+                .setTitle(TITLE)
+                .setSummary(SUMMARY)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIcon(ICON)
+                .setFragmentName(FRAGMENT_NAME)
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER)
+                .build();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testBuilder_noTitle_throwsIllegalStateException() {
+        new SliceData.Builder()
+                .setKey(KEY)
+                .setSummary(SUMMARY)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIcon(ICON)
+                .setFragmentName(FRAGMENT_NAME)
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER)
+                .build();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testBuilder_noFragment_throwsIllegalStateException() {
+        new SliceData.Builder()
+                .setKey(KEY)
+                .setFragmentName(FRAGMENT_NAME)
+                .setSummary(SUMMARY)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIcon(ICON)
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER)
+                .build();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testBuilder_noUri_throwsIllegalStateException() {
+        new SliceData.Builder()
+                .setKey(KEY)
+                .setTitle(TITLE)
+                .setSummary(SUMMARY)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIcon(ICON)
+                .setFragmentName(FRAGMENT_NAME)
+                .setPreferenceControllerClassName(PREF_CONTROLLER)
+                .build();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testBuilder_noPrefController_throwsIllegalStateException() {
+        new SliceData.Builder()
+                .setKey(KEY)
+                .setTitle(TITLE)
+                .setSummary(SUMMARY)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIcon(ICON)
+                .setUri(URI)
+                .setFragmentName(FRAGMENT_NAME)
+                .build();
+    }
+
+    @Test
+    public void testBuilder_noSubtitle_buildsMatchingObject() {
+        SliceData.Builder builder = new SliceData.Builder()
+                .setKey(KEY)
+                .setTitle(TITLE)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIcon(ICON)
+                .setFragmentName(FRAGMENT_NAME)
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER);
+
+        SliceData data = builder.build();
+
+        assertThat(data.getKey()).isEqualTo(KEY);
+        assertThat(data.getTitle()).isEqualTo(TITLE);
+        assertThat(data.getSummary()).isNull();
+        assertThat(data.getScreenTitle()).isEqualTo(SCREEN_TITLE);
+        assertThat(data.getIconResource()).isEqualTo(ICON);
+        assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME);
+        assertThat(data.getUri()).isEqualTo(URI);
+        assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER);
+    }
+
+    @Test
+    public void testBuilder_noScreenTitle_buildsMatchingObject() {
+        SliceData.Builder builder = new SliceData.Builder()
+                .setKey(KEY)
+                .setTitle(TITLE)
+                .setSummary(SUMMARY)
+                .setIcon(ICON)
+                .setFragmentName(FRAGMENT_NAME)
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER);
+
+        SliceData data = builder.build();
+
+        assertThat(data.getKey()).isEqualTo(KEY);
+        assertThat(data.getTitle()).isEqualTo(TITLE);
+        assertThat(data.getSummary()).isEqualTo(SUMMARY);
+        assertThat(data.getScreenTitle()).isNull();
+        assertThat(data.getIconResource()).isEqualTo(ICON);
+        assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME);
+        assertThat(data.getUri()).isEqualTo(URI);
+        assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER);
+    }
+
+    @Test
+    public void testBuilder_noIcon_buildsMatchingObject() {
+        SliceData.Builder builder = new SliceData.Builder()
+                .setKey(KEY)
+                .setTitle(TITLE)
+                .setSummary(SUMMARY)
+                .setScreenTitle(SCREEN_TITLE)
+                .setFragmentName(FRAGMENT_NAME)
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER);
+
+        SliceData data = builder.build();
+
+        assertThat(data.getKey()).isEqualTo(KEY);
+        assertThat(data.getTitle()).isEqualTo(TITLE);
+        assertThat(data.getSummary()).isEqualTo(SUMMARY);
+        assertThat(data.getScreenTitle()).isEqualTo(SCREEN_TITLE);
+        assertThat(data.getIconResource()).isEqualTo(0);
+        assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME);
+        assertThat(data.getUri()).isEqualTo(URI);
+        assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER);
+    }
+
+    @Test
+    public void testEquality_identicalObjects() {
+        SliceData.Builder builder = new SliceData.Builder()
+                .setKey(KEY)
+                .setTitle(TITLE)
+                .setSummary(SUMMARY)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIcon(ICON)
+                .setFragmentName(FRAGMENT_NAME)
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER);
+
+        SliceData dataOne = builder.build();
+        SliceData dataTwo = builder.build();
+
+        assertThat(dataOne.hashCode()).isEqualTo(dataTwo.hashCode());
+        assertThat(dataOne).isEqualTo(dataTwo);
+    }
+
+    @Test
+    public void testEquality_matchingKey_EqualObjects() {
+        SliceData.Builder builder = new SliceData.Builder()
+                .setKey(KEY)
+                .setTitle(TITLE)
+                .setSummary(SUMMARY)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIcon(ICON)
+                .setFragmentName(FRAGMENT_NAME)
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER);
+
+        SliceData dataOne = builder.build();
+
+        builder.setTitle(TITLE + " diff")
+                .setSummary(SUMMARY + " diff")
+                .setScreenTitle(SCREEN_TITLE + " diff")
+                .setIcon(ICON + 1)
+                .setFragmentName(FRAGMENT_NAME + " diff")
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER + " diff");
+
+        SliceData dataTwo = builder.build();
+
+        assertThat(dataOne.hashCode()).isEqualTo(dataTwo.hashCode());
+        assertThat(dataOne).isEqualTo(dataTwo);
+    }
+
+    @Test
+    public void testEquality_differentKey_differentObjects() {
+        SliceData.Builder builder = new SliceData.Builder()
+                .setKey(KEY)
+                .setTitle(TITLE)
+                .setSummary(SUMMARY)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIcon(ICON)
+                .setFragmentName(FRAGMENT_NAME)
+                .setUri(URI)
+                .setPreferenceControllerClassName(PREF_CONTROLLER);
+
+        SliceData dataOne = builder.build();
+
+        builder.setKey("not key");
+        SliceData dataTwo = builder.build();
+
+        assertThat(dataOne.hashCode()).isNotEqualTo(dataTwo.hashCode());
+        assertThat(dataOne).isNotEqualTo(dataTwo);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java
new file mode 100644
index 0000000..a4020dd
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java
@@ -0,0 +1,87 @@
+package com.android.settings.slices;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.android.settings.TestConfig;
+import com.android.settings.slices.SlicesDatabaseHelper.IndexColumns;
+import com.android.settings.testutils.DatabaseTestUtils;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SlicesDatabaseHelperTest {
+
+    private Context mContext;
+    private SlicesDatabaseHelper mSlicesDatabaseHelper;
+    private SQLiteDatabase mDatabase;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mSlicesDatabaseHelper = new SlicesDatabaseHelper(mContext);
+        mDatabase = mSlicesDatabaseHelper.getWritableDatabase();
+    }
+
+    @After
+    public void cleanUp() {
+        DatabaseTestUtils.clearDb(mContext);
+    }
+
+    @Test
+    public void testDatabaseSchema() {
+        Cursor cursor = mDatabase.rawQuery("SELECT * FROM slices_index", null);
+        String[] columnNames = cursor.getColumnNames();
+
+        String[] expectedNames = new String[]{
+                IndexColumns.KEY,
+                IndexColumns.TITLE,
+                IndexColumns.SUBTITLE,
+                IndexColumns.SCREENTITLE,
+                IndexColumns.ICON_RESOURCE,
+                IndexColumns.FRAGMENT,
+                IndexColumns.CONTROLLER
+        };
+
+        assertThat(columnNames).isEqualTo(expectedNames);
+    }
+
+    @Test
+    public void testUpgrade_dropsOldData() {
+        ContentValues dummyValues = getDummyRow();
+
+        mDatabase.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, dummyValues);
+        Cursor baseline = mDatabase.rawQuery("SELECT * FROM slices_index", null);
+        assertThat(baseline.getCount()).isEqualTo(1);
+
+        mSlicesDatabaseHelper.onUpgrade(mDatabase, 0, 1);
+
+        Cursor newCursor = mDatabase.rawQuery("SELECT * FROM slices_index", null);
+        assertThat(newCursor.getCount()).isEqualTo(0);
+    }
+
+    private ContentValues getDummyRow() {
+        ContentValues values;
+
+        values = new ContentValues();
+        values.put(IndexColumns.KEY, "key");
+        values.put(IndexColumns.TITLE, "title");
+        values.put(IndexColumns.SUBTITLE, "subtitle");
+        values.put(IndexColumns.ICON_RESOURCE, 99);
+        values.put(IndexColumns.FRAGMENT, "fragmentClassName");
+        values.put(IndexColumns.CONTROLLER, "preferenceController");
+
+        return values;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java b/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java
index 2e8bac0..7c374e9 100644
--- a/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java
+++ b/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java
@@ -62,8 +62,8 @@
 
         // By adding any resources from libraries we need the AndroidManifest, we can access
         // them from within the parallel universe's resource loader.
-        final AndroidManifest manifest = new AndroidManifest(Fs.fileFromPath(manifestPath),
-                Fs.fileFromPath(resDir), Fs.fileFromPath(assetsDir)) {
+        return new AndroidManifest(Fs.fileFromPath(manifestPath), Fs.fileFromPath(resDir),
+            Fs.fileFromPath(assetsDir), "com.android.settings") {
             @Override
             public List<ResourcePath> getIncludedResourcePaths() {
                 List<ResourcePath> paths = super.getIncludedResourcePaths();
@@ -71,10 +71,6 @@
                 return paths;
             }
         };
-
-        // Set the package name to the renamed one
-        manifest.setPackageName("com.android.settings");
-        return manifest;
     }
 
     public static void getIncludedResourcePaths(String packageName, List<ResourcePath> paths) {
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index 65ed661..b22c01b 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -23,6 +23,7 @@
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
 
diff --git a/tests/unit/src/com/android/settings/applications/AppOpsSettingsTest.java b/tests/unit/src/com/android/settings/applications/AppOpsSettingsTest.java
new file mode 100644
index 0000000..d89d4a3
--- /dev/null
+++ b/tests/unit/src/com/android/settings/applications/AppOpsSettingsTest.java
@@ -0,0 +1,205 @@
+/*
+ * 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.app.AppOpsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.support.v7.widget.RecyclerView;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * An abstract parent for testing settings activities that manage an AppOps permission.
+ */
+abstract public class AppOpsSettingsTest {
+    private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard";
+    private static final long START_ACTIVITY_TIMEOUT = 5000;
+
+    private Context mContext;
+    private UiDevice mUiDevice;
+    private PackageManager mPackageManager;
+    private AppOpsManager mAppOpsManager;
+    private List<UserInfo> mProfiles;
+    private String mPackageName;
+
+    // These depend on which app op's settings UI is being tested.
+    private final String mActivityAction;
+    private final int mAppOpCode;
+
+    protected AppOpsSettingsTest(String activityAction, int appOpCode) {
+        mActivityAction = activityAction;
+        mAppOpCode = appOpCode;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mPackageName = InstrumentationRegistry.getContext().getPackageName();
+        mPackageManager = mContext.getPackageManager();
+        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+        mProfiles = mContext.getSystemService(UserManager.class).getProfiles(UserHandle.myUserId());
+        resetAppOpModeForAllProfiles();
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mUiDevice.wakeUp();
+        mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND);
+    }
+
+    private void resetAppOpModeForAllProfiles() throws Exception {
+        for (UserInfo user : mProfiles) {
+            final int uid = mPackageManager.getPackageUidAsUser(mPackageName, user.id);
+            mAppOpsManager.setMode(mAppOpCode, uid, mPackageName, MODE_DEFAULT);
+        }
+    }
+
+    /**
+     * Creates an intent for showing the permission settings for all apps.
+     */
+    private Intent createManageAllAppsIntent() {
+        return new Intent(mActivityAction);
+    }
+
+    /**
+     * Creates an intent for showing the permission setting for a single app.
+     */
+    private Intent createManageSingleAppIntent(String packageName) {
+        final Intent intent = createManageAllAppsIntent();
+        intent.setData(Uri.parse("package:" + packageName));
+        return intent;
+    }
+
+    private String getApplicationLabel(String packageName) throws Exception {
+        final ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
+        return mPackageManager.getApplicationLabel(info).toString();
+    }
+
+    private UiObject2 findAndVerifySwitchState(boolean checked) {
+        final BySelector switchSelector = By.clazz(Switch.class).res("android:id/switch_widget");
+        final UiObject2 switchPref = mUiDevice.wait(Until.findObject(switchSelector),
+                START_ACTIVITY_TIMEOUT);
+        assertNotNull("Switch not shown", switchPref);
+        assertTrue("Switch in invalid state", switchPref.isChecked() == checked);
+        return switchPref;
+    }
+
+    @Test
+    public void testAppList() throws Exception {
+        final String testAppLabel = getApplicationLabel(mPackageName);
+
+        mContext.startActivity(createManageAllAppsIntent());
+        final BySelector preferenceListSelector =
+                By.clazz(RecyclerView.class).res("com.android.settings:id/apps_list");
+        final UiObject2 preferenceList = mUiDevice.wait(Until.findObject(preferenceListSelector),
+                START_ACTIVITY_TIMEOUT);
+        assertNotNull("App list not shown", preferenceList);
+
+        final BySelector appLabelTextViewSelector = By.clazz(TextView.class)
+                .res("android:id/title")
+                .text(testAppLabel);
+        List<UiObject2> listOfMatchingTextViews;
+        do {
+            listOfMatchingTextViews = preferenceList.findObjects(appLabelTextViewSelector);
+            // assuming the number of profiles will be sufficiently small so that all the entries
+            // for the same package will fit in one screen at some time during the scroll.
+        } while (listOfMatchingTextViews.size() != mProfiles.size() &&
+                preferenceList.scroll(Direction.DOWN, 0.2f));
+        assertEquals("Test app not listed for each profile", mProfiles.size(),
+                listOfMatchingTextViews.size());
+
+        for (UiObject2 matchingObject : listOfMatchingTextViews) {
+            matchingObject.click();
+            findAndVerifySwitchState(true);
+            mUiDevice.pressBack();
+        }
+    }
+
+    private void testAppDetailScreenForAppOp(int appOpMode, int userId) throws Exception {
+        final String testAppLabel = getApplicationLabel(mPackageName);
+        final BySelector appDetailTitleSelector = By.clazz(TextView.class)
+                .res("com.android.settings:id/app_detail_title")
+                .text(testAppLabel);
+
+        mAppOpsManager.setMode(mAppOpCode,
+                mPackageManager.getPackageUidAsUser(mPackageName, userId), mPackageName, appOpMode);
+        mContext.startActivityAsUser(createManageSingleAppIntent(mPackageName),
+                UserHandle.of(userId));
+        mUiDevice.wait(Until.findObject(appDetailTitleSelector), START_ACTIVITY_TIMEOUT);
+        findAndVerifySwitchState(appOpMode == MODE_ALLOWED || appOpMode == MODE_DEFAULT);
+        mUiDevice.pressBack();
+    }
+
+    @Test
+    public void testSingleApp() throws Exception {
+        // App op MODE_DEFAULT is already tested in #testAppList
+        for (UserInfo user : mProfiles) {
+            testAppDetailScreenForAppOp(MODE_ALLOWED, user.id);
+            testAppDetailScreenForAppOp(MODE_ERRORED, user.id);
+        }
+    }
+
+    private void testSwitchToggle(int fromAppOp, int toAppOp) throws Exception {
+        final int packageUid = mPackageManager.getPackageUid(mPackageName, 0);
+        final boolean initialState = (fromAppOp == MODE_ALLOWED || fromAppOp == MODE_DEFAULT);
+
+        mAppOpsManager.setMode(mAppOpCode, packageUid, mPackageName, fromAppOp);
+        mContext.startActivity(createManageSingleAppIntent(mPackageName));
+        final UiObject2 switchPref = findAndVerifySwitchState(initialState);
+        switchPref.click();
+        Thread.sleep(1000);
+        assertEquals("Toggling switch did not change app op", toAppOp,
+                mAppOpsManager.checkOpNoThrow(mAppOpCode, packageUid,
+                        mPackageName));
+        mUiDevice.pressBack();
+    }
+
+    @Test
+    public void testIfSwitchTogglesAppOp() throws Exception {
+        testSwitchToggle(MODE_ALLOWED, MODE_ERRORED);
+        testSwitchToggle(MODE_ERRORED, MODE_ALLOWED);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mUiDevice.pressHome();
+        resetAppOpModeForAllProfiles();
+    }
+}
diff --git a/tests/unit/src/com/android/settings/applications/DrawOverlaySettingsTest.java b/tests/unit/src/com/android/settings/applications/DrawOverlaySettingsTest.java
new file mode 100644
index 0000000..24760ae
--- /dev/null
+++ b/tests/unit/src/com/android/settings/applications/DrawOverlaySettingsTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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.app.AppOpsManager;
+import android.provider.Settings;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class DrawOverlaySettingsTest extends AppOpsSettingsTest {
+
+    public DrawOverlaySettingsTest() {
+        super(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, AppOpsManager.OP_SYSTEM_ALERT_WINDOW);
+    }
+
+    // Test cases are in the superclass.
+}
\ No newline at end of file
diff --git a/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java b/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java
index f7e956b..6ac21af 100644
--- a/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java
+++ b/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java
@@ -16,187 +16,21 @@
 
 package com.android.settings.applications;
 
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_ERRORED;
-import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
 import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
-import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.Direction;
 
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.widget.ListView;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.List;
-
-@Suppress
 @RunWith(AndroidJUnit4.class)
 @LargeTest
-public class ExternalSourcesSettingsTest {
+public class ExternalSourcesSettingsTest extends AppOpsSettingsTest {
 
-    private static final String TAG = ExternalSourcesSettingsTest.class.getSimpleName();
-    private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard";
-    private static final long START_ACTIVITY_TIMEOUT = 5000;
-
-    private Context mContext;
-    private UiDevice mUiDevice;
-    private PackageManager mPackageManager;
-    private AppOpsManager mAppOpsManager;
-    private List<UserInfo> mProfiles;
-    private String mPackageName;
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mPackageName = InstrumentationRegistry.getContext().getPackageName();
-        mPackageManager = mContext.getPackageManager();
-        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
-        mProfiles = mContext.getSystemService(UserManager.class).getProfiles(UserHandle.myUserId());
-        resetAppOpModeForAllProfiles();
-        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        mUiDevice.wakeUp();
-        mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND);
+    public ExternalSourcesSettingsTest() {
+        super(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
+                AppOpsManager.OP_REQUEST_INSTALL_PACKAGES);
     }
 
-    private void resetAppOpModeForAllProfiles() throws Exception {
-        for (UserInfo user : mProfiles) {
-            final int uid = mPackageManager.getPackageUidAsUser(mPackageName, user.id);
-            mAppOpsManager.setMode(OP_REQUEST_INSTALL_PACKAGES, uid, mPackageName, MODE_DEFAULT);
-        }
-    }
-
-    private Intent createManageExternalSourcesListIntent() {
-        final Intent manageExternalSourcesIntent = new Intent();
-        manageExternalSourcesIntent.setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
-        return manageExternalSourcesIntent;
-    }
-
-    private Intent createManageExternalSourcesAppIntent(String packageName) {
-        final Intent intent = createManageExternalSourcesListIntent();
-        intent.setData(Uri.parse("package:" + packageName));
-        return intent;
-    }
-
-    private String getApplicationLabel(String packageName) throws Exception {
-        final ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
-        return mPackageManager.getApplicationLabel(info).toString();
-    }
-
-    private UiObject2 findAndVerifySwitchState(boolean checked) {
-        final BySelector switchSelector = By.clazz(Switch.class).res("android:id/switch_widget");
-        final UiObject2 switchPref = mUiDevice.wait(Until.findObject(switchSelector),
-                START_ACTIVITY_TIMEOUT);
-        assertNotNull("Switch not shown", switchPref);
-        assertTrue("Switch in invalid state", switchPref.isChecked() == checked);
-        return switchPref;
-    }
-
-    @Test
-    public void testManageExternalSourcesList() throws Exception {
-        final String testAppLabel = getApplicationLabel(mPackageName);
-
-        mContext.startActivity(createManageExternalSourcesListIntent());
-        final BySelector preferenceListSelector = By.clazz(ListView.class).res("android:id/list");
-        final UiObject2 preferenceList = mUiDevice.wait(Until.findObject(preferenceListSelector),
-                START_ACTIVITY_TIMEOUT);
-        assertNotNull("App list not shown", preferenceList);
-
-        final BySelector appLabelTextViewSelector = By.clazz(TextView.class)
-                .res("android:id/title")
-                .text(testAppLabel);
-        List<UiObject2> listOfMatchingTextViews;
-        do {
-            listOfMatchingTextViews = preferenceList.findObjects(appLabelTextViewSelector);
-            // assuming the number of profiles will be sufficiently small so that all the entries
-            // for the same package will fit in one screen at some time during the scroll.
-        } while (listOfMatchingTextViews.size() != mProfiles.size() &&
-                preferenceList.scroll(Direction.DOWN, 0.2f));
-        assertEquals("Test app not listed for each profile", mProfiles.size(),
-                listOfMatchingTextViews.size());
-
-        for (UiObject2 matchingObject : listOfMatchingTextViews) {
-            matchingObject.click();
-            findAndVerifySwitchState(true);
-            mUiDevice.pressBack();
-        }
-    }
-
-    private void testAppDetailScreenForAppOp(int appOpMode, int userId) throws Exception {
-        final String testAppLabel = getApplicationLabel(mPackageName);
-        final BySelector appDetailTitleSelector = By.clazz(TextView.class)
-                .res("com.android.settings:id/app_detail_title")
-                .text(testAppLabel);
-
-        mAppOpsManager.setMode(OP_REQUEST_INSTALL_PACKAGES,
-                mPackageManager.getPackageUidAsUser(mPackageName, userId), mPackageName, appOpMode);
-        mContext.startActivityAsUser(createManageExternalSourcesAppIntent(mPackageName),
-                UserHandle.of(userId));
-        mUiDevice.wait(Until.findObject(appDetailTitleSelector), START_ACTIVITY_TIMEOUT);
-        findAndVerifySwitchState(appOpMode == MODE_ALLOWED || appOpMode == MODE_DEFAULT);
-        mUiDevice.pressBack();
-    }
-
-    @Test
-    public void testManageExternalSourcesForApp() throws Exception {
-        // App op MODE_DEFAULT is already tested in #testManageExternalSourcesList
-        for (UserInfo user : mProfiles) {
-            testAppDetailScreenForAppOp(MODE_ALLOWED, user.id);
-            testAppDetailScreenForAppOp(MODE_ERRORED, user.id);
-        }
-    }
-
-    private void testSwitchToggle(int fromAppOp, int toAppOp) throws Exception {
-        final int packageUid = mPackageManager.getPackageUid(mPackageName, 0);
-        final boolean initialState = (fromAppOp == MODE_ALLOWED || fromAppOp == MODE_DEFAULT);
-
-        mAppOpsManager.setMode(OP_REQUEST_INSTALL_PACKAGES, packageUid, mPackageName, fromAppOp);
-        mContext.startActivity(createManageExternalSourcesAppIntent(mPackageName));
-        final UiObject2 switchPref = findAndVerifySwitchState(initialState);
-        switchPref.click();
-        Thread.sleep(1000);
-        assertEquals("Toggling switch did not change app op", toAppOp,
-                mAppOpsManager.checkOpNoThrow(OP_REQUEST_INSTALL_PACKAGES, packageUid,
-                        mPackageName));
-        mUiDevice.pressBack();
-    }
-
-    @Test
-    public void testIfSwitchTogglesAppOp() throws Exception {
-        testSwitchToggle(MODE_ALLOWED, MODE_ERRORED);
-        testSwitchToggle(MODE_ERRORED, MODE_ALLOWED);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mUiDevice.pressHome();
-        resetAppOpModeForAllProfiles();
-    }
+    // Test cases are in the superclass.
 }
diff --git a/tests/unit/src/com/android/settings/applications/PackageUtilTest.java b/tests/unit/src/com/android/settings/applications/PackageUtilTest.java
index 1c064ae..0e3c402 100644
--- a/tests/unit/src/com/android/settings/applications/PackageUtilTest.java
+++ b/tests/unit/src/com/android/settings/applications/PackageUtilTest.java
@@ -35,6 +35,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@Deprecated
 public class PackageUtilTest {
     private static final String ALL_USERS_APP_NAME = "com.google.allusers.app";
     private static final String ONE_USER_APP_NAME = "com.google.oneuser.app";
diff --git a/tests/unit/src/com/android/settings/applications/SpecialAppAccessSettingsTest.java b/tests/unit/src/com/android/settings/applications/SpecialAppAccessSettingsTest.java
index 4d92cf9..4165d06 100644
--- a/tests/unit/src/com/android/settings/applications/SpecialAppAccessSettingsTest.java
+++ b/tests/unit/src/com/android/settings/applications/SpecialAppAccessSettingsTest.java
@@ -20,6 +20,7 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
 import android.support.test.uiautomator.UiScrollable;
 import android.support.test.uiautomator.UiSelector;
 import android.test.InstrumentationTestCase;
@@ -79,9 +80,15 @@
         final String titleSpecialApps = mTargetContext.getResources().getString(
             R.string.special_access);
 
-        final UiScrollable settings = new UiScrollable(
-                new UiSelector().packageName(mTargetContext.getPackageName()).scrollable(true));
-        settings.scrollTextIntoView(titleSpecialApps);
+        try {
+            // scollbar may or may not be present, depending on how many recents app are there. If
+            // the page is scrollable, scroll to the bottom to show the special app access settings.
+            final UiScrollable settings = new UiScrollable(
+                    new UiSelector().packageName(mTargetContext.getPackageName()).scrollable(true));
+            settings.scrollTextIntoView(titleSpecialApps);
+        } catch (UiObjectNotFoundException e) {
+            // ignore
+        }
 
         mDevice.findObject(new UiSelector().text(titleSpecialApps)).click();
     }
diff --git a/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java b/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java
index bb12efa..2accbf2 100644
--- a/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java
+++ b/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java
@@ -36,9 +36,9 @@
 import java.util.Map;
 
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.compat.ArgumentMatcher;
 
 public class PreferenceListTest extends AndroidTestCase {
     private static final String TAG = "PreferenceListTest";
@@ -135,13 +135,10 @@
                 /* lockdownVpnKey */ null);
         updater.run();
 
-        final ArgumentMatcher<VpnProfile> equalsFake = new ArgumentMatcher<VpnProfile>() {
-            @Override
-            public boolean matchesObject(final Object arg) {
-                if (arg == vpnProfile) return true;
-                if (arg == null) return false;
-                return TextUtils.equals(((VpnProfile) arg).key, vpnProfile.key);
-            }
+        final ArgumentMatcher<VpnProfile> equalsFake = arg -> {
+            if (arg == vpnProfile) return true;
+            if (arg == null) return false;
+            return TextUtils.equals(arg.key, vpnProfile.key);
         };
 
         // The VPN profile should have been used to create a preference and set up at laest once