Merge "DisplayCutout: Add support for multiple cutout emulation options"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 51a811d..a36ec8b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -476,7 +476,7 @@
             </intent-filter>
         </activity>
 
-        <service android:name=".TetherService"
+        <service android:name=".wifi.tether.TetherService"
             android:exported="true"
             android:permission="android.permission.TETHER_PRIVILEGED" />
 
@@ -1004,6 +1004,34 @@
                 android:value="true" />
         </activity>
 
+
+        <activity android:name="Settings$MeCardActivity"
+                  android:label="@string/device_info_settings"
+                  android:icon="@drawable/ic_settings_about"
+                  android:taskAffinity="com.android.settings"
+                  android:parentActivityName="Settings">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.DEVICE_HEALTH_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE_LAUNCH" />
+            </intent-filter>
+            <intent-filter android:priority="10">
+                <action android:name="com.android.settings.action.SETTINGS" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.category"
+                       android:value="com.android.settings.category.ia.system" />
+            <meta-data android:name="com.android.settings.title"
+                       android:resource="@string/about_settings" />
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.MeCardFragment" />
+            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+                       android:value="true" />
+        </activity>
+
         <activity android:name="SettingsLicenseActivity"
                 android:label="@string/settings_license_activity_title"
                 android:theme="@android:style/Theme.DeviceDefault.Light.Panel"
@@ -3311,6 +3339,25 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="Settings$AdvancedConnectedDeviceActivity"
+                  android:label="@string/connected_device_connections_title"
+                  android:taskAffinity="com.android.settings"
+                  android:parentActivityName="Settings$ConnectedDeviceDashboardActivity"
+                  android:enabled="false">
+            <intent-filter android:priority="1">
+                <action android:name="com.android.settings.ADVANCED_CONNECTED_DEVICE_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment" />
+            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+                       android:value="true" />
+        </activity>
+
         <provider android:name=".slices.SettingsSliceProvider"
                   android:authorities="com.android.settings.slices"
                   android:exported="true">
diff --git a/res/layout/master_clear.xml b/res/layout/master_clear.xml
index 779e504..d328478 100644
--- a/res/layout/master_clear.xml
+++ b/res/layout/master_clear.xml
@@ -93,7 +93,7 @@
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_gravity="center_vertical"
-                        android:paddingEnd="8dp"
+                        android:paddingEnd="@dimen/reset_checkbox_padding_end"
                         android:focusable="false"
                         android:clickable="false"
                         android:duplicateParentState="true" />
@@ -104,17 +104,21 @@
                     <TextView
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:paddingTop="12dp"
-                        android:textSize="18sp"
+                        android:paddingTop="@dimen/reset_checkbox_title_padding_top"
+                        android:textSize="@dimen/reset_checkbox_title_text_size"
                         android:text="@string/erase_external_storage" />
                     <TextView
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:paddingTop="4sp"
-                        android:textSize="14sp"
+                        android:paddingTop="@dimen/reset_checkbox_summary_padding_top"
+                        android:textSize="@dimen/reset_checkbox_summary_text_size"
                         android:text="@string/erase_external_storage_description" />
                 </LinearLayout>
             </LinearLayout>
+            <include layout="@layout/reset_esim_checkbox"
+                 android:id="@+id/erase_esim_container"
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content" />
         </LinearLayout>
     </ScrollView>
     <Button
diff --git a/res/layout/reset_esim_checkbox.xml b/res/layout/reset_esim_checkbox.xml
new file mode 100644
index 0000000..d830bf4
--- /dev/null
+++ b/res/layout/reset_esim_checkbox.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:focusable="true"
+    android:clickable="true"
+    android:visibility="gone">
+
+    <CheckBox
+        android:id="@+id/erase_esim"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingEnd="@dimen/reset_checkbox_padding_end"
+        android:focusable="false"
+        android:clickable="false"
+        android:duplicateParentState="true" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/erase_esim_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="@dimen/reset_checkbox_title_padding_top"
+            android:textSize="@dimen/reset_checkbox_title_text_size" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="@dimen/reset_checkbox_summary_padding_top"
+            android:textSize="@dimen/reset_checkbox_summary_text_size"
+            android:text="@string/reset_esim_desc" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/reset_network.xml b/res/layout/reset_network.xml
index be966dd..1850bb2 100644
--- a/res/layout/reset_network.xml
+++ b/res/layout/reset_network.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical" >
@@ -27,7 +28,8 @@
         android:layout_marginTop="12dp"
         android:layout_weight="1">
 
-        <LinearLayout android:layout_width="match_parent"
+        <LinearLayout
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical">
 
@@ -38,6 +40,11 @@
                 android:textDirection="locale"
                 android:text="@string/reset_network_desc" />
 
+            <include layout="@layout/reset_esim_checkbox"
+                android:id="@+id/erase_esim_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
         </LinearLayout>
 
     </ScrollView>
diff --git a/res/values/config.xml b/res/values/config.xml
index 097350b..ce61164 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -44,6 +44,10 @@
     <!-- Whether to show Camera laser sensor switch in Developer Options -->
     <bool name="config_show_camera_laser_sensor">false</bool>
 
+    <!-- Whether to show Connected MAC Randomization in Developer Options
+         as not all devices can support dynamic MAC address change.  -->
+    <bool name="config_wifi_support_connected_mac_randomization">false</bool>
+
     <!-- Fully-qualified class name for the implementation of the FeatureFactory to be instantiated. -->
     <string name="config_featureFactory" translatable="false">com.android.settings.overlay.FeatureFactoryImpl</string>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 339eaf2..d4071ed 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -311,4 +311,11 @@
     <dimen name="suggestion_card_title_padding_bottom_one_card">6dp</dimen>
     <dimen name="suggestion_card_title_padding_bottom_multiple_cards">8dp</dimen>
 
+    <!-- Padding for the reset screens -->
+    <dimen name="reset_checkbox_padding_end">8dp</dimen>
+    <dimen name="reset_checkbox_title_padding_top">12dp</dimen>
+    <dimen name="reset_checkbox_summary_padding_top">4dp</dimen>
+    <dimen name="reset_checkbox_title_text_size">18sp</dimen>
+    <dimen name="reset_checkbox_summary_text_size">14sp</dimen>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4193947..eea6ddd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3213,6 +3213,10 @@
     <string name="reset_network_title">Reset Wi-Fi, mobile &amp; Bluetooth</string>
     <!-- SD card & phone storage settings screen, message on screen after user selects Reset network settings [CHAR LIMIT=NONE] -->
     <string name="reset_network_desc">This will reset all network settings, including:\n\n<li>Wi\u2011Fi</li>\n<li>Mobile data</li>\n<li>Bluetooth</li>"</string>
+    <!-- SD card & phone storage settings screen, title for the checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=NONE] -->
+    <string name="reset_esim_title">Also reset eSIMs</string>
+    <!-- SD card & phone storage settings screen, message for the checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=NONE] -->
+    <string name="reset_esim_desc">Erase all eSIMs on the phone. You\u2019ll have to contract your carrier to redownload your eSIMs. This will not cancel your mobile service plan.</string>
     <!-- SD card & phone storage settings screen, button on screen after user selects Reset network settings -->
     <string name="reset_network_button_text">Reset settings</string>
     <!-- SD card & phone storage settings screen, message on screen after user selects Reset settings button -->
@@ -3225,6 +3229,10 @@
     <string name="network_reset_not_available">Network reset is not available for this user</string>
     <!-- Reset settings complete toast text [CHAR LIMIT=75] -->
     <string name="reset_network_complete_toast">Network settings have been reset</string>
+    <!-- Title of the error message shown when error happens during erase eSIM data [CHAR LIMIT=NONE] -->
+    <string name="reset_esim_error_title">Cant\u2019t reset eSIMs</string>
+    <!-- Message of the error message shown when error happens during erase eSIM data [CHAR LIMIT=NONE] -->
+    <string name="reset_esim_error_msg">The eSIMs can\u2019tt be reset due to an error.</string>
 
     <!-- Master Clear -->
     <!-- Button title to factory data reset the entire device -->
@@ -3240,7 +3248,7 @@
     <!-- SD card & phone storage settings screen, list of items in user data storage (USB storage or SD card) that will be erased during this operation [CHAR LIMIT=NONE] -->
     <string name="master_clear_desc_also_erases_external">"<li>Music</li>\n<li>Photos</li>\n<li>Other user data</li>"</string>
     <!-- SD card & phone storage settings screen, list of items on an eSIM (embedded SIM) that will be erased during this operation [CHAR LIMIT=NONE] -->
-    <string name="master_clear_desc_also_erases_esim">"<li>Carriers on eSIM</li>"</string>
+    <string name="master_clear_desc_also_erases_esim">"<li>eSIMs</li>"</string>
     <!-- SD card & phone storage settings screen, notification if there are eSIM (embedded SIM) profiles present that the user's mobile service plan will not be canceled [CHAR LIMIT=NONE] -->
     <string name="master_clear_desc_no_cancel_mobile_plan">"\n\nThis will not cancel your mobile service plan.</string>
     <!-- SD card & phone storage settings screen, instructions about whether to also erase the external storage (SD card) when erasing the internal storage [CHAR LIMIT=NONE] -->
@@ -4711,7 +4719,7 @@
     <string name="power_charge_remaining"><xliff:g id="until_charged">%1$s</xliff:g> to charge</string>
 
     <!-- Title for the background activity setting, which allows a user to control whether an app can run in the background [CHAR_LIMIT=40] -->
-    <string name="background_activity_title">Background activity</string>
+    <string name="background_activity_title">Restricted</string>
     <!-- Summary for the background activity [CHAR_LIMIT=120] -->
     <string name="background_activity_summary">Allow the app to run in the background</string>
     <!-- Summary for the background activity when it is on [CHAR_LIMIT=120] -->
@@ -6957,6 +6965,9 @@
     <!-- Configure Notifications Settings title. [CHAR LIMIT=30] -->
     <string name="configure_notification_settings">Notifications</string>
 
+    <!-- notification header - apps that have recently sent notifications -->
+    <string name="recent_notifications">Recently sent</string>
+
     <!-- Configure Notifications: Advanced section header [CHAR LIMIT=30] -->
     <string name="advanced_section_header">Advanced</string>
 
@@ -7594,6 +7605,11 @@
     <!-- Button label to say no to the question of whether to require PIN/password/pattern to start your device. [CHAR LIMIT=20] -->
     <string name="encryption_interstitial_no">No</string>
 
+    <!-- Label to say yes to the question of whether app is restricted. [CHAR LIMIT=20] -->
+    <string name="restricted_true_label">Yes</string>
+    <!-- Label to say no to the question of whether app is restricted. [CHAR LIMIT=20] -->
+    <string name="restricted_false_label">No</string>
+
     <!-- Title for encryption dialog that disables TalkBack. [CHAR_LIMIT=25] -->
     <string name="encrypt_talkback_dialog_require_pin">Require PIN?</string>
 
@@ -8327,6 +8343,8 @@
     <string name="disabled_by_policy_title_camera">Camera not allowed</string>
     <!-- Title for dialog displayed to tell user that screenshots are disabled by an admin [CHAR LIMIT=50] -->
     <string name="disabled_by_policy_title_screen_capture">Screenshot not allowed</string>
+    <!-- Title for dialog displayed to tell user that turning off backups is disallowed by an admin [CHAR LIMIT=50] -->
+    <string name="disabled_by_policy_title_turn_off_backups">Can\'t turn off backups</string>
     <!-- Shown when the user tries to change a settings locked by an admin [CHAR LIMIT=200] -->
     <string name="default_admin_support_msg">This action is disabled. To learn more, contact your
         organization\'s admin.</string>
@@ -9211,4 +9229,15 @@
          [DO NOT TRANSLATE] -->
     <string name="account_confirmation_package"></string>
 
+    <!-- Title for the new About Phone screen [CHAR LIMIT=40] -->
+    <string name="me_card_title" product="default">My Phone</string>
+    <!-- Title for the new About Phone screen [CHAR LIMIT=40] -->
+    <string name="me_card_title" product="tablet">My Tablet</string>
+    <!-- Title for the new About Phone screen [CHAR LIMIT=40] -->
+    <string name="me_card_title" product="device">My Device</string>
+    <!-- Title for preference showing the primary account on the device [CHAR LIMIT=60]-->
+    <string name="me_card_account_preference_title">Account</string>
+    <!-- Title for preference showing the name of the device. [CHAR LIMIT=60]-->
+    <string name="me_card_device_name_preference_title">Device name</string>
+
 </resources>
diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml
index 21904e6..520ebaa 100644
--- a/res/xml/configure_notification_settings.xml
+++ b/res/xml/configure_notification_settings.xml
@@ -19,8 +19,28 @@
                   android:key="configure_notification_settings">
 
     <PreferenceCategory
-        android:key="dashboard_tile_placeholder"
-        android:order="1"/>
+        android:key="recent_notifications_category"
+        android:title="@string/recent_notifications"
+        android:order="-200">
+        <!-- Placeholder for a list of recent apps -->
+
+        <!-- See all apps button -->
+        <Preference
+            android:title="@string/notifications_title"
+            android:key="all_notifications"
+            android:order="20">
+            <intent
+                android:action="android.intent.action.MAIN"
+                android:targetPackage="com.android.settings"
+                android:targetClass="com.android.settings.Settings$NotificationAppListActivity">
+            </intent>
+        </Preference>
+    </PreferenceCategory>
+
+    <!-- Empty category to draw divider -->
+    <PreferenceCategory
+        android:key="all_notifications_divider"
+        android:order="-190"/>
 
     <!-- When device is locked -->
     <com.android.settings.notification.RestrictedDropDownPreference
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 9377fa0..04644f5 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -203,6 +203,11 @@
             android:summary="@string/wifi_verbose_logging_summary"/>
 
         <SwitchPreference
+            android:key="wifi_connected_mac_randomization"
+            android:title="@string/wifi_connected_mac_randomization"
+            android:summary="@string/wifi_connected_mac_randomization_summary"/>
+
+        <SwitchPreference
             android:key="mobile_data_always_on"
             android:title="@string/mobile_data_always_on"
             android:summary="@string/mobile_data_always_on_summary"/>
diff --git a/res/xml/directory_access_details.xml b/res/xml/directory_access_details.xml
new file mode 100644
index 0000000..4448ba6
--- /dev/null
+++ b/res/xml/directory_access_details.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:settings="http://schemas.android.com/apk/res-auto"
+        android:key="directory_access_details"
+        android:title="@string/directory_access"/>
diff --git a/res/xml/me_card.xml b/res/xml/me_card.xml
new file mode 100644
index 0000000..2d8c21f
--- /dev/null
+++ b/res/xml/me_card.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:key="me_card_pref_screen"
+    android:title="@string/me_card_title">
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="me_card_header"
+        android:order="0"
+        android:layout="@layout/settings_entity_header"
+        android:selectable="false"/>
+
+    <!-- Account name -->
+    <Preference
+        android:key="account"
+        android:order="1"
+        android:title="@string/me_card_account_preference_title"
+        android:summary="@string/summary_placeholder"/>
+
+    <!-- Phone number -->
+    <Preference
+        android:key="phone_number"
+        android:order="2"
+        android:title="@string/status_number"
+        android:summary="@string/summary_placeholder"/>
+
+    <!-- Device name -->
+    <Preference
+        android:key="device_name"
+        android:order="3"
+        android:title="@string/me_card_device_name_preference_title"
+        android:summary="@string/summary_placeholder"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/power_usage_detail.xml b/res/xml/power_usage_detail.xml
index acd6367..acc62b6 100644
--- a/res/xml/power_usage_detail.xml
+++ b/res/xml/power_usage_detail.xml
@@ -27,6 +27,22 @@
         android:key="action_buttons"
         android:order="-9999"/>
 
+    <PreferenceCategory
+        android:title="@string/battery_detail_manage_title">
+
+        <Preference
+            android:key="background_activity"
+            android:title="@string/background_activity_title"
+            android:selectable="true"/>
+
+        <Preference
+            android:key="battery_optimization"
+            android:title="@string/high_power_apps"
+            android:summary="@string/high_power_off"
+            android:selectable="true"/>
+
+    </PreferenceCategory>
+
     <Preference
         android:key="high_usage"
         android:icon="@drawable/ic_battery_alert_24dp"
@@ -52,20 +68,4 @@
 
     </PreferenceCategory>
 
-    <PreferenceCategory
-        android:title="@string/battery_detail_manage_title">
-
-        <SwitchPreference
-            android:key="background_activity"
-            android:title="@string/background_activity_title"
-            android:selectable="true"/>
-
-        <Preference
-            android:key="battery_optimization"
-            android:title="@string/high_power_apps"
-            android:summary="@string/high_power_off"
-            android:selectable="true"/>
-
-    </PreferenceCategory>
-
 </PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/restricted_apps_detail.xml b/res/xml/restricted_apps_detail.xml
new file mode 100644
index 0000000..1e08a7e
--- /dev/null
+++ b/res/xml/restricted_apps_detail.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:title="@string/restricted_app_title">
+
+    <PreferenceCategory
+        android:key="restrict_app_list"/>
+
+</PreferenceScreen>
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index 0843b79..7205eaf 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -111,7 +111,6 @@
             android:value="com.android.settings.Settings$VrListenersSettingsActivity" />
     </Preference>
 
-<!-- TODO(b/63720392): add when ready
     <Preference
         android:key="special_app_directory_access"
         android:title="@string/directory_access"
@@ -119,8 +118,7 @@
         settings:keywords="@string/keywords_directory_access">
         <extra
             android:name="classname"
-            android:value="com.android.settings.Settings$DirectoryeAccessSettingsActivity" />
+            android:value="com.android.settings.Settings$DirectoryAccessSettingsActivity" />
     </Preference>
- -->
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/AirplaneModeEnabler.java b/src/com/android/settings/AirplaneModeEnabler.java
index 5f93589..4fc205d 100644
--- a/src/com/android/settings/AirplaneModeEnabler.java
+++ b/src/com/android/settings/AirplaneModeEnabler.java
@@ -30,8 +30,8 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.PhoneStateIntentReceiver;
 import com.android.internal.telephony.TelephonyProperties;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.WirelessUtils;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListener {
 
diff --git a/src/com/android/settings/DeviceAdminSettings.java b/src/com/android/settings/DeviceAdminSettings.java
index bb53018..9391439 100644
--- a/src/com/android/settings/DeviceAdminSettings.java
+++ b/src/com/android/settings/DeviceAdminSettings.java
@@ -49,9 +49,8 @@
 import android.widget.TextView;
 
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
+import com.android.settings.core.instrumentation.Instrumentable;
+import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -64,7 +63,8 @@
 public class DeviceAdminSettings extends ListFragment implements Instrumentable {
     static final String TAG = "DeviceAdminSettings";
 
-    private VisibilityLoggerMixin mVisibilityLoggerMixin;
+    private final VisibilityLoggerMixin mVisibilityLoggerMixin =
+            new VisibilityLoggerMixin(getMetricsCategory());
     private DevicePolicyManager mDPM;
     private UserManager mUm;
 
@@ -85,6 +85,12 @@
         }
     }
 
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mVisibilityLoggerMixin.onAttach(context);
+    }
+
     /**
      * Internal collection of device admin info objects for all profiles associated with the current
      * user.
@@ -115,8 +121,6 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(),
-                FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider());
     }
 
     @Override
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 3cc722b..0e337f6 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -76,7 +76,10 @@
     private static final String TAG = "MasterClear";
 
     private static final int KEYGUARD_REQUEST = 55;
-    private static final int CREDENTIAL_CONFIRM_REQUEST = 56;
+    @VisibleForTesting static final int CREDENTIAL_CONFIRM_REQUEST = 56;
+
+    private static final String KEY_SHOW_ESIM_RESET_CHECKBOX
+            = "masterclear.allow_retain_esim_profiles_after_fdr";
 
     static final String ERASE_EXTERNAL_EXTRA = "erase_sd";
     static final String ERASE_ESIMS_EXTRA = "erase_esim";
@@ -85,6 +88,8 @@
     private Button mInitiateButton;
     private View mExternalStorageContainer;
     @VisibleForTesting CheckBox mExternalStorage;
+    private View mEsimStorageContainer;
+    @VisibleForTesting CheckBox mEsimStorage;
     private ScrollView mScrollView;
 
     private final OnGlobalLayoutListener mOnGlobalLayoutListener = new OnGlobalLayoutListener() {
@@ -134,8 +139,7 @@
     void showFinalConfirmation() {
         Bundle args = new Bundle();
         args.putBoolean(ERASE_EXTERNAL_EXTRA, mExternalStorage.isChecked());
-        // TODO: Offer the user a choice to wipe eSIMs when it is technically feasible to do so.
-        args.putBoolean(ERASE_ESIMS_EXTRA, true);
+        args.putBoolean(ERASE_ESIMS_EXTRA, mEsimStorage.isChecked());
         ((SettingsActivity) getActivity()).startPreferencePanel(
                 this, MasterClearConfirm.class.getName(),
                 args, R.string.master_clear_confirm_title, null, null, 0);
@@ -157,9 +161,12 @@
                 .setAction("android.accounts.action.PRE_FACTORY_RESET");
             // Check to make sure that the intent is supported.
             final PackageManager pm = context.getPackageManager();
-            final List<ResolveInfo> resolutions =
-                pm.queryIntentActivities(requestAccountConfirmation, 0);
-            if (resolutions != null && resolutions.size() > 0) {
+            final ResolveInfo resolution = pm.resolveActivity(requestAccountConfirmation, 0);
+            if (resolution != null
+                    && resolution.activityInfo != null
+                    && packageName.equals(resolution.activityInfo.packageName)) {
+                // Note that we need to check the packagename to make sure that an Activity resolver
+                // wasn't returned.
                 getActivity().startActivityForResult(
                     requestAccountConfirmation, CREDENTIAL_CONFIRM_REQUEST);
                 return true;
@@ -214,6 +221,8 @@
         mInitiateButton.setOnClickListener(mInitiateListener);
         mExternalStorageContainer = mContentView.findViewById(R.id.erase_external_container);
         mExternalStorage = (CheckBox) mContentView.findViewById(R.id.erase_external);
+        mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container);
+        mEsimStorage = (CheckBox) mContentView.findViewById(R.id.erase_esim);
         mScrollView = (ScrollView) mContentView.findViewById(R.id.master_clear_scrollview);
 
         /*
@@ -248,11 +257,25 @@
         }
 
         if (showWipeEuicc()) {
-            final View esimAlsoErased = mContentView.findViewById(R.id.also_erases_esim);
-            esimAlsoErased.setVisibility(View.VISIBLE);
+            if (showWipeEuiccCheckbox()) {
+                TextView title = mContentView.findViewById(R.id.erase_esim_title);
+                title.setText(R.string.erase_esim_storage);
+                mEsimStorageContainer.setVisibility(View.VISIBLE);
+                mEsimStorageContainer.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        mEsimStorage.toggle();
+                    }
+                });
+            } else {
+                final View esimAlsoErased = mContentView.findViewById(R.id.also_erases_esim);
+                esimAlsoErased.setVisibility(View.VISIBLE);
 
-            final View noCancelMobilePlan = mContentView.findViewById(R.id.no_cancel_mobile_plan);
-            noCancelMobilePlan.setVisibility(View.VISIBLE);
+                final View noCancelMobilePlan = mContentView.findViewById(
+                        R.id.no_cancel_mobile_plan);
+                noCancelMobilePlan.setVisibility(View.VISIBLE);
+                mEsimStorage.setChecked(true /* checked */);
+            }
         }
 
         final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
@@ -294,6 +317,12 @@
     }
 
     @VisibleForTesting
+    boolean showWipeEuiccCheckbox() {
+        return SystemProperties
+                .getBoolean(KEY_SHOW_ESIM_RESET_CHECKBOX, false /* def */);
+    }
+
+    @VisibleForTesting
     protected boolean isEuiccEnabled(Context context) {
         EuiccManager euiccManager = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
         return euiccManager.isEnabled();
diff --git a/src/com/android/settings/MeCardFragment.java b/src/com/android/settings/MeCardFragment.java
new file mode 100644
index 0000000..9790fd5
--- /dev/null
+++ b/src/com/android/settings/MeCardFragment.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.provider.SearchIndexableResource;
+import android.view.View;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.deviceinfo.BrandedAccountPreferenceController;
+import com.android.settings.deviceinfo.PhoneNumberPreferenceController;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class MeCardFragment extends DashboardFragment implements Indexable {
+    private static final String LOG_TAG = "MeCardFragment";
+
+    private static final String KEY_ME_CARD_HEADER = "me_card_header";
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.DEVICEINFO;
+    }
+
+    @Override
+    public int getHelpResource() {
+        return R.string.help_uri_about;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        initHeader();
+    }
+
+    @Override
+    protected String getLogTag() {
+        return LOG_TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.me_card;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return buildPreferenceControllers(context, getActivity(), this /* fragment */,
+                getLifecycle());
+    }
+
+    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
+            Activity activity, Fragment fragment, Lifecycle lifecycle) {
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new PhoneNumberPreferenceController(context));
+        controllers.add(new BrandedAccountPreferenceController(context));
+        // TODO: Add preference controller for getting the device name.
+        return controllers;
+    }
+
+    private void initHeader() {
+        // TODO: Migrate into its own controller.
+        final LayoutPreference headerPreference =
+                (LayoutPreference) getPreferenceScreen().findPreference(KEY_ME_CARD_HEADER);
+        final View appSnippet = headerPreference.findViewById(R.id.entity_header);
+        final Activity context = getActivity();
+        final Bundle bundle = getArguments();
+        EntityHeaderController controller = EntityHeaderController
+                .newInstance(context, this, appSnippet)
+                .setRecyclerView(getListView(), getLifecycle())
+                .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
+                        EntityHeaderController.ActionType.ACTION_NONE);
+
+        // TODO: There may be an avatar setting action we can use here.
+        final int iconId = bundle.getInt("icon_id", 0);
+        if (iconId == 0) {
+            UserManager userManager = (UserManager) getActivity().getSystemService(
+                    Context.USER_SERVICE);
+            UserInfo info = Utils.getExistingUser(userManager, android.os.Process.myUserHandle());
+            controller.setLabel(info.name);
+            controller.setIcon(
+                    com.android.settingslib.Utils.getUserIcon(getActivity(), userManager, info));
+        }
+
+        controller.done(context, true /* rebindActions */);
+    }
+
+    /**
+     * For Search.
+     */
+    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(
+                        Context context, boolean enabled) {
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = R.xml.me_card;
+                    return Arrays.asList(sir);
+                }
+
+                @Override
+                public List<AbstractPreferenceController> getPreferenceControllers(
+                        Context context) {
+                    return buildPreferenceControllers(context, null /*activity */,
+                            null /* fragment */, null /* lifecycle */);
+                }
+            };
+}
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index 0f08c26..f64f6dc 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -18,20 +18,28 @@
 
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.euicc.EuiccManager;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
+import android.widget.CheckBox;
 import android.widget.Spinner;
+import android.widget.TextView;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.PhoneConstants;
@@ -64,6 +72,8 @@
     private View mContentView;
     private Spinner mSubscriptionSpinner;
     private Button mInitiateButton;
+    private View mEsimContainer;
+    private CheckBox mEsimCheckbox;
 
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -107,6 +117,7 @@
             SubscriptionInfo subscription = mSubscriptions.get(selectedIndex);
             args.putInt(PhoneConstants.SUBSCRIPTION_KEY, subscription.getSubscriptionId());
         }
+        args.putBoolean(MasterClear.ERASE_ESIMS_EXTRA, mEsimCheckbox.isChecked());
         ((SettingsActivity) getActivity()).startPreferencePanel(
                 this, ResetNetworkConfirm.class.getName(),
                 args, R.string.reset_network_confirm_title, null, null, 0);
@@ -141,6 +152,8 @@
      */
     private void establishInitialState() {
         mSubscriptionSpinner = (Spinner) mContentView.findViewById(R.id.reset_network_subscription);
+        mEsimContainer = mContentView.findViewById(R.id.erase_esim_container);
+        mEsimCheckbox = mContentView.findViewById(R.id.erase_esim);
 
         mSubscriptions = SubscriptionManager.from(getActivity()).getActiveSubscriptionInfoList();
         if (mSubscriptions != null && mSubscriptions.size() > 0) {
@@ -192,6 +205,30 @@
         }
         mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_reset_network);
         mInitiateButton.setOnClickListener(mInitiateListener);
+        if (showEuiccSettings(getContext())) {
+            mEsimContainer.setVisibility(View.VISIBLE);
+            TextView title = mContentView.findViewById(R.id.erase_esim_title);
+            title.setText(R.string.reset_esim_title);
+            mEsimContainer.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mEsimCheckbox.toggle();
+                }
+            });
+        } else {
+            mEsimCheckbox.setChecked(false /* checked */);
+        }
+    }
+
+    private boolean showEuiccSettings(Context context) {
+        EuiccManager euiccManager =
+                (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
+        if (!euiccManager.isEnabled()) {
+            return false;
+        }
+        ContentResolver resolver = context.getContentResolver();
+        return Settings.Global.getInt(resolver, Global.EUICC_PROVISIONED, 0) != 0
+                || Settings.Global.getInt(resolver, Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
     }
 
     @Override
diff --git a/src/com/android/settings/ResetNetworkConfirm.java b/src/com/android/settings/ResetNetworkConfirm.java
index 58b8289..bc0fa77 100644
--- a/src/com/android/settings/ResetNetworkConfirm.java
+++ b/src/com/android/settings/ResetNetworkConfirm.java
@@ -16,6 +16,7 @@
 
 package com.android.settings;
 
+import android.app.AlertDialog;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothManager;
 import android.content.ContentResolver;
@@ -24,9 +25,12 @@
 import android.net.NetworkPolicyManager;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.RecoverySystem;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.view.LayoutInflater;
@@ -39,6 +43,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.wrapper.RecoverySystemWrapper;
 import com.android.settingslib.RestrictedLockUtils;
 
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -57,6 +62,42 @@
 
     private View mContentView;
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    @VisibleForTesting boolean mEraseEsim;
+    @VisibleForTesting EraseEsimAsyncTask mEraseEsimTask;
+    @VisibleForTesting static RecoverySystemWrapper mRecoverySystem;
+
+    /**
+     * Async task used to erase all the eSIM profiles from the phone. If error happens during
+     * erasing eSIM profiles or timeout, an error msg is shown.
+     */
+    private static class EraseEsimAsyncTask extends AsyncTask<Void, Void, Boolean> {
+        private final Context mContext;
+        private final String mPackageName;
+
+        EraseEsimAsyncTask(Context context, String packageName) {
+            mContext = context;
+            mPackageName = packageName;
+        }
+
+        @Override
+        protected Boolean doInBackground(Void... params) {
+            return mRecoverySystem.wipeEuiccData(mContext, mPackageName);
+        }
+
+        @Override
+        protected void onPostExecute(Boolean succeeded) {
+            if (succeeded) {
+                Toast.makeText(mContext, R.string.reset_network_complete_toast, Toast.LENGTH_SHORT)
+                        .show();
+            } else {
+                new AlertDialog.Builder(mContext)
+                        .setTitle(R.string.reset_esim_error_title)
+                        .setMessage(R.string.reset_esim_error_msg)
+                        .setPositiveButton(android.R.string.ok, null /* listener */)
+                        .show();
+            }
+        }
+    }
 
     /**
      * The user has gone through the multiple confirmation, so now we go ahead
@@ -69,7 +110,8 @@
             if (Utils.isMonkeyRunning()) {
                 return;
             }
-            // TODO maybe show a progress dialog if this ends up taking a while
+            // TODO maybe show a progress screen if this ends up taking a while and won't let user
+            // go back until the tasks finished.
             Context context = getActivity();
 
             ConnectivityManager connectivityManager = (ConnectivityManager)
@@ -108,11 +150,20 @@
 
             ImsManager.factoryReset(context);
             restoreDefaultApn(context);
+            esimFactoryReset(context, context.getPackageName());
+        }
+    };
 
+    @VisibleForTesting
+    void esimFactoryReset(Context context, String packageName) {
+        if (mEraseEsim) {
+            mEraseEsimTask = new EraseEsimAsyncTask(context, packageName);
+            mEraseEsimTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        } else {
             Toast.makeText(context, R.string.reset_network_complete_toast, Toast.LENGTH_SHORT)
                     .show();
         }
-    };
+    }
 
     /**
      * Restore APN settings to default.
@@ -163,6 +214,16 @@
         if (args != null) {
             mSubId = args.getInt(PhoneConstants.SUBSCRIPTION_KEY,
                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            mEraseEsim = args.getBoolean(MasterClear.ERASE_ESIMS_EXTRA);
+        }
+        mRecoverySystem = new RecoverySystemWrapper();
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mEraseEsimTask != null) {
+            mEraseEsimTask.cancel(true /* mayInterruptIfRunning */);
+            mEraseEsimTask = null;
         }
     }
 
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 5e815bc..4503b64 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -55,6 +55,7 @@
     public static class NightDisplaySettingsActivity extends SettingsActivity { /* empty */ }
     public static class NightDisplaySuggestionActivity extends NightDisplaySettingsActivity { /* empty */ }
     public static class DeviceInfoSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class MeCardActivity extends SettingsActivity { /* empty */ }
     public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ }
     public static class ManageAssistActivity extends SettingsActivity { /* empty */ }
@@ -178,5 +179,10 @@
     public static class StorageDashboardActivity extends SettingsActivity {}
     public static class UserAndAccountDashboardActivity extends SettingsActivity {}
     public static class SystemDashboardActivity extends SettingsActivity {}
+    public static class AdvancedConnectedDeviceActivity extends SettingsActivity {
+        public static final boolean isEnabled() {
+            return FeatureFlagUtils.isEnabled(null /* context */, CONNECTED_DEVICE_V2);
+        }
+    }
 
 }
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 5cb7c06..3ac268a 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -56,13 +56,13 @@
 import com.android.settings.applications.manageapplications.ManageApplications;
 import com.android.settings.backup.BackupSettingsActivity;
 import com.android.settings.core.gateway.SettingsGateway;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.core.instrumentation.SharedPreferencesLogger;
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.DashboardSummary;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.wfd.WifiDisplaySettings;
 import com.android.settings.widget.SwitchBar;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.instrumentation.SharedPreferencesLogger;
 import com.android.settingslib.development.DevelopmentSettingsEnabler;
 import com.android.settingslib.drawer.DashboardCategory;
 import com.android.settingslib.drawer.SettingsDrawerActivity;
@@ -93,6 +93,11 @@
     public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
 
     /**
+     * The metrics category constant for logging source when a setting fragment is opened.
+     */
+    public static final String EXTRA_SOURCE_METRICS_CATEGORY = ":settings:source_metrics";
+
+    /**
      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
      * this extra can also be specified to supply a Bundle of arguments to pass
      * to that fragment when it is instantiated during the initial creation
@@ -215,8 +220,7 @@
     @Override
     public SharedPreferences getSharedPreferences(String name, int mode) {
         if (name.equals(getPackageName() + "_preferences")) {
-            return new SharedPreferencesLogger(this, getMetricsTag(),
-                    FeatureFactory.getFactory(this).getMetricsFeatureProvider());
+            return new SharedPreferencesLogger(this, getMetricsTag());
         }
         return super.getSharedPreferences(name, mode);
     }
@@ -873,6 +877,19 @@
                 WifiDisplaySettings.isAvailable(this), isAdmin)
                 || somethingChanged;
 
+        // Enable/disable the Me Card page.
+        final boolean isMeCardEnabled = featureFactory
+                .getAccountFeatureProvider()
+                .isMeCardEnabled(this);
+        somethingChanged = setTileEnabled(new ComponentName(packageName,
+                        Settings.MeCardActivity.class.getName()),
+                isMeCardEnabled, isAdmin)
+                || somethingChanged;
+        somethingChanged = setTileEnabled(new ComponentName(packageName,
+                        Settings.DeviceInfoSettingsActivity.class.getName()),
+                !isMeCardEnabled, isAdmin)
+                || somethingChanged;
+
         if (UserHandle.MU_ENABLED && !isAdmin) {
 
             // When on restricted users, disable all extra categories (but only the settings ones).
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index c5d477a..d9e264b 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -45,6 +45,7 @@
 
 import com.android.settings.applications.LayoutPreference;
 import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.instrumentation.Instrumentable;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settings.search.actionbar.SearchMenuController;
 import com.android.settings.support.actionbar.HelpMenuController;
@@ -52,7 +53,6 @@
 import com.android.settings.widget.LoadingViewController;
 import com.android.settingslib.CustomDialogPreference;
 import com.android.settingslib.CustomEditTextPreference;
-import com.android.settingslib.core.instrumentation.Instrumentable;
 import com.android.settingslib.widget.FooterPreferenceMixin;
 
 import java.util.UUID;
diff --git a/src/com/android/settings/ShowAdminSupportDetailsDialog.java b/src/com/android/settings/ShowAdminSupportDetailsDialog.java
index c1cd6f5..321f93d 100644
--- a/src/com/android/settings/ShowAdminSupportDetailsDialog.java
+++ b/src/com/android/settings/ShowAdminSupportDetailsDialog.java
@@ -150,6 +150,9 @@
             case DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE:
                 titleView.setText(R.string.disabled_by_policy_title_screen_capture);
                 break;
+            case DevicePolicyManager.POLICY_MANDATORY_BACKUPS:
+                titleView.setText(R.string.disabled_by_policy_title_turn_off_backups);
+                break;
             default:
                 // Use general text if no specialized title applies
                 titleView.setText(R.string.disabled_by_policy_title);
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 1c674b6..ae10ffe 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -110,7 +110,6 @@
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settings.wrapper.DevicePolicyManagerWrapper;
 import com.android.settings.wrapper.FingerprintManagerWrapper;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 
 import java.net.InetAddress;
 import java.util.ArrayList;
@@ -515,7 +514,8 @@
             Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
             CharSequence title, boolean isShortcut, int metricsCategory) {
         startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
-                titleResPackageName, titleResId, title, isShortcut, metricsCategory, 0);
+                titleResPackageName, titleResId, title, isShortcut, metricsCategory,
+                Intent.FLAG_ACTIVITY_NEW_TASK);
     }
 
 
@@ -577,7 +577,7 @@
         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
-        intent.putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY, sourceMetricsCategory);
+        intent.putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, sourceMetricsCategory);
         return intent;
     }
 
diff --git a/src/com/android/settings/accounts/AccountFeatureProvider.java b/src/com/android/settings/accounts/AccountFeatureProvider.java
new file mode 100644
index 0000000..bbfc48a
--- /dev/null
+++ b/src/com/android/settings/accounts/AccountFeatureProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accounts;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.util.FeatureFlagUtils;
+
+import com.android.settings.core.FeatureFlags;
+
+public interface AccountFeatureProvider {
+    String getAccountType();
+    Account[] getAccounts(Context context);
+    /**
+     * Checks whether or not to display the new About Phone page.
+     */
+    default boolean isMeCardEnabled(Context context) {
+        return FeatureFlagUtils.isEnabled(context, FeatureFlags.ABOUT_PHONE_V2);
+    }
+}
diff --git a/src/com/android/settings/accounts/AccountFeatureProviderImpl.java b/src/com/android/settings/accounts/AccountFeatureProviderImpl.java
new file mode 100644
index 0000000..90b581b
--- /dev/null
+++ b/src/com/android/settings/accounts/AccountFeatureProviderImpl.java
@@ -0,0 +1,16 @@
+package com.android.settings.accounts;
+
+import android.accounts.Account;
+import android.content.Context;
+
+public class AccountFeatureProviderImpl implements AccountFeatureProvider {
+    @Override
+    public String getAccountType() {
+        return null;
+    }
+
+    @Override
+    public Account[] getAccounts(Context context) {
+        return new Account[0];
+    }
+}
diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java
index 6127ab9..c0bf7d2 100644
--- a/src/com/android/settings/accounts/AccountPreferenceController.java
+++ b/src/com/android/settings/accounts/AccountPreferenceController.java
@@ -51,12 +51,12 @@
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.SearchIndexableRaw;
 import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.accounts.AuthenticatorHelper;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
 import com.android.settingslib.core.lifecycle.events.OnResume;
diff --git a/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java b/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java
index 8cd4444..1c2a0af 100644
--- a/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java
+++ b/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java
@@ -33,11 +33,15 @@
 
 import java.util.Set;
 
-// TODO(b/63720392): add unit tests
+// TODO(b/72055774): add unit tests
 public class AppStateDirectoryAccessBridge extends AppStateBaseBridge {
 
     private static final String TAG = "DirectoryAccessBridge";
 
+    // TODO(b/72055774): set to false once feature is ready (or use Log.isLoggable)
+    static final boolean DEBUG = true;
+    static final boolean VERBOSE = true;
+
     public AppStateDirectoryAccessBridge(ApplicationsState appState, Callback callback) {
         super(appState, callback);
     }
@@ -59,27 +63,34 @@
 
         @Override
         public void init(Context context) {
-            try (Cursor cursor = context.getContentResolver().query(
-                    new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
-                            .authority(AUTHORITY).appendPath(TABLE_PACKAGES).appendPath("*")
-                            .build(), TABLE_PACKAGES_COLUMNS, null, null)) {
+            mPackages = null;
+            final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                    .authority(AUTHORITY).appendPath(TABLE_PACKAGES).appendPath("*")
+                    .build();
+            try (Cursor cursor = context.getContentResolver().query(providerUri,
+                    TABLE_PACKAGES_COLUMNS, null, null)) {
                 if (cursor == null) {
-                    Log.w(TAG, "didn't get cursor");
+                    Log.w(TAG, "Didn't get cursor for " + providerUri);
                     return;
                 }
                 final int count = cursor.getCount();
                 if (count == 0) {
-                    Log.d(TAG, "no packages");
+                    if (DEBUG) {
+                        Log.d(TAG, "No packages anymore (was " + mPackages + ")");
+                    }
                     return;
                 }
                 mPackages = new ArraySet<>(count);
                 while (cursor.moveToNext()) {
                     mPackages.add(cursor.getString(TABLE_PACKAGES_COL_PACKAGE));
                 }
-                Log.v(TAG, "init(): " + mPackages);
+                if (DEBUG) {
+                    Log.d(TAG, "init(): " + mPackages);
+                }
             }
         }
 
+
         @Override
         public boolean filterApp(AppEntry info) {
             return mPackages != null && mPackages.contains(info.info.packageName);
diff --git a/src/com/android/settings/applications/DirectoryAccessDetails.java b/src/com/android/settings/applications/DirectoryAccessDetails.java
index 1f7a81a..43422d0 100644
--- a/src/com/android/settings/applications/DirectoryAccessDetails.java
+++ b/src/com/android/settings/applications/DirectoryAccessDetails.java
@@ -17,84 +17,236 @@
 package com.android.settings.applications;
 
 import static android.os.storage.StorageVolume.ScopedAccessProviderContract.AUTHORITY;
+import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_DIRECTORY;
+import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_GRANTED;
+import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_PACKAGE;
+import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_VOLUME_UUID;
 import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS;
 import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COLUMNS;
 import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_DIRECTORY;
-import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_GRANTED;
 import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_PACKAGE;
 import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_VOLUME_UUID;
 
+import static com.android.settings.applications.AppStateDirectoryAccessBridge.DEBUG;
+import static com.android.settings.applications.AppStateDirectoryAccessBridge.VERBOSE;
+
+import android.annotation.Nullable;
+import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
 import android.support.v7.preference.Preference.OnPreferenceClickListener;
+import android.text.TextUtils;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IconDrawableFactory;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settings.widget.EntityHeaderController.ActionType;
+import com.android.settingslib.applications.AppUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Detailed settings for an app's directory access permissions (A.K.A Scoped Directory Access).
+ *
+ * <p>Currently, it shows the entry for which the user denied access with the "Do not ask again"
+ * flag checked on: the user than can use the settings toggle to reset that deniel.
+ *
+ * <p>This fragments dynamically lists all such permissions, starting with one preference per
+ * directory in the primary storage, then adding additional entries for the external volumes (one
+ * entry for the whole volume).
  */
-// TODO(b/63720392): explain its layout
-// TODO(b/63720392): add unit tests
-public class DirectoryAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
-        OnPreferenceClickListener {
-    private static final String MY_TAG = "DirectoryAccessDetails";
+// TODO(b/72055774): add unit tests
+public class DirectoryAccessDetails extends AppInfoBase {
+
+    @SuppressWarnings("hiding")
+    private static final String TAG = "DirectoryAccessDetails";
+
+    private boolean mCreated;
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        if (mCreated) {
+            Log.w(TAG, "onActivityCreated(): ignoring duplicate call");
+            return;
+        }
+        mCreated = true;
+        if (mPackageInfo == null) {
+            Log.w(TAG, "onActivityCreated(): no package info");
+            return;
+        }
+        final Activity activity = getActivity();
+        final Preference pref = EntityHeaderController
+                .newInstance(activity, this, /* header= */ null )
+                .setRecyclerView(getListView(), getLifecycle())
+                .setIcon(IconDrawableFactory.newInstance(getPrefContext())
+                        .getBadgedIcon(mPackageInfo.applicationInfo))
+                .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm))
+                .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
+                .setPackageName(mPackageName)
+                .setUid(mPackageInfo.applicationInfo.uid)
+                .setHasAppInfoLink(false)
+                .setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE)
+                .done(activity, getPrefContext());
+        getPreferenceScreen().addPreference(pref);
+    }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        if (true) {
-            // TODO(b/63720392): temporarily hack so the screen doesn't crash..
-            addPreferencesFromResource(R.xml.app_ops_permissions_details);
-            // ... we need to dynamically create the preferences by calling the provider instead:
-            try (Cursor cursor = getContentResolver().query(
-                    new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
-                            .authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*")
-                            .build(),
-                    TABLE_PERMISSIONS_COLUMNS, null, null)) {
-                if (cursor == null) {
-                    Log.w(TAG, "didn't get cursor");
-                    return;
+        final Context context = getPrefContext();
+        addPreferencesFromResource(R.xml.directory_access_details);
+        final PreferenceScreen prefsGroup = getPreferenceScreen();
+
+        // Set external directory UUIDs.
+        ArraySet<String> externalDirectoryUuids = null;
+
+        final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*")
+                .build();
+        // Query provider for entries.
+        try (Cursor cursor = context.getContentResolver().query(providerUri,
+                TABLE_PERMISSIONS_COLUMNS, null, new String[] { mPackageName }, null)) {
+            if (cursor == null) {
+                Log.w(TAG, "Didn't get cursor for " + mPackageName);
+                return;
+            }
+            final int count = cursor.getCount();
+            if (count == 0) {
+                if (DEBUG) {
+                    Log.d(TAG, "No permissions for " + mPackageName);
                 }
-                final int count = cursor.getCount();
-                if (count == 0) {
-                    Log.d(TAG, "no permissions");
-                    return;
+                // TODO(b/72055774): display empty message
+                return;
+            }
+
+            while (cursor.moveToNext()) {
+                final String pkg = cursor.getString(TABLE_PERMISSIONS_COL_PACKAGE);
+                final String uuid = cursor.getString(TABLE_PERMISSIONS_COL_VOLUME_UUID);
+                final String dir = cursor.getString(TABLE_PERMISSIONS_COL_DIRECTORY);
+                if (VERBOSE) {
+                    Log.v(TAG, "Pkg:"  + pkg + " uuid: " + uuid + " dir: " + dir);
                 }
-                while (cursor.moveToNext()) {
-                    final String pkg = cursor.getString(TABLE_PERMISSIONS_COL_PACKAGE);
-                    final String uuid = cursor.getString(TABLE_PERMISSIONS_COL_VOLUME_UUID);
-                    final String dir = cursor.getString(TABLE_PERMISSIONS_COL_DIRECTORY);
-                    final boolean granted = cursor.getInt(TABLE_PERMISSIONS_COL_GRANTED) == 1;
-                    Log.v(MY_TAG, "pkg:" + pkg + " uuid: " + uuid + " dir: " + dir + "> "
-                            + granted);
+
+                if (!mPackageName.equals(pkg)) {
+                    // Sanity check, shouldn't happen
+                    Log.w(TAG, "Ignoring " + uuid + "/" + dir + " due to package mismatch: "
+                            + "expected " + mPackageName + ", got " + pkg);
+                    continue;
+                }
+
+                if (uuid == null) {
+                    // Primary storage entry: add right away
+                    prefsGroup.addPreference(
+                            newPreference(context, dir, providerUri, /* uuid= */ null, dir));
+                } else {
+                    // External volume entry: save it for later.
+                    if (externalDirectoryUuids == null) {
+                        externalDirectoryUuids = new ArraySet<>(1);
+                    }
+                    externalDirectoryUuids.add(uuid);
                 }
             }
         }
+
+        // Add entries from external volumes
+        if (externalDirectoryUuids != null) {
+            if (VERBOSE) {
+                Log.v(TAG, "adding external directories: " + externalDirectoryUuids);
+            }
+
+            // Query StorageManager to get the user-friendly volume names.
+            final StorageManager sm = context.getSystemService(StorageManager.class);
+            final List<VolumeInfo> volumes = sm.getVolumes();
+            if (volumes.isEmpty()) {
+                Log.w(TAG, "StorageManager returned no secondary volumes");
+                return;
+            }
+            final Map<String, String> volumeNames = new HashMap<>(volumes.size());
+            for (VolumeInfo volume : volumes) {
+                final String uuid = volume.getFsUuid();
+                if (uuid == null) continue; // Primary storage; not used.
+
+                String name = sm.getBestVolumeDescription(volume);
+                if (name == null) {
+                    Log.w(TAG, "No description for " + volume + "; using uuid instead: " + uuid);
+                    name = uuid;
+                }
+                volumeNames.put(uuid, name);
+            }
+            if (VERBOSE) {
+                Log.v(TAG, "UUID -> name mapping: " + volumeNames);
+            }
+
+            externalDirectoryUuids.forEach((uuid) ->{
+                final String name = volumeNames.get(uuid);
+                // TODO(b/72055774): add separator
+                prefsGroup.addPreference(
+                        newPreference(context, name, providerUri, uuid, /* dir= */ null));
+            });
+        }
+        return;
+    }
+
+
+    private SwitchPreference newPreference(Context context, String title, Uri providerUri,
+            String uuid, String dir) {
+        final SwitchPreference pref = new SwitchPreference(context);
+        pref.setKey(String.format("%s:%s", uuid, dir));
+        pref.setTitle(title);
+        pref.setChecked(false);
+        pref.setOnPreferenceChangeListener((unused, value) -> {
+            resetDoNotAskAgain(context, value, providerUri, uuid, dir);
+            return true;
+        });
+        return pref;
+    }
+
+    private void resetDoNotAskAgain(Context context, Object value, Uri providerUri,
+            @Nullable String uuid, @Nullable String directory) {
+        if (!Boolean.class.isInstance(value)) {
+            // Sanity check
+            Log.wtf(TAG, "Invalid value from switch: " + value);
+            return;
+        }
+        final boolean newValue = ((Boolean) value).booleanValue();
+        if (DEBUG) {
+            Log.d(TAG, "Asking " + providerUri  + " to update " + uuid + "/" + directory + " to "
+                    + newValue);
+        }
+        final ContentValues values = new ContentValues(1);
+        values.put(COL_GRANTED, newValue);
+        final int updated = context.getContentResolver().update(providerUri, values,
+                null, new String[] { mPackageName, uuid, directory });
+        if (DEBUG) {
+            Log.d(TAG, "Updated " + updated + " entries for " + uuid + "/" + directory);
+        }
     }
 
     @Override
-    public boolean onPreferenceClick(Preference preference) {
-        // TODO(b/63720392): implement or remove listener
-        return false;
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        // TODO(b/63720392): implement or remove listener
-        return false;
-    }
-
-    @Override
     protected boolean refreshUi() {
-        // TODO(b/63720392): implement
         return true;
     }
 
diff --git a/src/com/android/settings/applications/UsageAccessDetails.java b/src/com/android/settings/applications/UsageAccessDetails.java
index c172137..c10fb55 100644
--- a/src/com/android/settings/applications/UsageAccessDetails.java
+++ b/src/com/android/settings/applications/UsageAccessDetails.java
@@ -37,8 +37,8 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.applications.AppStateUsageBridge.UsageState;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
         OnPreferenceClickListener {
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 3fd7ced..a0ce733 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -33,10 +33,10 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.GearPreference;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
 
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java b/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java
index cea0147..a12d1a8 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java
@@ -23,9 +23,9 @@
 import android.text.TextUtils;
 
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 public class BluetoothDeviceRenamePreferenceController extends
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
index 127730b..2862083 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
@@ -27,14 +27,19 @@
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
 import com.android.settings.widget.GearPreference;
+import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HeadsetProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
+
+import android.util.Log;
 
 /**
  * Update the bluetooth devices. It gets bluetooth event from {@link LocalBluetoothManager} using
@@ -45,6 +50,7 @@
  * whether the {@link CachedBluetoothDevice} is relevant.
  */
 public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
+    private static final String TAG = "BluetoothDeviceUpdater";
     private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
             "persist.bluetooth.showdeviceswithoutnames";
 
@@ -55,6 +61,7 @@
 
     private final boolean mShowDeviceWithoutNames;
     private DashboardFragment mFragment;
+    private Preference.OnPreferenceClickListener mDevicePreferenceClickListener = null;
 
     @VisibleForTesting
     final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> {
@@ -73,6 +80,38 @@
 
     };
 
+    private class PreferenceClickListener implements
+        Preference.OnPreferenceClickListener {
+        @Override
+        public boolean onPreferenceClick(Preference preference) {
+            final CachedBluetoothDevice device =
+                ((BluetoothDevicePreference) preference).getBluetoothDevice();
+            if (device == null) {
+                return false;
+            }
+
+            // Set the device as active per profile only if the device supports that profile
+            // TODO: The active device selector location might change in the future
+            Log.i(TAG, "OnPreferenceClickListener: device=" + device);
+            boolean result = false;
+            A2dpProfile a2dpProfile = mLocalManager.getProfileManager().getA2dpProfile();
+            if ((a2dpProfile != null) && device.isConnectedProfile(a2dpProfile)) {
+                if (a2dpProfile.setActiveDevice(device.getDevice())) {
+                    Log.i(TAG, "OnPreferenceClickListener: A2DP active device=" + device);
+                    result = true;
+                }
+            }
+            HeadsetProfile headsetProfile = mLocalManager.getProfileManager().getHeadsetProfile();
+            if ((headsetProfile != null) && device.isConnectedProfile(headsetProfile)) {
+                if (headsetProfile.setActiveDevice(device.getDevice())) {
+                    Log.i(TAG, "OnPreferenceClickListener: Headset active device=" + device);
+                    result = true;
+                }
+            }
+            return result;
+        }
+    }
+
     public BluetoothDeviceUpdater(DashboardFragment fragment,
             DevicePreferenceCallback devicePreferenceCallback) {
         this(fragment, devicePreferenceCallback, Utils.getLocalBtManager(fragment.getContext()));
@@ -87,6 +126,7 @@
                 BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
         mPreferenceMap = new HashMap<>();
         mLocalManager = localManager;
+        mDevicePreferenceClickListener = new PreferenceClickListener();
     }
 
     /**
@@ -141,6 +181,18 @@
     @Override
     public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {}
 
+    @Override
+    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+        Collection<CachedBluetoothDevice> cachedDevices =
+                mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
+        // TODO: The state update of the Cached Bluetooth Devices should be
+        // moved to the device manager: b/72316092
+        for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) {
+            boolean isActive = Objects.equals(cachedBluetoothDevice, activeDevice);
+            cachedBluetoothDevice.setActiveDevice(isActive, bluetoothProfile);
+        }
+    }
+
     /**
      * Set the context to generate the {@link Preference}, so it could get the correct theme.
      */
@@ -176,6 +228,7 @@
                     new BluetoothDevicePreference(mPrefContext, cachedDevice,
                             mShowDeviceWithoutNames);
             btPreference.setOnGearClickListener(mDeviceProfilesListener);
+            btPreference.setOnPreferenceClickListener(mDevicePreferenceClickListener);
             mPreferenceMap.put(device, btPreference);
             mDevicePreferenceCallback.onDeviceAdded(btPreference);
         }
diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java
index 0f294bd..87fa43d 100644
--- a/src/com/android/settings/bluetooth/BluetoothEnabler.java
+++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java
@@ -27,12 +27,12 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.widget.SwitchWidgetController;
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.WirelessUtils;
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * BluetoothEnabler is a helper to manage the Bluetooth on/off checkbox
diff --git a/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
index 1ecfed4..450c7b2 100644
--- a/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
@@ -23,9 +23,9 @@
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Controller that shows received files
diff --git a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
index 662cd70..43d25e7 100644
--- a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
@@ -77,6 +77,10 @@
     }
 
     @Override
+    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+    }
+
+    @Override
     public void register(boolean listening) {
         if (mBluetoothAdapter == null) {
             return;
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
index 0a90edc..207a4b0 100644
--- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
+++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
@@ -277,6 +277,9 @@
 
     public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
 
+    @Override
+    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { }
+
     /**
      * Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
      */
diff --git a/src/com/android/settings/core/FeatureFlags.java b/src/com/android/settings/core/FeatureFlags.java
index 4b5ce78..4b8ccd1 100644
--- a/src/com/android/settings/core/FeatureFlags.java
+++ b/src/com/android/settings/core/FeatureFlags.java
@@ -27,4 +27,5 @@
     public static final String SECURITY_SETTINGS_V2 = "settings_security_settings_v2";
     public static final String ZONE_PICKER_V2 = "settings_zone_picker_v2";
     public static final String SUGGESTION_UI_V2 = "settings_suggestion_ui_v2";
+    public static final String ABOUT_PHONE_V2 = "settings_about_phone_v2";
 }
diff --git a/src/com/android/settings/core/InstrumentedActivity.java b/src/com/android/settings/core/InstrumentedActivity.java
index 294de2c..9b24756 100644
--- a/src/com/android/settings/core/InstrumentedActivity.java
+++ b/src/com/android/settings/core/InstrumentedActivity.java
@@ -16,9 +16,8 @@
 
 package com.android.settings.core;
 
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
+import com.android.settings.core.instrumentation.Instrumentable;
+import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settingslib.core.lifecycle.ObservableActivity;
 
 /**
@@ -28,8 +27,7 @@
 
     public InstrumentedActivity() {
         // Mixin that logs visibility change for activity.
-        getLifecycle().addObserver(new VisibilityLoggerMixin(getMetricsCategory(),
-              FeatureFactory.getFactory(this).getMetricsFeatureProvider()));
+        getLifecycle().addObserver(new VisibilityLoggerMixin(getMetricsCategory()));
     }
 
 }
diff --git a/src/com/android/settings/core/InstrumentedFragment.java b/src/com/android/settings/core/InstrumentedFragment.java
index b1215b9..45db836 100644
--- a/src/com/android/settings/core/InstrumentedFragment.java
+++ b/src/com/android/settings/core/InstrumentedFragment.java
@@ -18,28 +18,30 @@
 
 import android.content.Context;
 
+import com.android.settings.core.instrumentation.Instrumentable;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.survey.SurveyMixin;
-import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settingslib.core.lifecycle.ObservableFragment;
 
 public abstract class InstrumentedFragment extends ObservableFragment implements Instrumentable {
 
     protected MetricsFeatureProvider mMetricsFeatureProvider;
 
-    private VisibilityLoggerMixin mVisibilityLoggerMixin;
+    private final VisibilityLoggerMixin mVisibilityLoggerMixin;
+
+    public InstrumentedFragment() {
+        // Mixin that logs visibility change for activity.
+        mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory());
+        getLifecycle().addObserver(mVisibilityLoggerMixin);
+        getLifecycle().addObserver(new SurveyMixin(this, getClass().getSimpleName()));
+    }
 
     @Override
     public void onAttach(Context context) {
-        mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
-        mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(),
-                mMetricsFeatureProvider);
-        // Mixin that logs visibility change for activity.
-        getLifecycle().addObserver(mVisibilityLoggerMixin);
-        getLifecycle().addObserver(new SurveyMixin(this, getClass().getSimpleName()));
         super.onAttach(context);
+        mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
     }
 
     @Override
diff --git a/src/com/android/settings/core/InstrumentedPreferenceFragment.java b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
index 278676c..7e37115 100644
--- a/src/com/android/settings/core/InstrumentedPreferenceFragment.java
+++ b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
@@ -23,11 +23,11 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.settings.core.instrumentation.Instrumentable;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.survey.SurveyMixin;
-import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
 
 /**
@@ -44,17 +44,19 @@
     // metrics placeholder value. Only use this for development.
     protected final int PLACEHOLDER_METRIC = 10000;
 
-    private VisibilityLoggerMixin mVisibilityLoggerMixin;
+    private final VisibilityLoggerMixin mVisibilityLoggerMixin;
+
+    public InstrumentedPreferenceFragment() {
+        // Mixin that logs visibility change for activity.
+        mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory());
+        getLifecycle().addObserver(mVisibilityLoggerMixin);
+        getLifecycle().addObserver(new SurveyMixin(this, getClass().getSimpleName()));
+    }
 
     @Override
     public void onAttach(Context context) {
-        mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
-        // Mixin that logs visibility change for activity.
-        mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(),
-                mMetricsFeatureProvider);
-        getLifecycle().addObserver(mVisibilityLoggerMixin);
-        getLifecycle().addObserver(new SurveyMixin(this, getClass().getSimpleName()));
         super.onAttach(context);
+        mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
     }
 
     @Override
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 0a4b1f2..2cb1cbf 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -57,6 +57,7 @@
 import com.android.settings.applications.manageapplications.ManageApplications;
 import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
 import com.android.settings.bluetooth.BluetoothSettings;
+import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
 import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
 import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
 import com.android.settings.datausage.DataPlanUsageSummary;
@@ -254,7 +255,8 @@
             LockscreenDashboardFragment.class.getName(),
             BluetoothDeviceDetailsFragment.class.getName(),
             DataUsageList.class.getName(),
-            DirectoryAccessDetails.class.getName()
+            DirectoryAccessDetails.class.getName(),
+            AdvancedConnectedDeviceDashboardFragment.class.getName()
     };
 
     public static final String[] SETTINGS_FOR_RESTRICTED = {
@@ -296,5 +298,6 @@
             Settings.DateTimeSettingsActivity.class.getName(),
             Settings.DeviceInfoSettingsActivity.class.getName(),
             Settings.EnterprisePrivacySettingsActivity.class.getName(),
+            Settings.MeCardActivity.class.getName(),
     };
 }
diff --git a/src/com/android/settings/core/instrumentation/EventLogWriter.java b/src/com/android/settings/core/instrumentation/EventLogWriter.java
new file mode 100644
index 0000000..3196f76
--- /dev/null
+++ b/src/com/android/settings/core/instrumentation/EventLogWriter.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.core.instrumentation;
+
+import android.content.Context;
+import android.metrics.LogMaker;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+
+/**
+ * {@link LogWriter} that writes data to eventlog.
+ */
+public class EventLogWriter implements LogWriter {
+
+    private final MetricsLogger mMetricsLogger = new MetricsLogger();
+
+    public void visible(Context context, int source, int category) {
+        final LogMaker logMaker = new LogMaker(category)
+                .setType(MetricsProto.MetricsEvent.TYPE_OPEN)
+                .addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source);
+        MetricsLogger.action(logMaker);
+    }
+
+    public void hidden(Context context, int category) {
+        MetricsLogger.hidden(context, category);
+    }
+
+    public void action(int category, int value, Pair<Integer, Object>... taggedData) {
+        if (taggedData == null || taggedData.length == 0) {
+            mMetricsLogger.action(category, value);
+        } else {
+            final LogMaker logMaker = new LogMaker(category)
+                    .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
+                    .setSubtype(value);
+            for (Pair<Integer, Object> pair : taggedData) {
+                logMaker.addTaggedData(pair.first, pair.second);
+            }
+            mMetricsLogger.write(logMaker);
+        }
+    }
+
+    public void action(int category, boolean value, Pair<Integer, Object>... taggedData) {
+        action(category, value ? 1 : 0, taggedData);
+    }
+
+    public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
+        action(context, category, "", taggedData);
+    }
+
+    public void actionWithSource(Context context, int source, int category) {
+        final LogMaker logMaker = new LogMaker(category)
+                .setType(MetricsProto.MetricsEvent.TYPE_ACTION);
+        if (source != MetricsProto.MetricsEvent.VIEW_UNKNOWN) {
+            logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source);
+        }
+        MetricsLogger.action(logMaker);
+    }
+
+    /** @deprecated use {@link #action(int, int, Pair[])} */
+    @Deprecated
+    public void action(Context context, int category, int value) {
+        MetricsLogger.action(context, category, value);
+    }
+
+    /** @deprecated use {@link #action(int, boolean, Pair[])} */
+    @Deprecated
+    public void action(Context context, int category, boolean value) {
+        MetricsLogger.action(context, category, value);
+    }
+
+    public void action(Context context, int category, String pkg,
+            Pair<Integer, Object>... taggedData) {
+        if (taggedData == null || taggedData.length == 0) {
+            MetricsLogger.action(context, category, pkg);
+        } else {
+            final LogMaker logMaker = new LogMaker(category)
+                    .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
+                    .setPackageName(pkg);
+            for (Pair<Integer, Object> pair : taggedData) {
+                logMaker.addTaggedData(pair.first, pair.second);
+            }
+            MetricsLogger.action(logMaker);
+        }
+    }
+
+    public void count(Context context, String name, int value) {
+        MetricsLogger.count(context, name, value);
+    }
+
+    public void histogram(Context context, String name, int bucket) {
+        MetricsLogger.histogram(context, name, bucket);
+    }
+}
diff --git a/src/com/android/settings/core/instrumentation/Instrumentable.java b/src/com/android/settings/core/instrumentation/Instrumentable.java
new file mode 100644
index 0000000..f58e140
--- /dev/null
+++ b/src/com/android/settings/core/instrumentation/Instrumentable.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.core.instrumentation;
+
+public interface Instrumentable {
+
+    int METRICS_CATEGORY_UNKNOWN = 0;
+
+    /**
+     * Instrumented name for a view as defined in
+     * {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}.
+     */
+    int getMetricsCategory();
+}
diff --git a/src/com/android/settings/core/instrumentation/InstrumentedDialogFragment.java b/src/com/android/settings/core/instrumentation/InstrumentedDialogFragment.java
index 0a214f1..5a9ab56 100644
--- a/src/com/android/settings/core/instrumentation/InstrumentedDialogFragment.java
+++ b/src/com/android/settings/core/instrumentation/InstrumentedDialogFragment.java
@@ -19,9 +19,6 @@
 
 import com.android.settings.DialogCreatable;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settingslib.core.lifecycle.ObservableDialogFragment;
 
 public abstract class InstrumentedDialogFragment extends ObservableDialogFragment
@@ -41,15 +38,13 @@
     public InstrumentedDialogFragment(DialogCreatable dialogCreatable, int dialogId) {
         mDialogCreatable = dialogCreatable;
         mDialogId = dialogId;
+        mLifecycle.addObserver(new VisibilityLoggerMixin(getMetricsCategory()));
     }
 
+
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
-        mMetricsFeatureProvider = FeatureFactory.getFactory(context)
-                .getMetricsFeatureProvider();
-        mLifecycle.addObserver(new VisibilityLoggerMixin(getMetricsCategory(),
-                mMetricsFeatureProvider));
-        mLifecycle.onAttach(context);
+        mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
     }
 }
diff --git a/src/com/android/settings/core/instrumentation/LogWriter.java b/src/com/android/settings/core/instrumentation/LogWriter.java
new file mode 100644
index 0000000..062d46f
--- /dev/null
+++ b/src/com/android/settings/core/instrumentation/LogWriter.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.core.instrumentation;
+
+import android.content.Context;
+import android.util.Pair;
+
+/**
+ * Generic log writer interface.
+ */
+public interface LogWriter {
+
+    /**
+     * Logs a visibility event when view becomes visible.
+     */
+    void visible(Context context, int source, int category);
+
+    /**
+     * Logs a visibility event when view becomes hidden.
+     */
+    void hidden(Context context, int category);
+
+    /**
+     * Logs a user action.
+     */
+    void action(int category, int value, Pair<Integer, Object>... taggedData);
+
+    /**
+     * Logs a user action.
+     */
+    void action(int category, boolean value, Pair<Integer, Object>... taggedData);
+
+    /**
+     * Logs an user action.
+     */
+    void action(Context context, int category, Pair<Integer, Object>... taggedData);
+
+    /**
+     * Logs an user action.
+     */
+    void actionWithSource(Context context, int source, int category);
+
+    /**
+     * Logs an user action.
+     * @deprecated use {@link #action(int, int, Pair[])}
+     */
+    @Deprecated
+    void action(Context context, int category, int value);
+
+    /**
+     * Logs an user action.
+     * @deprecated use {@link #action(int, boolean, Pair[])}
+     */
+    @Deprecated
+    void action(Context context, int category, boolean value);
+
+    /**
+     * Logs an user action.
+     */
+    void action(Context context, int category, String pkg, Pair<Integer, Object>... taggedData);
+
+    /**
+     * Logs a count.
+     */
+    void count(Context context, String name, int value);
+
+    /**
+     * Logs a histogram event.
+     */
+    void histogram(Context context, String name, int bucket);
+}
diff --git a/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java b/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java
new file mode 100644
index 0000000..166cbb8
--- /dev/null
+++ b/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.core.instrumentation;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * FeatureProvider for metrics.
+ */
+public class MetricsFeatureProvider {
+    private List<LogWriter> mLoggerWriters;
+
+    public MetricsFeatureProvider() {
+        mLoggerWriters = new ArrayList<>();
+        installLogWriters();
+    }
+
+    protected void installLogWriters() {
+        mLoggerWriters.add(new EventLogWriter());
+    }
+
+    public void visible(Context context, int source, int category) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.visible(context, source, category);
+        }
+    }
+
+    public void hidden(Context context, int category) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.hidden(context, category);
+        }
+    }
+
+    public void actionWithSource(Context context, int source, int category) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.actionWithSource(context, source, category);
+        }
+    }
+
+    /**
+     * Logs a user action. Includes the elapsed time since the containing
+     * fragment has been visible.
+     */
+    public void action(VisibilityLoggerMixin visibilityLogger, int category, int value) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.action(category, value,
+                    sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible()));
+        }
+    }
+
+    /**
+     * Logs a user action. Includes the elapsed time since the containing
+     * fragment has been visible.
+     */
+    public void action(VisibilityLoggerMixin visibilityLogger, int category, boolean value) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.action(category, value,
+                    sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible()));
+        }
+    }
+
+    public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.action(context, category, taggedData);
+        }
+    }
+
+    /** @deprecated use {@link #action(VisibilityLoggerMixin, int, int)} */
+    @Deprecated
+    public void action(Context context, int category, int value) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.action(context, category, value);
+        }
+    }
+
+    /** @deprecated use {@link #action(VisibilityLoggerMixin, int, boolean)} */
+    @Deprecated
+    public void action(Context context, int category, boolean value) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.action(context, category, value);
+        }
+    }
+
+    public void action(Context context, int category, String pkg,
+            Pair<Integer, Object>... taggedData) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.action(context, category, pkg, taggedData);
+        }
+    }
+
+    public void count(Context context, String name, int value) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.count(context, name, value);
+        }
+    }
+
+    public void histogram(Context context, String name, int bucket) {
+        for (LogWriter writer : mLoggerWriters) {
+            writer.histogram(context, name, bucket);
+        }
+    }
+
+    public int getMetricsCategory(Object object) {
+        if (object == null || !(object instanceof Instrumentable)) {
+            return MetricsEvent.VIEW_UNKNOWN;
+        }
+        return ((Instrumentable) object).getMetricsCategory();
+    }
+
+    public void logDashboardStartIntent(Context context, Intent intent,
+            int sourceMetricsCategory) {
+        if (intent == null) {
+            return;
+        }
+        final ComponentName cn = intent.getComponent();
+        if (cn == null) {
+            final String action = intent.getAction();
+            if (TextUtils.isEmpty(action)) {
+                // Not loggable
+                return;
+            }
+            action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action,
+                    Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
+            return;
+        } else if (TextUtils.equals(cn.getPackageName(), context.getPackageName())) {
+            // Going to a Setting internal page, skip click logging in favor of page's own
+            // visibility logging.
+            return;
+        }
+        action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(),
+                Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
+    }
+
+    private Pair<Integer, Object> sinceVisibleTaggedData(long timestamp) {
+        return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp);
+    }
+}
diff --git a/src/com/android/settings/core/instrumentation/SharedPreferencesLogger.java b/src/com/android/settings/core/instrumentation/SharedPreferencesLogger.java
new file mode 100644
index 0000000..dee40c0
--- /dev/null
+++ b/src/com/android/settings/core/instrumentation/SharedPreferencesLogger.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.core.instrumentation;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.AsyncTask;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+public class SharedPreferencesLogger implements SharedPreferences {
+
+    private static final String LOG_TAG = "SharedPreferencesLogger";
+
+    private final String mTag;
+    private final Context mContext;
+    private final MetricsFeatureProvider mMetricsFeature;
+    private final Set<String> mPreferenceKeySet;
+
+    public SharedPreferencesLogger(Context context, String tag) {
+        mContext = context;
+        mTag = tag;
+        mMetricsFeature = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+        mPreferenceKeySet = new ConcurrentSkipListSet<>();
+    }
+
+    @Override
+    public Map<String, ?> getAll() {
+        return null;
+    }
+
+    @Override
+    public String getString(String key, @Nullable String defValue) {
+        return defValue;
+    }
+
+    @Override
+    public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
+        return defValues;
+    }
+
+    @Override
+    public int getInt(String key, int defValue) {
+        return defValue;
+    }
+
+    @Override
+    public long getLong(String key, long defValue) {
+        return defValue;
+    }
+
+    @Override
+    public float getFloat(String key, float defValue) {
+        return defValue;
+    }
+
+    @Override
+    public boolean getBoolean(String key, boolean defValue) {
+        return defValue;
+    }
+
+    @Override
+    public boolean contains(String key) {
+        return false;
+    }
+
+    @Override
+    public Editor edit() {
+        return new EditorLogger();
+    }
+
+    @Override
+    public void registerOnSharedPreferenceChangeListener(
+            OnSharedPreferenceChangeListener listener) {
+    }
+
+    @Override
+    public void unregisterOnSharedPreferenceChangeListener(
+            OnSharedPreferenceChangeListener listener) {
+    }
+
+    private void logValue(String key, Object value) {
+        logValue(key, value, false /* forceLog */);
+    }
+
+    private void logValue(String key, Object value, boolean forceLog) {
+        final String prefKey = buildPrefKey(mTag, key);
+        if (!forceLog && !mPreferenceKeySet.contains(prefKey)) {
+            // Pref key doesn't exist in set, this is initial display so we skip metrics but
+            // keeps track of this key.
+            mPreferenceKeySet.add(prefKey);
+            return;
+        }
+        // TODO: Remove count logging to save some resource.
+        mMetricsFeature.count(mContext, buildCountName(prefKey, value), 1);
+
+        final Pair<Integer, Object> valueData;
+        if (value instanceof Long) {
+            final Long longVal = (Long) value;
+            final int intVal;
+            if (longVal > Integer.MAX_VALUE) {
+                intVal = Integer.MAX_VALUE;
+            } else if (longVal < Integer.MIN_VALUE) {
+                intVal = Integer.MIN_VALUE;
+            } else {
+                intVal = longVal.intValue();
+            }
+            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
+                    intVal);
+        } else if (value instanceof Integer) {
+            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
+                    value);
+        } else if (value instanceof Boolean) {
+            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
+                    (Boolean) value ? 1 : 0);
+        } else if (value instanceof Float) {
+            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE,
+                    value);
+        } else if (value instanceof String) {
+            Log.d(LOG_TAG, "Tried to log string preference " + prefKey + " = " + value);
+            valueData = null;
+        } else {
+            Log.w(LOG_TAG, "Tried to log unloggable object" + value);
+            valueData = null;
+        }
+        if (valueData != null) {
+            // Pref key exists in set, log it's change in metrics.
+            mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE,
+                    Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey),
+                    valueData);
+        }
+    }
+
+    @VisibleForTesting
+    void logPackageName(String key, String value) {
+        final String prefKey = mTag + "/" + key;
+        mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, value,
+                Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey));
+    }
+
+    private void safeLogValue(String key, String value) {
+        new AsyncPackageCheck().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, key, value);
+    }
+
+    public static String buildCountName(String prefKey, Object value) {
+        return prefKey + "|" + value;
+    }
+
+    public static String buildPrefKey(String tag, String key) {
+        return tag + "/" + key;
+    }
+
+    private class AsyncPackageCheck extends AsyncTask<String, Void, Void> {
+        @Override
+        protected Void doInBackground(String... params) {
+            String key = params[0];
+            String value = params[1];
+            PackageManager pm = mContext.getPackageManager();
+            try {
+                // Check if this might be a component.
+                ComponentName name = ComponentName.unflattenFromString(value);
+                if (value != null) {
+                    value = name.getPackageName();
+                }
+            } catch (Exception e) {
+            }
+            try {
+                pm.getPackageInfo(value, PackageManager.MATCH_ANY_USER);
+                logPackageName(key, value);
+            } catch (PackageManager.NameNotFoundException e) {
+                // Clearly not a package, and it's unlikely this preference is in prefSet, so
+                // lets force log it.
+                logValue(key, value, true /* forceLog */);
+            }
+            return null;
+        }
+    }
+
+    public class EditorLogger implements Editor {
+        @Override
+        public Editor putString(String key, @Nullable String value) {
+            safeLogValue(key, value);
+            return this;
+        }
+
+        @Override
+        public Editor putStringSet(String key, @Nullable Set<String> values) {
+            safeLogValue(key, TextUtils.join(",", values));
+            return this;
+        }
+
+        @Override
+        public Editor putInt(String key, int value) {
+            logValue(key, value);
+            return this;
+        }
+
+        @Override
+        public Editor putLong(String key, long value) {
+            logValue(key, value);
+            return this;
+        }
+
+        @Override
+        public Editor putFloat(String key, float value) {
+            logValue(key, value);
+            return this;
+        }
+
+        @Override
+        public Editor putBoolean(String key, boolean value) {
+            logValue(key, value);
+            return this;
+        }
+
+        @Override
+        public Editor remove(String key) {
+            return this;
+        }
+
+        @Override
+        public Editor clear() {
+            return this;
+        }
+
+        @Override
+        public boolean commit() {
+            return true;
+        }
+
+        @Override
+        public void apply() {
+        }
+    }
+}
diff --git a/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java b/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java
new file mode 100644
index 0000000..2fe2a3b
--- /dev/null
+++ b/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.core.instrumentation;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+
+import android.os.SystemClock;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.SettingsActivity;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnAttach;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+import static com.android.settings.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
+
+/**
+ * Logs visibility change of a fragment.
+ */
+public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause, OnAttach {
+
+    private static final String TAG = "VisibilityLoggerMixin";
+
+    private final int mMetricsCategory;
+
+    private MetricsFeatureProvider mMetricsFeature;
+    private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN;
+    private long mVisibleTimestamp;
+
+    public VisibilityLoggerMixin(int metricsCategory) {
+        // MetricsFeature will be set during onAttach.
+        this(metricsCategory, null /* metricsFeature */);
+    }
+
+    public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) {
+        mMetricsCategory = metricsCategory;
+        mMetricsFeature = metricsFeature;
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        mMetricsFeature = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+    }
+
+    @Override
+    public void onResume() {
+        mVisibleTimestamp = SystemClock.elapsedRealtime();
+        if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
+            mMetricsFeature.visible(null /* context */, mSourceMetricsCategory, mMetricsCategory);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        mVisibleTimestamp = 0;
+        if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
+            mMetricsFeature.hidden(null /* context */, mMetricsCategory);
+        }
+    }
+
+    /**
+     * Sets source metrics category for this logger. Source is the caller that opened this UI.
+     */
+    public void setSourceMetricsCategory(Activity activity) {
+        if (mSourceMetricsCategory != MetricsProto.MetricsEvent.VIEW_UNKNOWN || activity == null) {
+            return;
+        }
+        final Intent intent = activity.getIntent();
+        if (intent == null) {
+            return;
+        }
+        mSourceMetricsCategory = intent.getIntExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY,
+                MetricsProto.MetricsEvent.VIEW_UNKNOWN);
+    }
+
+    /** Returns elapsed time since onResume() */
+    public long elapsedTimeSinceVisible() {
+        if (mVisibleTimestamp == 0) {
+            return 0;
+        }
+        return SystemClock.elapsedRealtime() - mVisibleTimestamp;
+    }
+}
diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
index 2d35ea7..97eef13 100644
--- a/src/com/android/settings/dashboard/DashboardAdapter.java
+++ b/src/com/android/settings/dashboard/DashboardAdapter.java
@@ -41,6 +41,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.R.id;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardData.SuggestionConditionHeaderData;
 import com.android.settings.dashboard.conditional.Condition;
 import com.android.settings.dashboard.conditional.ConditionAdapter;
@@ -49,7 +50,6 @@
 import com.android.settings.dashboard.suggestions.SuggestionDismissController;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.Utils;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.drawer.DashboardCategory;
 import com.android.settingslib.drawer.Tile;
 
diff --git a/src/com/android/settings/dashboard/DashboardAdapterV2.java b/src/com/android/settings/dashboard/DashboardAdapterV2.java
index ad93e4c..a422ae4 100644
--- a/src/com/android/settings/dashboard/DashboardAdapterV2.java
+++ b/src/com/android/settings/dashboard/DashboardAdapterV2.java
@@ -39,13 +39,13 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.R.id;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardDataV2.ConditionHeaderData;
 import com.android.settings.dashboard.conditional.Condition;
 import com.android.settings.dashboard.conditional.ConditionAdapterV2;
 import com.android.settings.dashboard.suggestions.SuggestionAdapterV2;
 import com.android.settings.dashboard.suggestions.SuggestionControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index a14d9e9..a06fee9 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -41,9 +41,8 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.core.FeatureFlags;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settingslib.drawer.CategoryManager;
 import com.android.settingslib.drawer.DashboardCategory;
 import com.android.settingslib.drawer.ProfileSelectDialog;
@@ -160,8 +159,7 @@
             pref.setFragment(clsName);
         } else if (tile.intent != null) {
             final Intent intent = new Intent(tile.intent);
-            intent.putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY,
-                    sourceMetricsCategory);
+            intent.putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, sourceMetricsCategory);
             if (action != null) {
                 intent.setAction(action);
             }
@@ -210,7 +208,7 @@
             return;
         }
         final Intent intent = new Intent(tile.intent)
-                .putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY,
+                .putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY,
                         MetricsEvent.DASHBOARD_SUMMARY)
                 .putExtra(SettingsDrawerActivity.EXTRA_SHOW_MENU, true)
                 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
diff --git a/src/com/android/settings/dashboard/conditional/Condition.java b/src/com/android/settings/dashboard/conditional/Condition.java
index d66440e..05783bd 100644
--- a/src/com/android/settings/dashboard/conditional/Condition.java
+++ b/src/com/android/settings/dashboard/conditional/Condition.java
@@ -24,8 +24,8 @@
 
 import android.support.annotation.VisibleForTesting;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 public abstract class Condition {
 
diff --git a/src/com/android/settings/dashboard/conditional/ConditionAdapter.java b/src/com/android/settings/dashboard/conditional/ConditionAdapter.java
index d84aa7c..eb768e5 100644
--- a/src/com/android/settings/dashboard/conditional/ConditionAdapter.java
+++ b/src/com/android/settings/dashboard/conditional/ConditionAdapter.java
@@ -27,13 +27,13 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardAdapter;
 import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
 import com.android.settings.dashboard.DashboardData;
 import com.android.settings.dashboard.DashboardData.HeaderMode;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.WirelessUtils;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import java.util.List;
 import java.util.Objects;
diff --git a/src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java b/src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java
index 8db57f7..3f3e5c9 100644
--- a/src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java
+++ b/src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java
@@ -27,10 +27,10 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.WirelessUtils;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import java.util.List;
 import java.util.Objects;
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java
index 2b79a9b..fc11029 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java
@@ -27,10 +27,10 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
 import com.android.settings.dashboard.DashboardAdapter.IconCache;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import java.util.List;
 import java.util.Objects;
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java b/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java
index e04ae93..afd0e08 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java
@@ -31,10 +31,10 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder;
 import com.android.settings.dashboard.DashboardAdapterV2.IconCache;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
index 8cd1675..fe19b95 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
@@ -36,6 +36,7 @@
 import com.android.settings.Settings.DoubleTwistSuggestionActivity;
 import com.android.settings.Settings.NightDisplaySuggestionActivity;
 import com.android.settings.Settings.SwipeToNotificationSuggestionActivity;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.fingerprint.FingerprintEnrollSuggestionActivity;
 import com.android.settings.fingerprint.FingerprintSuggestionActivity;
 import com.android.settings.gestures.DoubleTapPowerPreferenceController;
@@ -48,7 +49,6 @@
 import com.android.settings.support.NewDeviceIntroSuggestionActivity;
 import com.android.settings.wallpaper.WallpaperSuggestionActivity;
 import com.android.settings.wifi.WifiCallingSuggestionActivity;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.drawer.Tile;
 
 import java.util.List;
diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java
index b59da9d..ff568c7 100644
--- a/src/com/android/settings/datausage/DataSaverBackend.java
+++ b/src/com/android/settings/datausage/DataSaverBackend.java
@@ -25,8 +25,8 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.ArrayList;
diff --git a/src/com/android/settings/datetime/ZonePicker.java b/src/com/android/settings/datetime/ZonePicker.java
index dc69127..57c340c 100644
--- a/src/com/android/settings/datetime/ZonePicker.java
+++ b/src/com/android/settings/datetime/ZonePicker.java
@@ -35,9 +35,8 @@
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
+import com.android.settings.core.instrumentation.Instrumentable;
+import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settingslib.datetime.ZoneGetter;
 
 import java.text.Collator;
@@ -58,7 +57,8 @@
 
     private static final int MENU_TIMEZONE = Menu.FIRST+1;
     private static final int MENU_ALPHABETICAL = Menu.FIRST;
-    private VisibilityLoggerMixin mVisibilityLoggerMixin;
+    private final VisibilityLoggerMixin mVisibilityLoggerMixin =
+            new VisibilityLoggerMixin(getMetricsCategory());
 
     private boolean mSortedByTimezone;
 
@@ -145,6 +145,12 @@
     }
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mVisibilityLoggerMixin.onAttach(context);
+    }
+
+    @Override
     public int getMetricsCategory() {
         return MetricsProto.MetricsEvent.ZONE_PICKER;
     }
@@ -164,13 +170,6 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(),
-            FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider());
-    }
-
-    @Override
     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         final View view = super.onCreateView(inflater, container, savedInstanceState);
diff --git a/src/com/android/settings/datetime/timezone/ZonePicker.java b/src/com/android/settings/datetime/timezone/ZonePicker.java
index eafbaa2..d0d1720 100644
--- a/src/com/android/settings/datetime/timezone/ZonePicker.java
+++ b/src/com/android/settings/datetime/timezone/ZonePicker.java
@@ -31,6 +31,7 @@
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
 import android.widget.Spinner;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
@@ -56,11 +57,14 @@
     private List<RegionInfo> mRegions;
     private Map<String, List<TimeZoneInfo>> mZoneInfos;
     private List<TimeZoneInfo> mFixedOffsetTimeZones;
-    private TimeZoneAdapter mTimeZoneAdapter;
     private String mSelectedTimeZone;
     private boolean mSelectByRegion;
     private DataLoader mDataLoader;
+    private TimeZoneAdapter mTimeZoneAdapter;
+
     private RecyclerView mRecyclerView;
+    private LinearLayout mRegionSpinnerLayout;
+    private Spinner mRegionSpinner;
 
     @Override
     public int getMetricsCategory() {
@@ -88,15 +92,17 @@
         final ArrayAdapter<RegionInfo> regionAdapter = new ArrayAdapter<>(getContext(),
                 R.layout.filter_spinner_item, mRegions);
         regionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-        final Spinner spinner = view.findViewById(R.id.tz_region_spinner);
-        spinner.setAdapter(regionAdapter);
-        spinner.setOnItemSelectedListener(this);
-        setupForCurrentTimeZone(spinner);
+
+        mRegionSpinnerLayout = view.findViewById(R.id.tz_region_spinner_layout);
+        mRegionSpinner = view.findViewById(R.id.tz_region_spinner);
+        mRegionSpinner.setAdapter(regionAdapter);
+        mRegionSpinner.setOnItemSelectedListener(this);
+        setupForCurrentTimeZone();
         setHasOptionsMenu(true);
         return view;
     }
 
-    private void setupForCurrentTimeZone(Spinner spinner) {
+    private void setupForCurrentTimeZone() {
         final String localeRegionId = mLocale.getCountry().toUpperCase(Locale.ROOT);
         final String currentTimeZone = TimeZone.getDefault().getID();
         boolean fixedOffset = currentTimeZone.startsWith("Etc/GMT") ||
@@ -105,12 +111,12 @@
         for (int regionIndex = 0; regionIndex < mRegions.size(); regionIndex++) {
             final RegionInfo region = mRegions.get(regionIndex);
             if (localeRegionId.equals(region.getId())) {
-                spinner.setSelection(regionIndex);
+                mRegionSpinner.setSelection(regionIndex);
             }
             if (!fixedOffset) {
                 for (String timeZoneId: region.getTimeZoneIds()) {
                     if (TextUtils.equals(timeZoneId, mSelectedTimeZone)) {
-                        spinner.setSelection(regionIndex);
+                        mRegionSpinner.setSelection(regionIndex);
                         return;
                     }
                 }
@@ -179,16 +185,15 @@
 
     private void setSelectByRegion(boolean selectByRegion) {
         mSelectByRegion = selectByRegion;
-        getView().findViewById(R.id.tz_region_spinner_layout).setVisibility(
+        mRegionSpinnerLayout.setVisibility(
             mSelectByRegion ? View.VISIBLE : View.GONE);
         List<TimeZoneInfo> tzInfos;
         if (selectByRegion) {
-            Spinner regionSpinner = getView().findViewById(R.id.tz_region_spinner);
-            int selectedRegion = regionSpinner.getSelectedItemPosition();
+            int selectedRegion = mRegionSpinner.getSelectedItemPosition();
             if (selectedRegion == -1) {
                 // Arbitrarily pick the first item if no region was selected above.
                 selectedRegion = 0;
-                regionSpinner.setSelection(selectedRegion);
+                mRegionSpinner.setSelection(selectedRegion);
             }
             tzInfos = getTimeZoneInfos(mRegions.get(selectedRegion));
         } else {
diff --git a/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarController.java b/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarController.java
index a20afa1..8ab1a07 100644
--- a/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarController.java
+++ b/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarController.java
@@ -23,10 +23,10 @@
 import android.support.v7.preference.Preference;
 import android.widget.Switch;
 
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.Preconditions;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.widget.SwitchBar;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
 /** Handles the logic for flipping the storage management toggle on a {@link SwitchBar}. */
 public class AutomaticStorageManagerSwitchBarController
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 0420ecf..ce4b7bf 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -398,6 +398,7 @@
         controllers.add(new CameraLaserSensorPreferenceController(context));
         controllers.add(new WifiDisplayCertificationPreferenceController(context));
         controllers.add(new WifiVerboseLoggingPreferenceController(context));
+        controllers.add(new WifiConnectedMacRandomizationPreferenceController(context));
         controllers.add(new MobileDataAlwaysOnPreferenceController(context));
         controllers.add(new TetheringHardwareAccelPreferenceController(context));
         controllers.add(new SelectUsbConfigPreferenceController(context, lifecycle));
diff --git a/src/com/android/settings/development/WifiConnectedMacRandomizationPreferenceController.java b/src/com/android/settings/development/WifiConnectedMacRandomizationPreferenceController.java
new file mode 100644
index 0000000..eae2355
--- /dev/null
+++ b/src/com/android/settings/development/WifiConnectedMacRandomizationPreferenceController.java
@@ -0,0 +1,93 @@
+/*
+ * 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.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+public class WifiConnectedMacRandomizationPreferenceController extends
+        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
+        PreferenceControllerMixin {
+
+    private static final String WIFI_CONNECTED_MAC_RANDOMIZATION_KEY =
+            "wifi_connected_mac_randomization";
+
+    @VisibleForTesting
+    static final int SETTING_VALUE_ON = 1;
+    @VisibleForTesting
+    static final int SETTING_VALUE_OFF = 0;
+
+    private SwitchPreference mPreference;
+
+    public WifiConnectedMacRandomizationPreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mContext.getResources().getBoolean(
+                R.bool.config_wifi_support_connected_mac_randomization);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return WIFI_CONNECTED_MAC_RANDOMIZATION_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.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+                isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        final int enableMode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, SETTING_VALUE_OFF);
+        mPreference.setChecked(enableMode != SETTING_VALUE_OFF);
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchEnabled() {
+        mPreference.setEnabled(true);
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, SETTING_VALUE_OFF);
+        mPreference.setChecked(false);
+        mPreference.setEnabled(false);
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/BrandedAccountPreferenceController.java b/src/com/android/settings/deviceinfo/BrandedAccountPreferenceController.java
new file mode 100644
index 0000000..5565e3d
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/BrandedAccountPreferenceController.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.accounts.AccountDetailDashboardFragment;
+import com.android.settings.accounts.AccountFeatureProvider;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+
+public class BrandedAccountPreferenceController extends BasePreferenceController {
+    private static final String KEY_PREFERENCE_TITLE = "account";
+    private final Account[] mAccounts;
+
+    public BrandedAccountPreferenceController(Context context) {
+        super(context, KEY_PREFERENCE_TITLE);
+        final AccountFeatureProvider accountFeatureProvider = FeatureFactory.getFactory(
+                mContext).getAccountFeatureProvider();
+        mAccounts = accountFeatureProvider.getAccounts(mContext);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        if (mAccounts != null && mAccounts.length > 0) {
+            return AVAILABLE;
+        }
+        return DISABLED_FOR_USER;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        final AccountFeatureProvider accountFeatureProvider = FeatureFactory.getFactory(
+                mContext).getAccountFeatureProvider();
+        final Preference accountPreference = screen.findPreference(KEY_PREFERENCE_TITLE);
+        if (accountPreference != null && (mAccounts == null || mAccounts.length == 0)) {
+            screen.removePreference(accountPreference);
+            return;
+        }
+
+        accountPreference.setSummary(mAccounts[0].name);
+        accountPreference.setOnPreferenceClickListener(preference -> {
+            final Bundle args = new Bundle();
+            args.putParcelable(AccountDetailDashboardFragment.KEY_ACCOUNT,
+                    mAccounts[0]);
+            args.putParcelable(AccountDetailDashboardFragment.KEY_USER_HANDLE,
+                    android.os.Process.myUserHandle());
+            args.putString(AccountDetailDashboardFragment.KEY_ACCOUNT_TYPE,
+                    accountFeatureProvider.getAccountType());
+            Utils.startWithFragment(mContext, AccountDetailDashboardFragment.class.getName(),
+                    args, null, 0,
+                    R.string.account_sync_title, null, MetricsEvent.ACCOUNT);
+            return true;
+        });
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
index 0f3bfb8..2641f5d 100644
--- a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
@@ -34,11 +34,11 @@
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnResume;
diff --git a/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java b/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java
index 8ab21b3..717d765 100644
--- a/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java
@@ -25,12 +25,12 @@
 import android.support.v7.preference.PreferenceScreen;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.deletionhelper.ActivationWarningFragment;
 import com.android.settings.widget.MasterSwitchController;
 import com.android.settings.widget.MasterSwitchPreference;
 import com.android.settings.widget.SwitchWidgetController;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnResume;
 
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 3623298..1149b99 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -38,11 +38,11 @@
 import com.android.settings.Utils;
 import com.android.settings.applications.manageapplications.ManageApplications;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.deviceinfo.PrivateVolumeSettings.SystemInfoFragment;
 import com.android.settings.deviceinfo.StorageItemPreference;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.deviceinfo.StorageMeasurement;
 import com.android.settingslib.deviceinfo.StorageVolumeProvider;
 
diff --git a/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java b/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java
index f3d17d5..68a21ce 100644
--- a/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java
+++ b/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java
@@ -27,11 +27,11 @@
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.settings.R;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.search.DatabaseIndexingUtils;
 import com.android.settings.search.InlineSwitchPayload;
 import com.android.settings.search.ResultPayload;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 public class AmbientDisplayNotificationsPreferenceController extends
         AbstractPreferenceController implements PreferenceControllerMixin,
diff --git a/src/com/android/settings/display/AmbientDisplaySettings.java b/src/com/android/settings/display/AmbientDisplaySettings.java
index 187325c..24aede0 100644
--- a/src/com/android/settings/display/AmbientDisplaySettings.java
+++ b/src/com/android/settings/display/AmbientDisplaySettings.java
@@ -23,13 +23,13 @@
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.gestures.DoubleTapScreenPreferenceController;
 import com.android.settings.gestures.PickupGesturePreferenceController;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import java.util.ArrayList;
diff --git a/src/com/android/settings/display/AutoRotatePreferenceController.java b/src/com/android/settings/display/AutoRotatePreferenceController.java
index 2134b88..c7f6af1 100644
--- a/src/com/android/settings/display/AutoRotatePreferenceController.java
+++ b/src/com/android/settings/display/AutoRotatePreferenceController.java
@@ -20,9 +20,9 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.view.RotationPolicy;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
diff --git a/src/com/android/settings/display/ThemePreferenceController.java b/src/com/android/settings/display/ThemePreferenceController.java
index 9c1314e..d1341dd74 100644
--- a/src/com/android/settings/display/ThemePreferenceController.java
+++ b/src/com/android/settings/display/ThemePreferenceController.java
@@ -29,9 +29,9 @@
 
 import com.android.settings.R;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import libcore.util.Objects;
 
diff --git a/src/com/android/settings/display/TimeoutPreferenceController.java b/src/com/android/settings/display/TimeoutPreferenceController.java
index 1d86999..84f9641 100644
--- a/src/com/android/settings/display/TimeoutPreferenceController.java
+++ b/src/com/android/settings/display/TimeoutPreferenceController.java
@@ -13,9 +13,11 @@
  */
 package com.android.settings.display;
 
-import android.app.admin.DevicePolicyManager;
+import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+
 import android.content.Context;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.support.v7.preference.Preference;
 import android.util.Log;
@@ -25,10 +27,9 @@
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.wrapper.DevicePolicyManagerWrapper;
 import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.core.AbstractPreferenceController;
 
-import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-
 public class TimeoutPreferenceController extends AbstractPreferenceController implements
         PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
 
@@ -69,6 +70,13 @@
             timeoutListPreference.removeUnusableTimeouts(maxTimeout, admin);
         }
         updateTimeoutPreferenceDescription(timeoutListPreference, currentTimeout);
+
+        EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(
+                        mContext, UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT,
+                        UserHandle.myUserId());
+        if(admin != null) {
+            timeoutListPreference.removeUnusableTimeouts(0/* disable all*/, admin);
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index 819846a..de027a3 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -374,7 +374,7 @@
 
     @Override
     public void onLimitBackgroundActivity() {
-        mBackgroundActivityPreferenceController.setUnchecked(
+        mBackgroundActivityPreferenceController.setRestricted(
                 findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
     }
 }
diff --git a/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java b/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java
index a52433b..c034746 100644
--- a/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java
@@ -48,6 +48,7 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.ActionButtonPreference;
 import com.android.settings.wrapper.DevicePolicyManagerWrapper;
@@ -55,7 +56,6 @@
 import com.android.settingslib.applications.AppUtils;
 import com.android.settingslib.applications.ApplicationsState;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnDestroy;
diff --git a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
index 6323715..01e4182 100644
--- a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
@@ -43,20 +43,21 @@
  * Controller to control whether an app can run in the background
  */
 public class BackgroundActivityPreferenceController extends AbstractPreferenceController
-        implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+        implements PreferenceControllerMixin {
 
     private static final String TAG = "BgActivityPrefContr";
-    private static final String KEY_BACKGROUND_ACTIVITY = "background_activity";
+    @VisibleForTesting
+    static final String KEY_BACKGROUND_ACTIVITY = "background_activity";
 
-    private final PackageManager mPackageManager;
     private final AppOpsManager mAppOpsManager;
     private final UserManager mUserManager;
     private final int mUid;
     @VisibleForTesting
     DevicePolicyManagerWrapper mDpm;
+    @VisibleForTesting
+    BatteryUtils mBatteryUtils;
     private Fragment mFragment;
     private String mTargetPackage;
-    private boolean mIsPreOApp;
     private PowerWhitelistBackend mPowerWhitelistBackend;
 
     public BackgroundActivityPreferenceController(Context context, Fragment fragment,
@@ -69,7 +70,6 @@
             int uid, String packageName, PowerWhitelistBackend backend) {
         super(context);
         mPowerWhitelistBackend = backend;
-        mPackageManager = context.getPackageManager();
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mDpm = new DevicePolicyManagerWrapper(
                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE));
@@ -77,7 +77,7 @@
         mUid = uid;
         mFragment = fragment;
         mTargetPackage = packageName;
-        mIsPreOApp = isLegacyApp(packageName);
+        mBatteryUtils = BatteryUtils.getInstance(context);
     }
 
     @Override
@@ -85,11 +85,6 @@
         final int mode = mAppOpsManager
                 .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
         final boolean whitelisted = mPowerWhitelistBackend.isWhitelisted(mTargetPackage);
-        // Set checked or not before we may set it disabled
-        if (mode != AppOpsManager.MODE_ERRORED) {
-            final boolean checked = whitelisted || mode != AppOpsManager.MODE_IGNORED;
-            ((SwitchPreference) preference).setChecked(checked);
-        }
         if (whitelisted || mode == AppOpsManager.MODE_ERRORED
                 || Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mTargetPackage)) {
             preference.setEnabled(false);
@@ -108,14 +103,8 @@
      * Called from the warning dialog, if the user decides to go ahead and disable background
      * activity for this package
      */
-    public void setUnchecked(Preference preference) {
-        if (mIsPreOApp) {
-            mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage,
-                    AppOpsManager.MODE_IGNORED);
-        }
-        mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage,
-                AppOpsManager.MODE_IGNORED);
-        ((SwitchPreference) preference).setChecked(false);
+    public void setRestricted(Preference preference) {
+        mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_IGNORED);
         updateSummary(preference);
     }
 
@@ -125,33 +114,18 @@
     }
 
     @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final boolean switchOn = (Boolean) newValue;
-        if (!switchOn) {
-            final WarningDialogFragment dialogFragment = new WarningDialogFragment();
-            dialogFragment.setTargetFragment(mFragment, 0);
-            dialogFragment.show(mFragment.getFragmentManager(), TAG);
-            return false;
-        }
-        if (mIsPreOApp) {
-            mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage,
-                    AppOpsManager.MODE_ALLOWED);
-        }
-        mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage,
-                AppOpsManager.MODE_ALLOWED);
-        updateSummary(preference);
-        return true;
-    }
-
-    @VisibleForTesting
-    boolean isLegacyApp(final String packageName) {
-        try {
-            ApplicationInfo info = mPackageManager.getApplicationInfo(packageName,
-                    PackageManager.GET_META_DATA);
-
-            return info.targetSdkVersion < Build.VERSION_CODES.O;
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Cannot find package: " + packageName, e);
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (KEY_BACKGROUND_ACTIVITY.equals(preference.getKey())) {
+            final int mode = mAppOpsManager
+                    .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
+            final boolean restricted = mode == AppOpsManager.MODE_IGNORED;
+            if (!restricted) {
+                showDialog();
+                return false;
+            }
+            mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED);
+            updateSummary(preference);
+            return true;
         }
 
         return false;
@@ -169,12 +143,19 @@
         if (mode == AppOpsManager.MODE_ERRORED) {
             preference.setSummary(R.string.background_activity_summary_disabled);
         } else {
-            final boolean checked = mode != AppOpsManager.MODE_IGNORED;
-            preference.setSummary(checked ? R.string.background_activity_summary_on
-                    : R.string.background_activity_summary_off);
+            final boolean restricted = mode == AppOpsManager.MODE_IGNORED;
+            preference.setSummary(restricted ? R.string.restricted_true_label
+                    : R.string.restricted_false_label);
         }
     }
 
+    @VisibleForTesting
+    void showDialog() {
+        final WarningDialogFragment dialogFragment = new WarningDialogFragment();
+        dialogFragment.setTargetFragment(mFragment, 0 /* requestCode */);
+        dialogFragment.show(mFragment.getFragmentManager(), TAG);
+    }
+
     interface WarningConfirmationListener {
         void onLimitBackgroundActivity();
     }
diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
index 91f35e2..5d95dd2 100644
--- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -49,10 +49,10 @@
 import com.android.settings.core.FeatureFlags;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.Utils;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnDestroy;
diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index acd8144..c4c795b 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -272,7 +272,7 @@
         void onParsingDone();
     }
 
-    private static void parse(BatteryStats stats, BatteryDataParser... parsers) {
+    public static void parse(BatteryStats stats, BatteryDataParser... parsers) {
         long startWalltime = 0;
         long endWalltime = 0;
         long historyStart = 0;
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 0952f1f..5738c29 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -398,6 +398,19 @@
         return timeMs * 1000;
     }
 
+    public void setForceAppStandby(int uid, String packageName,
+            int mode) {
+        final boolean isPreOApp = isLegacyApp(packageName);
+        if (isPreOApp) {
+            // Control whether app could run in the background if it is pre O app
+            mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName,
+                    mode);
+        }
+        // Control whether app could run jobs in the background
+        mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName,
+                mode);
+    }
+
     public void initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle,
             UserManager userManager) {
         statsHelper.create(bundle);
@@ -481,5 +494,18 @@
         return 0;
     }
 
+    public boolean isLegacyApp(final String packageName) {
+        try {
+            ApplicationInfo info = mPackageManager.getApplicationInfo(packageName,
+                    PackageManager.GET_META_DATA);
+
+            return info.targetSdkVersion < Build.VERSION_CODES.O;
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Cannot find package: " + packageName, e);
+        }
+
+        return false;
+    }
+
 }
 
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index e0954e5..ec54291 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -47,6 +47,7 @@
 import com.android.settings.Utils;
 import com.android.settings.applications.LayoutPreference;
 import com.android.settings.applications.manageapplications.ManageApplications;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.display.AmbientDisplayPreferenceController;
 import com.android.settings.display.AutoBrightnessPreferenceController;
@@ -60,7 +61,6 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import java.util.ArrayList;
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java b/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java
index 605591d..c50d580 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java
@@ -54,6 +54,7 @@
 import com.android.settings.Utils;
 import com.android.settings.applications.LayoutPreference;
 import com.android.settings.applications.manageapplications.ManageApplications;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.display.AmbientDisplayPreferenceController;
 import com.android.settings.display.AutoBrightnessPreferenceController;
@@ -66,7 +67,6 @@
 import com.android.settings.fuelgauge.anomaly.AnomalySummaryPreferenceController;
 import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.ArrayList;
diff --git a/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java b/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java
index 7df0fb1..0a40f1e 100644
--- a/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java
@@ -20,9 +20,11 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.support.annotation.VisibleForTesting;
+import android.support.v14.preference.PreferenceFragment;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
+import com.android.settings.SettingsActivity;
 import com.android.settings.applications.LayoutPreference;
 import com.android.settings.core.BasePreferenceController;
 
@@ -37,12 +39,21 @@
 
     private AppOpsManager mAppOpsManager;
     private List<AppOpsManager.PackageOps> mPackageOps;
+    private SettingsActivity mSettingsActivity;
+    private PreferenceFragment mPreferenceFragment;
 
     public RestrictAppPreferenceController(Context context) {
         super(context, KEY_RESTRICT_APP);
         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
     }
 
+    public RestrictAppPreferenceController(SettingsActivity settingsActivity,
+            PreferenceFragment preferenceFragment) {
+        this(settingsActivity.getApplicationContext());
+        mSettingsActivity = settingsActivity;
+        mPreferenceFragment = preferenceFragment;
+    }
+
     @Override
     public int getAvailabilityStatus() {
         return AVAILABLE;
@@ -51,13 +62,27 @@
     @Override
     public void updateState(Preference preference) {
         super.updateState(preference);
+
         mPackageOps = mAppOpsManager.getPackagesForOps(
                 new int[]{AppOpsManager.OP_RUN_ANY_IN_BACKGROUND});
         final int num = mPackageOps != null ? mPackageOps.size() : 0;
 
+        // Enable the preference if some apps already been restricted, otherwise disable it
+        preference.setEnabled(num > 0);
         preference.setSummary(
                 mContext.getResources().getQuantityString(R.plurals.restricted_app_summary, num,
                         num));
     }
 
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (getPreferenceKey().equals(preference.getKey())) {
+            // start fragment
+            RestrictedAppDetails.startRestrictedAppDetails(mSettingsActivity, mPreferenceFragment,
+                    mPackageOps);
+            return true;
+        }
+
+        return super.handlePreferenceTreeClick(preference);
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/RestrictedAppDetails.java b/src/com/android/settings/fuelgauge/RestrictedAppDetails.java
new file mode 100644
index 0000000..bdaaa3a
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/RestrictedAppDetails.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.util.IconDrawableFactory;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.Utils;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.fuelgauge.anomaly.Anomaly;
+import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment;
+import com.android.settings.fuelgauge.anomaly.AnomalyPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.List;
+
+/**
+ * Fragment to show a list of anomaly apps, where user could handle these anomalies
+ */
+public class RestrictedAppDetails extends DashboardFragment {
+
+    public static final String TAG = "RestrictedAppDetails";
+
+    private static final String EXTRA_PACKAGE_OPS_LIST = "package_ops_list";
+    private static final String KEY_PREF_RESTRICTED_APP_LIST = "restrict_app_list";
+
+    @VisibleForTesting
+    List<AppOpsManager.PackageOps> mPackageOpsList;
+    @VisibleForTesting
+    IconDrawableFactory mIconDrawableFactory;
+    @VisibleForTesting
+    PreferenceGroup mRestrictedAppListGroup;
+    @VisibleForTesting
+    BatteryUtils mBatteryUtils;
+    @VisibleForTesting
+    PackageManager mPackageManager;
+
+    public static void startRestrictedAppDetails(SettingsActivity caller,
+            PreferenceFragment fragment, List<AppOpsManager.PackageOps> packageOpsList) {
+        Bundle args = new Bundle();
+        args.putParcelableList(EXTRA_PACKAGE_OPS_LIST, packageOpsList);
+
+        caller.startPreferencePanelAsUser(fragment, RestrictedAppDetails.class.getName(), args,
+                R.string.restricted_app_title, null /* titleText */,
+                new UserHandle(UserHandle.myUserId()));
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        final Context context = getContext();
+
+        mRestrictedAppListGroup = (PreferenceGroup) findPreference(KEY_PREF_RESTRICTED_APP_LIST);
+        mPackageOpsList = getArguments().getParcelableArrayList(EXTRA_PACKAGE_OPS_LIST);
+        mPackageManager = context.getPackageManager();
+        mIconDrawableFactory = IconDrawableFactory.newInstance(context);
+        mBatteryUtils = BatteryUtils.getInstance(context);
+
+        refreshUi();
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+
+        return super.onPreferenceTreeClick(preference);
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.restricted_apps_detail;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return null;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.FUELGAUGE_RESTRICTED_APP_DETAILS;
+    }
+
+    @VisibleForTesting
+    void refreshUi() {
+        mRestrictedAppListGroup.removeAll();
+        final Context context = getPrefContext();
+
+        for (int i = 0, size = mPackageOpsList.size(); i < size; i++) {
+            final CheckBoxPreference checkBoxPreference = new CheckBoxPreference(context);
+            final AppOpsManager.PackageOps packageOps = mPackageOpsList.get(i);
+            try {
+                final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
+                        packageOps.getPackageName(), 0 /* flags */);
+                checkBoxPreference.setChecked(true);
+                checkBoxPreference.setTitle(mPackageManager.getApplicationLabel(applicationInfo));
+                checkBoxPreference.setKey(packageOps.getPackageName());
+                checkBoxPreference.setOnPreferenceChangeListener((pref, value) -> {
+                    // change the toggle
+                    final int mode = (Boolean) value ? AppOpsManager.MODE_IGNORED
+                            : AppOpsManager.MODE_ALLOWED;
+                    final String packageName = pref.getKey();
+                    final int uid = mBatteryUtils.getPackageUid(packageName);
+                    mBatteryUtils.setForceAppStandby(uid, packageName, mode);
+                    return true;
+                });
+                mRestrictedAppListGroup.addPreference(checkBoxPreference);
+            } catch (PackageManager.NameNotFoundException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+}
diff --git a/src/com/android/settings/fuelgauge/SmartBatterySettings.java b/src/com/android/settings/fuelgauge/SmartBatterySettings.java
index 5faaef4..52c0cef 100644
--- a/src/com/android/settings/fuelgauge/SmartBatterySettings.java
+++ b/src/com/android/settings/fuelgauge/SmartBatterySettings.java
@@ -19,9 +19,11 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.provider.SearchIndexableResource;
+import android.support.v14.preference.PreferenceFragment;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
+import com.android.settings.SettingsActivity;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -63,14 +65,20 @@
 
     @Override
     protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
-        return buildPreferenceControllers(context);
+        return buildPreferenceControllers(context, (SettingsActivity) getActivity(), this);
     }
 
     private static List<AbstractPreferenceController> buildPreferenceControllers(
-            Context context) {
+            Context context, SettingsActivity settingsActivity, PreferenceFragment fragment) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         controllers.add(new SmartBatteryPreferenceController(context));
-        controllers.add(new RestrictAppPreferenceController(context));
+        if (settingsActivity != null && fragment != null) {
+            controllers.add(
+                    new RestrictAppPreferenceController(settingsActivity, fragment));
+        } else {
+            controllers.add(new RestrictAppPreferenceController(context));
+        }
+
         return controllers;
     }
 
@@ -92,7 +100,7 @@
                 @Override
                 public List<AbstractPreferenceController> getPreferenceControllers(
                         Context context) {
-                    return buildPreferenceControllers(context);
+                    return buildPreferenceControllers(context, null, null);
                 }
             };
 }
diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java
index 3dde95e..39d51dc0 100644
--- a/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java
+++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java
@@ -24,6 +24,7 @@
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
 import com.android.settings.fuelgauge.anomaly.action.ForceStopAction;
 import com.android.settings.fuelgauge.anomaly.action.LocationCheckAction;
@@ -32,7 +33,6 @@
 import com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector;
 import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
 import com.android.settings.fuelgauge.anomaly.checker.WakeupAlarmAnomalyDetector;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java b/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java
index d7de5a7..3ee89d1 100644
--- a/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java
+++ b/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java
@@ -20,9 +20,9 @@
 import android.util.Pair;
 
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Abstract class for anomaly action, which is triggered if we need to handle the anomaly
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
index 6af859b..a580db1 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
@@ -19,9 +19,12 @@
 import android.content.Context;
 import android.provider.Settings;
 import android.support.annotation.VisibleForTesting;
+import android.text.format.DateUtils;
 import android.util.KeyValueListParser;
 import android.util.Log;
 
+import java.time.Duration;
+
 /**
  * Class to store the policy for battery tips, which comes from
  * {@link Settings.Global}
@@ -34,6 +37,8 @@
     private static final String KEY_BATTERY_SAVER_TIP_ENABLED = "battery_saver_tip_enabled";
     private static final String KEY_HIGH_USAGE_ENABLED = "high_usage_enabled";
     private static final String KEY_HIGH_USAGE_APP_COUNT = "high_usage_app_count";
+    private static final String KEY_HIGH_USAGE_PERIOD_MS = "high_usage_period_ms";
+    private static final String KEY_HIGH_USAGE_BATTERY_DRAINING = "high_usage_battery_draining";
     private static final String KEY_APP_RESTRICTION_ENABLED = "app_restriction_enabled";
     private static final String KEY_REDUCED_BATTERY_ENABLED = "reduced_battery_enabled";
     private static final String KEY_REDUCED_BATTERY_PERCENT = "reduced_battery_percent";
@@ -81,6 +86,24 @@
     public final int highUsageAppCount;
 
     /**
+     * The size of the window(milliseconds) for checking if the device is being heavily used
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_HIGH_USAGE_PERIOD_MS
+     */
+    public final long highUsagePeriodMs;
+
+    /**
+     * The battery draining threshold to detect whether device is heavily used.
+     * If battery drains more than {@link #highUsageBatteryDraining} in last {@link
+     * #highUsagePeriodMs}, treat device as heavily used.
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_HIGH_USAGE_BATTERY_DRAINING
+     */
+    public final int highUsageBatteryDraining;
+
+    /**
      * {@code true} if app restriction tip is enabled
      *
      * @see Settings.Global#BATTERY_TIP_CONSTANTS
@@ -143,6 +166,9 @@
         batterySaverTipEnabled = mParser.getBoolean(KEY_BATTERY_SAVER_TIP_ENABLED, true);
         highUsageEnabled = mParser.getBoolean(KEY_HIGH_USAGE_ENABLED, true);
         highUsageAppCount = mParser.getInt(KEY_HIGH_USAGE_APP_COUNT, 3);
+        highUsagePeriodMs = mParser.getLong(KEY_HIGH_USAGE_PERIOD_MS,
+                Duration.ofHours(2).toMillis());
+        highUsageBatteryDraining = mParser.getInt(KEY_HIGH_USAGE_BATTERY_DRAINING, 25);
         appRestrictionEnabled = mParser.getBoolean(KEY_APP_RESTRICTION_ENABLED, true);
         reducedBatteryEnabled = mParser.getBoolean(KEY_REDUCED_BATTERY_ENABLED, false);
         reducedBatteryPercent = mParser.getInt(KEY_REDUCED_BATTERY_PERCENT, 50);
diff --git a/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParser.java b/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParser.java
new file mode 100644
index 0000000..cc5aed6
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParser.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batterytip;
+
+import android.os.BatteryStats;
+
+import com.android.settings.fuelgauge.BatteryInfo;
+
+/**
+ * DataParser used to go through battery data and detect whether battery is
+ * heavily used.
+ */
+public class HighUsageDataParser implements BatteryInfo.BatteryDataParser {
+    /**
+     * time period to check the battery usage
+     */
+    private final long mTimePeriodMs;
+    /**
+     * treat device as heavily used if battery usage is more than {@code threshold}. 1 means 1%
+     * battery usage.
+     */
+    private int mThreshold;
+    private long mEndTimeMs;
+    private byte mEndBatteryLevel;
+    private byte mLastPeriodBatteryLevel;
+    private int mBatteryDrain;
+
+    public HighUsageDataParser(long timePeriodMs, int threshold) {
+        mTimePeriodMs = timePeriodMs;
+        mThreshold = threshold;
+    }
+
+    @Override
+    public void onParsingStarted(long startTime, long endTime) {
+        mEndTimeMs = endTime;
+    }
+
+    @Override
+    public void onDataPoint(long time, BatteryStats.HistoryItem record) {
+        if (record.currentTime <= mEndTimeMs - mTimePeriodMs) {
+            // Since onDataPoint is invoked sorted by time, so we could use this way to get the
+            // closet battery level 'mTimePeriodMs' time ago.
+            mLastPeriodBatteryLevel = record.batteryLevel;
+        }
+        mEndBatteryLevel = record.batteryLevel;
+    }
+
+    @Override
+    public void onDataGap() {
+        // do nothing
+    }
+
+    @Override
+    public void onParsingDone() {
+        mBatteryDrain = mLastPeriodBatteryLevel - mEndBatteryLevel;
+    }
+
+    /**
+     * Return {@code true} if the battery drain in {@link #mTimePeriodMs} is too much
+     */
+    public boolean isDeviceHeavilyUsed() {
+        return mBatteryDrain > mThreshold;
+    }
+}
+
diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/BatteryTipAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/BatteryTipAction.java
index 1bf08b7..9fa69fd 100644
--- a/src/com/android/settings/fuelgauge/batterytip/actions/BatteryTipAction.java
+++ b/src/com/android/settings/fuelgauge/batterytip/actions/BatteryTipAction.java
@@ -18,7 +18,7 @@
 
 import android.content.Context;
 
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Abstract class for battery tip action, which is triggered if we need to handle the battery tip
diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/SmartBatteryAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/SmartBatteryAction.java
index cbd1581..a19471e 100644
--- a/src/com/android/settings/fuelgauge/batterytip/actions/SmartBatteryAction.java
+++ b/src/com/android/settings/fuelgauge/batterytip/actions/SmartBatteryAction.java
@@ -22,8 +22,8 @@
 
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.fuelgauge.SmartBatterySettings;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 public class SmartBatteryAction extends BatteryTipAction {
     private SettingsActivity mSettingsActivity;
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
index 3c69667..ed3fa04 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
@@ -19,13 +19,14 @@
 import android.content.Context;
 import android.os.BatteryStats;
 import android.support.annotation.VisibleForTesting;
-import android.text.format.DateUtils;
 
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
 import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.BatteryInfo;
+import com.android.settings.fuelgauge.batterytip.HighUsageDataParser;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
 
@@ -43,6 +44,8 @@
     private List<AppInfo> mHighUsageAppList;
     private Context mContext;
     @VisibleForTesting
+    HighUsageDataParser mDataParser;
+    @VisibleForTesting
     BatteryUtils mBatteryUtils;
 
     public HighUsageDetector(Context context, BatteryTipPolicy policy,
@@ -52,32 +55,42 @@
         mBatteryStatsHelper = batteryStatsHelper;
         mHighUsageAppList = new ArrayList<>();
         mBatteryUtils = BatteryUtils.getInstance(context);
+        mDataParser = new HighUsageDataParser(mPolicy.highUsagePeriodMs,
+                mPolicy.highUsageBatteryDraining);
     }
 
     @Override
     public BatteryTip detect() {
         final long screenUsageTimeMs = mBatteryUtils.calculateScreenUsageTime(mBatteryStatsHelper);
-        //TODO(b/70570352): Change it to detect whether battery drops 25% in last 2 hours
-        if (mPolicy.highUsageEnabled && screenUsageTimeMs > DateUtils.HOUR_IN_MILLIS) {
-            final List<BatterySipper> batterySippers = mBatteryStatsHelper.getUsageList();
-            for (int i = 0, size = batterySippers.size(); i < size; i++) {
-                final BatterySipper batterySipper = batterySippers.get(i);
-                if (!mBatteryUtils.shouldHideSipper(batterySipper)) {
-                    final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
-                            BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
-                            BatteryStats.STATS_SINCE_CHARGED);
-                    mHighUsageAppList.add(new AppInfo.Builder()
-                            .setPackageName(mBatteryUtils.getPackageName(batterySipper.getUid()))
-                            .setScreenOnTimeMs(foregroundTimeMs)
-                            .build());
+        if (mPolicy.highUsageEnabled) {
+            parseBatteryData();
+            if (mDataParser.isDeviceHeavilyUsed()) {
+                final List<BatterySipper> batterySippers = mBatteryStatsHelper.getUsageList();
+                for (int i = 0, size = batterySippers.size(); i < size; i++) {
+                    final BatterySipper batterySipper = batterySippers.get(i);
+                    if (!mBatteryUtils.shouldHideSipper(batterySipper)) {
+                        final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
+                                BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
+                                BatteryStats.STATS_SINCE_CHARGED);
+                        mHighUsageAppList.add(new AppInfo.Builder()
+                                .setPackageName(
+                                        mBatteryUtils.getPackageName(batterySipper.getUid()))
+                                .setScreenOnTimeMs(foregroundTimeMs)
+                                .build());
+                    }
                 }
-            }
 
-            mHighUsageAppList = mHighUsageAppList.subList(0,
-                    Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size()));
-            Collections.sort(mHighUsageAppList, Collections.reverseOrder());
+                mHighUsageAppList = mHighUsageAppList.subList(0,
+                        Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size()));
+                Collections.sort(mHighUsageAppList, Collections.reverseOrder());
+            }
         }
 
         return new HighUsageTip(screenUsageTimeMs, mHighUsageAppList);
     }
+
+    @VisibleForTesting
+    void parseBatteryData() {
+        BatteryInfo.parse(mBatteryStatsHelper.getStats(), mDataParser);
+    }
 }
diff --git a/src/com/android/settings/inputmethod/UserDictionarySettings.java b/src/com/android/settings/inputmethod/UserDictionarySettings.java
index 3bbc581..9680af1 100644
--- a/src/com/android/settings/inputmethod/UserDictionarySettings.java
+++ b/src/com/android/settings/inputmethod/UserDictionarySettings.java
@@ -42,11 +42,10 @@
 import android.widget.TextView;
 
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
-import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
+import com.android.settings.core.instrumentation.Instrumentable;
+import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
 
 public class UserDictionarySettings extends ListFragment implements Instrumentable,
         LoaderManager.LoaderCallbacks<Cursor> {
@@ -60,7 +59,8 @@
     private static final int OPTIONS_MENU_ADD = Menu.FIRST;
     private static final int LOADER_ID = 1;
 
-    private VisibilityLoggerMixin mVisibilityLoggerMixin;
+    private final VisibilityLoggerMixin mVisibilityLoggerMixin =
+            new VisibilityLoggerMixin(getMetricsCategory());
 
     private Cursor mCursor;
     private String mLocale;
@@ -71,12 +71,15 @@
     }
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mVisibilityLoggerMixin.onAttach(context);
+    }
+
+    @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(),
-                FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider());
-
         final Intent intent = getActivity().getIntent();
         final String localeFromIntent =
                 null == intent ? null : intent.getStringExtra("locale");
diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java
index 0b77179..17cf211 100644
--- a/src/com/android/settings/network/AirplaneModePreferenceController.java
+++ b/src/com/android/settings/network/AirplaneModePreferenceController.java
@@ -28,10 +28,10 @@
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.settings.AirplaneModeEnabler;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.R;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
 import com.android.settingslib.core.lifecycle.events.OnResume;
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index 74c1910..4b1da31 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -31,13 +31,13 @@
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.network.MobilePlanPreferenceController.MobilePlanPreferenceHost;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import java.util.ArrayList;
diff --git a/src/com/android/settings/notification/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/AbstractZenModePreferenceController.java
index 9180791..81ceca1 100644
--- a/src/com/android/settings/notification/AbstractZenModePreferenceController.java
+++ b/src/com/android/settings/notification/AbstractZenModePreferenceController.java
@@ -34,9 +34,9 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
diff --git a/src/com/android/settings/notification/ConfigureNotificationSettings.java b/src/com/android/settings/notification/ConfigureNotificationSettings.java
index 2533466..7cfa124 100644
--- a/src/com/android/settings/notification/ConfigureNotificationSettings.java
+++ b/src/com/android/settings/notification/ConfigureNotificationSettings.java
@@ -17,6 +17,8 @@
 package com.android.settings.notification;
 
 import android.app.Activity;
+import android.app.Application;
+import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
@@ -77,11 +79,18 @@
 
     @Override
     protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
-        return buildPreferenceControllers(context, getLifecycle());
+        final Activity activity = getActivity();
+        final Application app;
+        if (activity != null) {
+            app = activity.getApplication();
+        } else {
+            app = null;
+        }
+        return buildPreferenceControllers(context, getLifecycle(), app, this);
     }
 
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
-            Lifecycle lifecycle) {
+            Lifecycle lifecycle, Application app, Fragment host) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         final BadgingNotificationPreferenceController badgeController =
                 new BadgingNotificationPreferenceController(context);
@@ -96,6 +105,8 @@
             lifecycle.addObserver(pulseController);
             lifecycle.addObserver(lockScreenNotificationController);
         }
+        controllers.add(new RecentNotifyingAppsPreferenceController(
+                context, new NotificationBackend(), app, host));
         controllers.add(new SwipeToNotificationPreferenceController(context, lifecycle,
                 KEY_SWIPE_DOWN));
         controllers.add(badgeController);
@@ -167,7 +178,7 @@
                 @Override
                 public List<AbstractPreferenceController> getPreferenceControllers(
                         Context context) {
-                    return buildPreferenceControllers(context, null);
+                    return buildPreferenceControllers(context, null, null, null);
                 }
 
                 @Override
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 4de528e..e047efa 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -27,12 +27,16 @@
 import android.graphics.drawable.Drawable;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.service.notification.NotifyingApp;
 import android.util.IconDrawableFactory;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.Utils;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class NotificationBackend {
     private static final String TAG = "NotificationBackend";
 
@@ -185,7 +189,6 @@
         }
     }
 
-
     public int getDeletedChannelCount(String pkg, int uid) {
         try {
             return sINM.getDeletedChannelCount(pkg, uid);
@@ -204,6 +207,15 @@
         }
     }
 
+    public List<NotifyingApp> getRecentApps() {
+        try {
+            return sINM.getRecentNotifyingAppsForUser(UserHandle.myUserId()).getList();
+        } catch (Exception e) {
+            Log.w(TAG, "Error calling NoMan", e);
+            return new ArrayList<>();
+        }
+    }
+
     static class Row {
         public String section;
     }
diff --git a/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java b/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java
new file mode 100644
index 0000000..ef34a9b
--- /dev/null
+++ b/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.Application;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.service.notification.NotifyingApp;
+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.ArrayMap;
+import android.util.ArraySet;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.applications.InstalledAppCounter;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.AppPreference;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This controller displays a list of recently used apps and a "See all" button. If there is
+ * no recently used app, "See all" will be displayed as "Notifications".
+ */
+public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceController
+        implements PreferenceControllerMixin {
+
+    private static final String TAG = "RecentNotisCtrl";
+    private static final String KEY_PREF_CATEGORY = "recent_notifications_category";
+    @VisibleForTesting
+    static final String KEY_DIVIDER = "all_notifications_divider";
+    @VisibleForTesting
+    static final String KEY_SEE_ALL = "all_notifications";
+    private static final int SHOW_RECENT_APP_COUNT = 5;
+    private static final Set<String> SKIP_SYSTEM_PACKAGES = new ArraySet<>();
+
+    private final Fragment mHost;
+    private final PackageManager mPm;
+    private final NotificationBackend mNotificationBackend;
+    private final int mUserId;
+    private final IconDrawableFactory mIconDrawableFactory;
+
+    private List<NotifyingApp> mApps;
+    private final ApplicationsState mApplicationsState;
+
+    private PreferenceCategory mCategory;
+    private Preference mSeeAllPref;
+    private Preference mDivider;
+    private boolean mHasRecentApps;
+
+    static {
+        SKIP_SYSTEM_PACKAGES.addAll(Arrays.asList(
+                "android",
+                "com.android.phone",
+                "com.android.settings",
+                "com.android.systemui",
+                "com.android.providers.calendar",
+                "com.android.providers.media"
+        ));
+    }
+
+    public RecentNotifyingAppsPreferenceController(Context context, NotificationBackend backend,
+            Application app, Fragment host) {
+        this(context, backend, app == null ? null : ApplicationsState.getInstance(app), host);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+    RecentNotifyingAppsPreferenceController(Context context, NotificationBackend backend,
+            ApplicationsState appState, Fragment host) {
+        super(context);
+        mIconDrawableFactory = IconDrawableFactory.newInstance(context);
+        mUserId = UserHandle.myUserId();
+        mPm = context.getPackageManager();
+        mHost = host;
+        mApplicationsState = appState;
+        mNotificationBackend = backend;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_PREF_CATEGORY;
+    }
+
+    @Override
+    public void updateNonIndexableKeys(List<String> keys) {
+        PreferenceControllerMixin.super.updateNonIndexableKeys(keys);
+        // Don't index category name into search. It's not actionable.
+        keys.add(KEY_PREF_CATEGORY);
+        keys.add(KEY_DIVIDER);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        mCategory = (PreferenceCategory) screen.findPreference(getPreferenceKey());
+        mSeeAllPref = screen.findPreference(KEY_SEE_ALL);
+        mDivider = screen.findPreference(KEY_DIVIDER);
+        super.displayPreference(screen);
+        refreshUi(mCategory.getContext());
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        refreshUi(mCategory.getContext());
+        // Show total number of installed apps as See all's summary.
+        new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
+                new PackageManagerWrapper(mContext.getPackageManager())) {
+            @Override
+            protected void onCountComplete(int num) {
+                if (mHasRecentApps) {
+                    mSeeAllPref.setTitle(mContext.getString(R.string.see_all_apps_title, num));
+                } else {
+                    mSeeAllPref.setSummary(mContext.getString(R.string.apps_summary, num));
+                }
+            }
+        }.execute();
+
+    }
+
+    @VisibleForTesting
+    void refreshUi(Context prefContext) {
+        reloadData();
+        final List<NotifyingApp> recentApps = getDisplayableRecentAppList();
+        if (recentApps != null && !recentApps.isEmpty()) {
+            mHasRecentApps = true;
+            displayRecentApps(prefContext, recentApps);
+        } else {
+            mHasRecentApps = false;
+            displayOnlyAllAppsLink();
+        }
+    }
+
+    @VisibleForTesting
+    void reloadData() {
+        mApps = mNotificationBackend.getRecentApps();
+    }
+
+    private void displayOnlyAllAppsLink() {
+        mCategory.setTitle(null);
+        mDivider.setVisible(false);
+        mSeeAllPref.setTitle(R.string.notifications_title);
+        mSeeAllPref.setIcon(null);
+        int prefCount = mCategory.getPreferenceCount();
+        for (int i = prefCount - 1; i >= 0; i--) {
+            final Preference pref = mCategory.getPreference(i);
+            if (!TextUtils.equals(pref.getKey(), KEY_SEE_ALL)) {
+                mCategory.removePreference(pref);
+            }
+        }
+    }
+
+    private void displayRecentApps(Context prefContext, List<NotifyingApp> recentApps) {
+        mCategory.setTitle(R.string.recent_notifications);
+        mDivider.setVisible(true);
+        mSeeAllPref.setSummary(null);
+        mSeeAllPref.setIcon(R.drawable.ic_chevron_right_24dp);
+
+        // Rebind prefs/avoid adding new prefs if possible. Adding/removing prefs causes jank.
+        // Build a cached preference pool
+        final Map<String, Preference> appPreferences = new ArrayMap<>();
+        int prefCount = mCategory.getPreferenceCount();
+        for (int i = 0; i < prefCount; i++) {
+            final Preference pref = mCategory.getPreference(i);
+            final String key = pref.getKey();
+            if (!TextUtils.equals(key, KEY_SEE_ALL)) {
+                appPreferences.put(key, pref);
+            }
+        }
+        final int recentAppsCount = recentApps.size();
+        for (int i = 0; i < recentAppsCount; i++) {
+            final NotifyingApp app = recentApps.get(i);
+            // Bind recent apps to existing prefs if possible, or create a new pref.
+            final String pkgName = app.getPackage();
+            final ApplicationsState.AppEntry appEntry =
+                    mApplicationsState.getEntry(app.getPackage(), mUserId);
+            if (appEntry == null) {
+                continue;
+            }
+
+            boolean rebindPref = true;
+            Preference pref = appPreferences.remove(pkgName);
+            if (pref == null) {
+                pref = new AppPreference(prefContext);
+                rebindPref = false;
+            }
+            pref.setKey(pkgName);
+            pref.setTitle(appEntry.label);
+            pref.setIcon(mIconDrawableFactory.getBadgedIcon(appEntry.info));
+            pref.setSummary(Utils.formatRelativeTime(mContext,
+                    System.currentTimeMillis() - app.getLastNotified(), false));
+            pref.setOrder(i);
+            pref.setOnPreferenceClickListener(preference -> {
+                AppInfoBase.startAppInfoFragment(AppNotificationSettings.class,
+                        R.string.notifications_title, pkgName, appEntry.info.uid, mHost,
+                        1001 /*RequestCode */,
+                        MetricsProto.MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS);
+                    return true;
+            });
+            if (!rebindPref) {
+                mCategory.addPreference(pref);
+            }
+        }
+        // Remove unused prefs from pref cache pool
+        for (Preference unusedPrefs : appPreferences.values()) {
+            mCategory.removePreference(unusedPrefs);
+        }
+    }
+
+    private List<NotifyingApp> getDisplayableRecentAppList() {
+        Collections.sort(mApps);
+        List<NotifyingApp> displayableApps = new ArrayList<>(SHOW_RECENT_APP_COUNT);
+        int count = 0;
+        for (NotifyingApp app : mApps) {
+            final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry(
+                    app.getPackage(), mUserId);
+            if (appEntry == null) {
+                continue;
+            }
+            if (!shouldIncludePkgInRecents(app.getPackage())) {
+                continue;
+            }
+            displayableApps.add(app);
+            count++;
+            if (count >= SHOW_RECENT_APP_COUNT) {
+                break;
+            }
+        }
+        return displayableApps;
+    }
+
+
+    /**
+     * Whether or not the app should be included in recent list.
+     */
+    private boolean shouldIncludePkgInRecents(String pkgName) {
+         if (SKIP_SYSTEM_PACKAGES.contains(pkgName)) {
+            Log.d(TAG, "System package, skipping " + pkgName);
+            return false;
+        }
+        final Intent launchIntent = new Intent().addCategory(Intent.CATEGORY_LAUNCHER)
+                .setPackage(pkgName);
+
+        if (mPm.resolveActivity(launchIntent, 0) == null) {
+            // Not visible on launcher -> likely not a user visible app, skip if non-instant.
+            final ApplicationsState.AppEntry appEntry =
+                    mApplicationsState.getEntry(pkgName, mUserId);
+            if (!AppUtils.isInstant(appEntry.info)) {
+                Log.d(TAG, "Not a user visible or instant app, skipping " + pkgName);
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenRulePreference.java b/src/com/android/settings/notification/ZenRulePreference.java
index fee390f..7193873 100644
--- a/src/com/android/settings/notification/ZenRulePreference.java
+++ b/src/com/android/settings/notification/ZenRulePreference.java
@@ -30,10 +30,10 @@
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.utils.ManagedServiceSettings;
 import com.android.settings.utils.ZenServiceListing;
 import com.android.settingslib.TwoTargetPreference;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import java.util.Map;
 
@@ -145,4 +145,4 @@
                 ? mContext.getResources().getString(R.string.switch_off_text)
                 : mContext.getResources().getString(R.string.switch_on_text);
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index 0805766..71fbaa4 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -21,9 +21,11 @@
 import android.util.Log;
 
 import com.android.settings.R;
+import com.android.settings.accounts.AccountFeatureProvider;
 import com.android.settings.applications.ApplicationFeatureProvider;
 import com.android.settings.bluetooth.BluetoothFeatureProvider;
 import com.android.settings.connecteddevice.SmsMirroringFeatureProvider;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
 import com.android.settings.datausage.DataPlanFeatureProvider;
@@ -35,7 +37,6 @@
 import com.android.settings.search.SearchFeatureProvider;
 import com.android.settings.slices.SlicesFeatureProvider;
 import com.android.settings.users.UserFeatureProvider;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Abstract class for creating feature controllers. Allows OEM implementations to define their own
@@ -109,6 +110,8 @@
 
     public abstract SlicesFeatureProvider getSlicesFeatureProvider();
 
+    public abstract AccountFeatureProvider getAccountFeatureProvider();
+
     public static final class FactoryNotFoundException extends RuntimeException {
         public FactoryNotFoundException(Throwable throwable) {
             super("Unable to create factory. Did you misconfigure Proguard?", throwable);
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index f817d4b..45dc238 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -23,12 +23,15 @@
 import android.os.UserManager;
 import android.support.annotation.Keep;
 
+import com.android.settings.accounts.AccountFeatureProvider;
+import com.android.settings.accounts.AccountFeatureProviderImpl;
 import com.android.settings.applications.ApplicationFeatureProvider;
 import com.android.settings.applications.ApplicationFeatureProviderImpl;
 import com.android.settings.bluetooth.BluetoothFeatureProvider;
 import com.android.settings.bluetooth.BluetoothFeatureProviderImpl;
 import com.android.settings.connecteddevice.SmsMirroringFeatureProvider;
 import com.android.settings.connecteddevice.SmsMirroringFeatureProviderImpl;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.DashboardFeatureProviderImpl;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
@@ -54,7 +57,6 @@
 import com.android.settings.wrapper.ConnectivityManagerWrapper;
 import com.android.settings.wrapper.DevicePolicyManagerWrapper;
 import com.android.settings.wrapper.IPackageManagerWrapper;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.wrapper.PackageManagerWrapper;
 
 /**
@@ -78,6 +80,7 @@
     private DataPlanFeatureProvider mDataPlanFeatureProvider;
     private SmsMirroringFeatureProvider mSmsMirroringFeatureProvider;
     private SlicesFeatureProvider mSlicesFeatureProvider;
+    private AccountFeatureProvider mAccountFeatureProvider;
 
     @Override
     public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -219,4 +222,12 @@
         }
         return mSlicesFeatureProvider;
     }
+
+    @Override
+    public AccountFeatureProvider getAccountFeatureProvider() {
+        if (mAccountFeatureProvider == null) {
+            mAccountFeatureProvider = new AccountFeatureProviderImpl();
+        }
+        return mAccountFeatureProvider;
+    }
 }
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index 878bbfd..cbe49f8 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -48,6 +48,11 @@
 
     DatabaseIndexingManager getIndexingManager(Context context);
 
+    /**
+     * @return a {@link SearchIndexableResources} to be used for indexing search results.
+     */
+    SearchIndexableResources getSearchIndexableResources();
+
     default String getSettingsIntelligencePkgName() {
         return "com.android.settings.intelligence";
     }
diff --git a/src/com/android/settings/search/SearchFeatureProviderImpl.java b/src/com/android/settings/search/SearchFeatureProviderImpl.java
index ccd4ff1..78c47ed 100644
--- a/src/com/android/settings/search/SearchFeatureProviderImpl.java
+++ b/src/com/android/settings/search/SearchFeatureProviderImpl.java
@@ -36,6 +36,7 @@
 
     private static final String METRICS_ACTION_SETTINGS_INDEX = "search_synchronous_indexing";
     private DatabaseIndexingManager mDatabaseIndexingManager;
+    private SearchIndexableResources mSearchIndexableResources;
 
     @Override
     public void verifyLaunchSearchResultPageCaller(Context context, ComponentName caller) {
@@ -72,6 +73,14 @@
                 .histogram(context, METRICS_ACTION_SETTINGS_INDEX, indexingTime);
     }
 
+    @Override
+    public SearchIndexableResources getSearchIndexableResources() {
+        if (mSearchIndexableResources == null) {
+            mSearchIndexableResources = new SearchIndexableResourcesImpl();
+        }
+        return mSearchIndexableResources;
+    }
+
     protected boolean isSignatureWhitelisted(Context context, String callerPackage) {
         return false;
     }
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index b0159cf..5a0a131 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,170 +16,14 @@
 
 package com.android.settings.search;
 
-import android.support.annotation.VisibleForTesting;
-
-import com.android.settings.DateTimeSettings;
-import com.android.settings.DisplaySettings;
-import com.android.settings.LegalSettings;
-import com.android.settings.accessibility.AccessibilitySettings;
-import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
-import com.android.settings.accessibility.MagnificationPreferenceFragment;
-import com.android.settings.accounts.UserAndAccountDashboardFragment;
-import com.android.settings.applications.AppAndNotificationDashboardFragment;
-import com.android.settings.applications.DefaultAppSettings;
-import com.android.settings.applications.SpecialAccessSettings;
-import com.android.settings.applications.assist.ManageAssist;
-import com.android.settings.backup.BackupSettingsActivity;
-import com.android.settings.backup.BackupSettingsFragment;
-import com.android.settings.bluetooth.BluetoothSettings;
-import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
-import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
-import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
-import com.android.settings.datausage.DataUsageSummary;
-import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
-import com.android.settings.development.DevelopmentSettingsDashboardFragment;
-import com.android.settings.deviceinfo.DeviceInfoSettings;
-import com.android.settings.deviceinfo.StorageDashboardFragment;
-import com.android.settings.deviceinfo.StorageSettings;
-import com.android.settings.display.AmbientDisplaySettings;
-import com.android.settings.display.NightDisplaySettings;
-import com.android.settings.display.ScreenZoomSettings;
-import com.android.settings.dream.DreamSettings;
-import com.android.settings.enterprise.EnterprisePrivacySettings;
-import com.android.settings.fuelgauge.BatterySaverSettings;
-import com.android.settings.fuelgauge.PowerUsageAdvanced;
-import com.android.settings.fuelgauge.PowerUsageSummary;
-import com.android.settings.fuelgauge.SmartBatterySettings;
-import com.android.settings.gestures.AssistGestureSettings;
-import com.android.settings.gestures.DoubleTapPowerSettings;
-import com.android.settings.gestures.DoubleTapScreenSettings;
-import com.android.settings.gestures.DoubleTwistGestureSettings;
-import com.android.settings.gestures.GestureSettings;
-import com.android.settings.gestures.PickupGestureSettings;
-import com.android.settings.gestures.SwipeToNotificationSettings;
-import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
-import com.android.settings.inputmethod.PhysicalKeyboardFragment;
-import com.android.settings.inputmethod.VirtualKeyboardFragment;
-import com.android.settings.language.LanguageAndInputSettings;
-import com.android.settings.location.LocationMode;
-import com.android.settings.location.LocationSettings;
-import com.android.settings.location.ScanningSettings;
-import com.android.settings.network.NetworkDashboardFragment;
-import com.android.settings.nfc.PaymentSettings;
-import com.android.settings.notification.ConfigureNotificationSettings;
-import com.android.settings.notification.SoundSettings;
-import com.android.settings.notification.ZenModeAutomationSettings;
-import com.android.settings.notification.ZenModeBehaviorSettings;
-import com.android.settings.notification.ZenModeSettings;
-import com.android.settings.print.PrintSettingsFragment;
-import com.android.settings.security.EncryptionAndCredential;
-import com.android.settings.security.LockscreenDashboardFragment;
-import com.android.settings.security.ScreenPinningSettings;
-import com.android.settings.security.SecuritySettingsV2;
-import com.android.settings.security.screenlock.ScreenLockSettings;
-import com.android.settings.sim.SimSettings;
-import com.android.settings.support.SupportDashboardActivity;
-import com.android.settings.system.ResetDashboardFragment;
-import com.android.settings.system.SystemDashboardFragment;
-import com.android.settings.tts.TextToSpeechSettings;
-import com.android.settings.tts.TtsEnginePreferenceFragment;
-import com.android.settings.users.UserSettings;
-import com.android.settings.wallpaper.WallpaperTypeSettings;
-import com.android.settings.wfd.WifiDisplaySettings;
-import com.android.settings.wifi.ConfigureWifiSettings;
-import com.android.settings.wifi.WifiSettings;
-
 import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
 
-public final class SearchIndexableResources {
+public interface SearchIndexableResources {
 
-    @VisibleForTesting
-    static final Set<Class> sProviders = new HashSet<>();
-
-    @VisibleForTesting
-    static void addIndex(Class indexClass) {
-        sProviders.add(indexClass);
-    }
-
-    static {
-        addIndex(WifiSettings.class);
-        addIndex(NetworkDashboardFragment.class);
-        addIndex(ConfigureWifiSettings.class);
-        addIndex(BluetoothSettings.class);
-        addIndex(SimSettings.class);
-        addIndex(DataUsageSummary.class);
-        addIndex(ScreenZoomSettings.class);
-        addIndex(DisplaySettings.class);
-        addIndex(AmbientDisplaySettings.class);
-        addIndex(WallpaperTypeSettings.class);
-        addIndex(AppAndNotificationDashboardFragment.class);
-        addIndex(SoundSettings.class);
-        addIndex(ZenModeSettings.class);
-        addIndex(StorageSettings.class);
-        addIndex(PowerUsageAdvanced.class);
-        addIndex(DefaultAppSettings.class);
-        addIndex(ManageAssist.class);
-        addIndex(SpecialAccessSettings.class);
-        addIndex(UserSettings.class);
-        addIndex(AssistGestureSettings.class);
-        addIndex(PickupGestureSettings.class);
-        addIndex(DoubleTapScreenSettings.class);
-        addIndex(DoubleTapPowerSettings.class);
-        addIndex(DoubleTwistGestureSettings.class);
-        addIndex(SwipeToNotificationSettings.class);
-        addIndex(GestureSettings.class);
-        addIndex(LanguageAndInputSettings.class);
-        addIndex(LocationSettings.class);
-        addIndex(LocationMode.class);
-        addIndex(ScanningSettings.class);
-        addIndex(SecuritySettingsV2.class);
-        addIndex(ScreenLockSettings.class);
-        addIndex(EncryptionAndCredential.class);
-        addIndex(ScreenPinningSettings.class);
-        addIndex(UserAndAccountDashboardFragment.class);
-        addIndex(VirtualKeyboardFragment.class);
-        addIndex(AvailableVirtualKeyboardFragment.class);
-        addIndex(PhysicalKeyboardFragment.class);
-        addIndex(BackupSettingsActivity.class);
-        addIndex(BackupSettingsFragment.class);
-        addIndex(DateTimeSettings.class);
-        addIndex(AccessibilitySettings.class);
-        addIndex(PrintSettingsFragment.class);
-        addIndex(DevelopmentSettingsDashboardFragment.class);
-        addIndex(DeviceInfoSettings.class);
-        addIndex(LegalSettings.class);
-        addIndex(SystemDashboardFragment.class);
-        addIndex(ResetDashboardFragment.class);
-        addIndex(StorageDashboardFragment.class);
-        addIndex(ConnectedDeviceDashboardFragment.class);
-        addIndex(ConnectedDeviceDashboardFragmentOld.class);
-        addIndex(AdvancedConnectedDeviceDashboardFragment.class);
-        addIndex(EnterprisePrivacySettings.class);
-        addIndex(PaymentSettings.class);
-        addIndex(TextToSpeechSettings.class);
-        addIndex(TtsEnginePreferenceFragment.class);
-        addIndex(MagnificationPreferenceFragment.class);
-        addIndex(AccessibilityShortcutPreferenceFragment.class);
-        addIndex(DreamSettings.class);
-        addIndex(SupportDashboardActivity.class);
-        addIndex(AutomaticStorageManagerSettings.class);
-        addIndex(ConfigureNotificationSettings.class);
-        addIndex(PowerUsageSummary.class);
-        addIndex(BatterySaverSettings.class);
-        addIndex(LockscreenDashboardFragment.class);
-        addIndex(WifiDisplaySettings.class);
-        addIndex(ZenModeBehaviorSettings.class);
-        addIndex(ZenModeAutomationSettings.class);
-        addIndex(NightDisplaySettings.class);
-        addIndex(SmartBatterySettings.class);
-    }
-
-    private SearchIndexableResources() {
-    }
-
-    public static Collection<Class> providerValues() {
-        return sProviders;
-    }
-}
\ No newline at end of file
+    /**
+     * Returns a collection of classes that should be indexed for search.
+     *
+     * Each class should have the SEARCH_INDEX_DATA_PROVIDER public static member.
+     */
+    Collection<Class> getProviderValues();
+}
diff --git a/src/com/android/settings/search/SearchIndexableResourcesImpl.java b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
new file mode 100644
index 0000000..4067e6b
--- /dev/null
+++ b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2014 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.search;
+
+import android.support.annotation.VisibleForTesting;
+
+import com.android.settings.DateTimeSettings;
+import com.android.settings.DisplaySettings;
+import com.android.settings.LegalSettings;
+import com.android.settings.MeCardFragment;
+import com.android.settings.accessibility.AccessibilitySettings;
+import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
+import com.android.settings.accessibility.MagnificationPreferenceFragment;
+import com.android.settings.accounts.UserAndAccountDashboardFragment;
+import com.android.settings.applications.AppAndNotificationDashboardFragment;
+import com.android.settings.applications.DefaultAppSettings;
+import com.android.settings.applications.SpecialAccessSettings;
+import com.android.settings.applications.assist.ManageAssist;
+import com.android.settings.backup.BackupSettingsActivity;
+import com.android.settings.backup.BackupSettingsFragment;
+import com.android.settings.bluetooth.BluetoothSettings;
+import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
+import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
+import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
+import com.android.settings.datausage.DataUsageSummary;
+import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
+import com.android.settings.development.DevelopmentSettingsDashboardFragment;
+import com.android.settings.deviceinfo.DeviceInfoSettings;
+import com.android.settings.deviceinfo.StorageDashboardFragment;
+import com.android.settings.deviceinfo.StorageSettings;
+import com.android.settings.display.AmbientDisplaySettings;
+import com.android.settings.display.NightDisplaySettings;
+import com.android.settings.display.ScreenZoomSettings;
+import com.android.settings.dream.DreamSettings;
+import com.android.settings.enterprise.EnterprisePrivacySettings;
+import com.android.settings.fuelgauge.BatterySaverSettings;
+import com.android.settings.fuelgauge.PowerUsageAdvanced;
+import com.android.settings.fuelgauge.PowerUsageSummary;
+import com.android.settings.fuelgauge.SmartBatterySettings;
+import com.android.settings.gestures.AssistGestureSettings;
+import com.android.settings.gestures.DoubleTapPowerSettings;
+import com.android.settings.gestures.DoubleTapScreenSettings;
+import com.android.settings.gestures.DoubleTwistGestureSettings;
+import com.android.settings.gestures.GestureSettings;
+import com.android.settings.gestures.PickupGestureSettings;
+import com.android.settings.gestures.SwipeToNotificationSettings;
+import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
+import com.android.settings.inputmethod.PhysicalKeyboardFragment;
+import com.android.settings.inputmethod.VirtualKeyboardFragment;
+import com.android.settings.language.LanguageAndInputSettings;
+import com.android.settings.location.LocationMode;
+import com.android.settings.location.LocationSettings;
+import com.android.settings.location.ScanningSettings;
+import com.android.settings.network.NetworkDashboardFragment;
+import com.android.settings.nfc.PaymentSettings;
+import com.android.settings.notification.ConfigureNotificationSettings;
+import com.android.settings.notification.SoundSettings;
+import com.android.settings.notification.ZenModeAutomationSettings;
+import com.android.settings.notification.ZenModeBehaviorSettings;
+import com.android.settings.notification.ZenModeSettings;
+import com.android.settings.print.PrintSettingsFragment;
+import com.android.settings.security.EncryptionAndCredential;
+import com.android.settings.security.LockscreenDashboardFragment;
+import com.android.settings.security.ScreenPinningSettings;
+import com.android.settings.security.SecuritySettingsV2;
+import com.android.settings.security.screenlock.ScreenLockSettings;
+import com.android.settings.sim.SimSettings;
+import com.android.settings.support.SupportDashboardActivity;
+import com.android.settings.system.ResetDashboardFragment;
+import com.android.settings.system.SystemDashboardFragment;
+import com.android.settings.tts.TextToSpeechSettings;
+import com.android.settings.tts.TtsEnginePreferenceFragment;
+import com.android.settings.users.UserSettings;
+import com.android.settings.wallpaper.WallpaperTypeSettings;
+import com.android.settings.wfd.WifiDisplaySettings;
+import com.android.settings.wifi.ConfigureWifiSettings;
+import com.android.settings.wifi.WifiSettings;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public class SearchIndexableResourcesImpl implements SearchIndexableResources {
+
+    private final Set<Class> sProviders = new HashSet<>();
+
+    @VisibleForTesting
+    void addIndex(Class indexClass) {
+        sProviders.add(indexClass);
+    }
+
+    public SearchIndexableResourcesImpl() {
+        addIndex(WifiSettings.class);
+        addIndex(NetworkDashboardFragment.class);
+        addIndex(ConfigureWifiSettings.class);
+        addIndex(BluetoothSettings.class);
+        addIndex(SimSettings.class);
+        addIndex(DataUsageSummary.class);
+        addIndex(ScreenZoomSettings.class);
+        addIndex(DisplaySettings.class);
+        addIndex(AmbientDisplaySettings.class);
+        addIndex(WallpaperTypeSettings.class);
+        addIndex(AppAndNotificationDashboardFragment.class);
+        addIndex(SoundSettings.class);
+        addIndex(ZenModeSettings.class);
+        addIndex(StorageSettings.class);
+        addIndex(PowerUsageAdvanced.class);
+        addIndex(DefaultAppSettings.class);
+        addIndex(ManageAssist.class);
+        addIndex(SpecialAccessSettings.class);
+        addIndex(UserSettings.class);
+        addIndex(AssistGestureSettings.class);
+        addIndex(PickupGestureSettings.class);
+        addIndex(DoubleTapScreenSettings.class);
+        addIndex(DoubleTapPowerSettings.class);
+        addIndex(DoubleTwistGestureSettings.class);
+        addIndex(SwipeToNotificationSettings.class);
+        addIndex(GestureSettings.class);
+        addIndex(LanguageAndInputSettings.class);
+        addIndex(LocationSettings.class);
+        addIndex(LocationMode.class);
+        addIndex(ScanningSettings.class);
+        addIndex(SecuritySettingsV2.class);
+        addIndex(ScreenLockSettings.class);
+        addIndex(EncryptionAndCredential.class);
+        addIndex(ScreenPinningSettings.class);
+        addIndex(UserAndAccountDashboardFragment.class);
+        addIndex(VirtualKeyboardFragment.class);
+        addIndex(AvailableVirtualKeyboardFragment.class);
+        addIndex(PhysicalKeyboardFragment.class);
+        addIndex(BackupSettingsActivity.class);
+        addIndex(BackupSettingsFragment.class);
+        addIndex(DateTimeSettings.class);
+        addIndex(AccessibilitySettings.class);
+        addIndex(PrintSettingsFragment.class);
+        addIndex(DevelopmentSettingsDashboardFragment.class);
+        addIndex(DeviceInfoSettings.class);
+        addIndex(LegalSettings.class);
+        addIndex(SystemDashboardFragment.class);
+        addIndex(ResetDashboardFragment.class);
+        addIndex(StorageDashboardFragment.class);
+        addIndex(ConnectedDeviceDashboardFragment.class);
+        addIndex(ConnectedDeviceDashboardFragmentOld.class);
+        addIndex(AdvancedConnectedDeviceDashboardFragment.class);
+        addIndex(EnterprisePrivacySettings.class);
+        addIndex(PaymentSettings.class);
+        addIndex(TextToSpeechSettings.class);
+        addIndex(TtsEnginePreferenceFragment.class);
+        addIndex(MagnificationPreferenceFragment.class);
+        addIndex(AccessibilityShortcutPreferenceFragment.class);
+        addIndex(DreamSettings.class);
+        addIndex(SupportDashboardActivity.class);
+        addIndex(AutomaticStorageManagerSettings.class);
+        addIndex(ConfigureNotificationSettings.class);
+        addIndex(PowerUsageSummary.class);
+        addIndex(BatterySaverSettings.class);
+        addIndex(LockscreenDashboardFragment.class);
+        addIndex(WifiDisplaySettings.class);
+        addIndex(ZenModeBehaviorSettings.class);
+        addIndex(ZenModeAutomationSettings.class);
+        addIndex(NightDisplaySettings.class);
+        addIndex(SmartBatterySettings.class);
+        addIndex(MeCardFragment.class);
+    }
+
+    @Override
+    public Collection<Class> getProviderValues() {
+        return sProviders;
+    }
+}
diff --git a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
index 0c98b9c..3ef1b85 100644
--- a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
+++ b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
@@ -175,7 +175,8 @@
     }
 
     private List<String> getNonIndexableKeysFromProvider(Context context) {
-        final Collection<Class> values = SearchIndexableResources.providerValues();
+        final Collection<Class> values = FeatureFactory.getFactory(context)
+                .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
         final List<String> nonIndexableKeys = new ArrayList<>();
 
         for (Class<?> clazz : values) {
@@ -209,7 +210,8 @@
     }
 
     private List<SearchIndexableResource> getSearchIndexableResourcesFromProvider(Context context) {
-        Collection<Class> values = SearchIndexableResources.providerValues();
+        Collection<Class> values = FeatureFactory.getFactory(context)
+                .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
         List<SearchIndexableResource> resourceList = new ArrayList<>();
 
         for (Class<?> clazz : values) {
@@ -236,7 +238,8 @@
     }
 
     private List<SearchIndexableRaw> getSearchIndexableRawFromProvider(Context context) {
-        final Collection<Class> values = SearchIndexableResources.providerValues();
+        final Collection<Class> values = FeatureFactory.getFactory(context)
+                .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
         final List<SearchIndexableRaw> rawList = new ArrayList<>();
 
         for (Class<?> clazz : values) {
diff --git a/src/com/android/settings/slices/SliceDataConverter.java b/src/com/android/settings/slices/SliceDataConverter.java
index c10753f..e5a21e4 100644
--- a/src/com/android/settings/slices/SliceDataConverter.java
+++ b/src/com/android/settings/slices/SliceDataConverter.java
@@ -27,9 +27,9 @@
 import android.util.Xml;
 
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.DatabaseIndexingUtils;
 import com.android.settings.search.Indexable.SearchIndexProvider;
-import com.android.settings.search.SearchIndexableResources;
 import com.android.settings.search.XmlParserUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -62,7 +62,8 @@
      * @return a list of {@link SliceData} to be indexed and later referenced as a Slice.
      *
      * The collection works as follows:
-     * - Collects a list of Fragments from {@link SearchIndexableResources}.
+     * - Collects a list of Fragments from
+     * {@link FeatureFactory#getSearchFeatureProvider()}.
      * - From each fragment, grab a {@link SearchIndexProvider}.
      * - For each provider, collect XML resource layout and a list of
      * {@link com.android.settings.core.BasePreferenceController}.
@@ -72,7 +73,8 @@
             return mSliceData;
         }
 
-        final Collection<Class> indexableClasses = SearchIndexableResources.providerValues();
+        final Collection<Class> indexableClasses = FeatureFactory.getFactory(mContext)
+                .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
 
         for (Class clazz : indexableClasses) {
             final String fragmentName = clazz.getName();
diff --git a/src/com/android/settings/widget/SwitchBar.java b/src/com/android/settings/widget/SwitchBar.java
index 3be5eca..749ec0a 100644
--- a/src/com/android/settings/widget/SwitchBar.java
+++ b/src/com/android/settings/widget/SwitchBar.java
@@ -39,9 +39,9 @@
 import android.widget.TextView;
 
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.RestrictedLockUtils;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/src/com/android/settings/wifi/WifiEnabler.java b/src/com/android/settings/wifi/WifiEnabler.java
index c5e79b2..9c43142 100644
--- a/src/com/android/settings/wifi/WifiEnabler.java
+++ b/src/com/android/settings/wifi/WifiEnabler.java
@@ -33,9 +33,9 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.widget.SwitchWidgetController;
 import com.android.settings.wrapper.ConnectivityManagerWrapper;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.WirelessUtils;
diff --git a/src/com/android/settings/wifi/WifiMasterSwitchPreferenceController.java b/src/com/android/settings/wifi/WifiMasterSwitchPreferenceController.java
index 8843d93..de1b030 100644
--- a/src/com/android/settings/wifi/WifiMasterSwitchPreferenceController.java
+++ b/src/com/android/settings/wifi/WifiMasterSwitchPreferenceController.java
@@ -19,12 +19,12 @@
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.R;
 import com.android.settings.widget.SummaryUpdater;
 import com.android.settings.widget.MasterSwitchPreference;
 import com.android.settings.widget.MasterSwitchController;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
 import com.android.settingslib.core.lifecycle.events.OnResume;
diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
index 13ffd5b..82ffc38 100644
--- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
@@ -54,6 +54,7 @@
 import com.android.settings.Utils;
 import com.android.settings.applications.LayoutPreference;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.widget.ActionButtonPreference;
 import com.android.settings.widget.EntityHeaderController;
 import com.android.settings.wifi.WifiDetailPreference;
@@ -62,7 +63,6 @@
 import com.android.settings.wifi.WifiUtils;
 import com.android.settings.wrapper.ConnectivityManagerWrapper;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
@@ -280,7 +280,7 @@
                 .setButton1Text(R.string.forget)
                 .setButton1Positive(false)
                 .setButton1OnClickListener(view -> forgetNetwork())
-                .setButton2Text(R.string.support_sign_in_button_text)
+                .setButton2Text(R.string.wifi_sign_in_button_text)
                 .setButton2Positive(true)
                 .setButton2OnClickListener(view -> signIntoNetwork());
 
diff --git a/src/com/android/settings/HotspotOffReceiver.java b/src/com/android/settings/wifi/tether/HotspotOffReceiver.java
similarity index 97%
rename from src/com/android/settings/HotspotOffReceiver.java
rename to src/com/android/settings/wifi/tether/HotspotOffReceiver.java
index 4083082..fcbf888 100644
--- a/src/com/android/settings/HotspotOffReceiver.java
+++ b/src/com/android/settings/wifi/tether/HotspotOffReceiver.java
@@ -1,5 +1,5 @@
 
-package com.android.settings;
+package com.android.settings.wifi.tether;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
diff --git a/src/com/android/settings/TetherService.java b/src/com/android/settings/wifi/tether/TetherService.java
similarity index 98%
rename from src/com/android/settings/TetherService.java
rename to src/com/android/settings/wifi/tether/TetherService.java
index fce3f27..e491de8 100644
--- a/src/com/android/settings/TetherService.java
+++ b/src/com/android/settings/wifi/tether/TetherService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.settings;
+package com.android.settings.wifi.tether;
 
 import android.app.Activity;
 import android.app.AlarmManager;
@@ -167,11 +167,16 @@
         SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
         prefs.edit().putString(KEY_TETHERS, tethersToString(mCurrentTethers)).commit();
 
+        unregisterReceivers();
         if (DEBUG) Log.d(TAG, "Destroying TetherService");
-        unregisterReceiver(mReceiver);
         super.onDestroy();
     }
 
+    private void unregisterReceivers() {
+        unregisterReceiver(mReceiver);
+        mHotspotReceiver.unregister();
+    }
+
     private void removeTypeAtIndex(int index) {
         mCurrentTethers.remove(index);
         // If we are currently in the middle of a check, we may need to adjust the
diff --git a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
index fa10607..11f1f59 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
@@ -24,6 +24,7 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.PreferenceScreen;
 import android.text.BidiFormatter;
 
@@ -51,7 +52,11 @@
     private final WifiManager mWifiManager;
     private final Lifecycle mLifecycle;
     private WifiTetherSwitchBarController mSwitchController;
-    private MasterSwitchPreference mPreference;
+    private int mSoftApState;
+    @VisibleForTesting
+    MasterSwitchPreference mPreference;
+    @VisibleForTesting
+    WifiTetherSoftApManager mWifiTetherSoftApManager;
 
     static {
         WIFI_TETHER_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
@@ -60,6 +65,12 @@
     }
 
     public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) {
+        this(context, lifecycle, true /* initSoftApManager */);
+    }
+
+    @VisibleForTesting
+    WifiTetherPreferenceController(Context context, Lifecycle lifecycle,
+            boolean initSoftApManager) {
         super(context);
         mConnectivityManager =
                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -69,6 +80,9 @@
         if (lifecycle != null) {
             lifecycle.addObserver(this);
         }
+        if (initSoftApManager) {
+            initWifiTetherSoftApManager();
+        }
     }
 
     @Override
@@ -101,6 +115,9 @@
         if (mPreference != null) {
             mContext.registerReceiver(mReceiver, WIFI_TETHER_INTENT_FILTER);
             clearSummaryForAirplaneMode();
+            if (mWifiTetherSoftApManager != null) {
+                mWifiTetherSoftApManager.registerSoftApCallback();
+            }
         }
     }
 
@@ -108,9 +125,36 @@
     public void onStop() {
         if (mPreference != null) {
             mContext.unregisterReceiver(mReceiver);
+            if (mWifiTetherSoftApManager != null) {
+                mWifiTetherSoftApManager.unRegisterSoftApCallback();
+            }
         }
     }
 
+    @VisibleForTesting
+    void initWifiTetherSoftApManager() {
+        // This manager only handles the number of connected devices, other parts are handled by
+        // normal BroadcastReceiver in this controller
+        mWifiTetherSoftApManager = new WifiTetherSoftApManager(mWifiManager,
+                new WifiTetherSoftApManager.WifiTetherSoftApCallback() {
+                    @Override
+                    public void onStateChanged(int state, int failureReason) {
+                        mSoftApState = state;
+                    }
+
+                    @Override
+                    public void onNumClientsChanged(int numClients) {
+                        if (mPreference != null
+                                && mSoftApState == WifiManager.WIFI_AP_STATE_ENABLED) {
+                            // Only show the number of clients when state is on
+                            mPreference.setSummary(mContext.getResources().getQuantityString(
+                                    R.plurals.wifi_tether_connected_summary, numClients,
+                                    numClients));
+                        }
+                    }
+                });
+    }
+
     //
     // Everything below is copied from WifiApEnabler
     //
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSoftApManager.java b/src/com/android/settings/wifi/tether/WifiTetherSoftApManager.java
new file mode 100644
index 0000000..77a44b0
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherSoftApManager.java
@@ -0,0 +1,47 @@
+package com.android.settings.wifi.tether;
+
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+
+/**
+ * Wrapper for {@link android.net.wifi.WifiManager.SoftApCallback} to pass the robo test
+ */
+public class WifiTetherSoftApManager {
+
+    private WifiManager mWifiManager;
+    private WifiTetherSoftApCallback mWifiTetherSoftApCallback;
+
+    private WifiManager.SoftApCallback mSoftApCallback = new WifiManager.SoftApCallback() {
+        @Override
+        public void onStateChanged(int state, int failureReason) {
+            mWifiTetherSoftApCallback.onStateChanged(state, failureReason);
+        }
+
+        @Override
+        public void onNumClientsChanged(int numClients) {
+            mWifiTetherSoftApCallback.onNumClientsChanged(numClients);
+        }
+    };
+    private Handler mHandler;
+
+    WifiTetherSoftApManager(WifiManager wifiManager,
+            WifiTetherSoftApCallback wifiTetherSoftApCallback) {
+        mWifiManager = wifiManager;
+        mWifiTetherSoftApCallback = wifiTetherSoftApCallback;
+        mHandler = new Handler();
+    }
+
+    public void registerSoftApCallback() {
+        mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
+    }
+
+    public void unRegisterSoftApCallback() {
+        mWifiManager.unregisterSoftApCallback(mSoftApCallback);
+    }
+
+    public interface WifiTetherSoftApCallback {
+        void onStateChanged(int state, int failureReason);
+
+        void onNumClientsChanged(int numClients);
+    }
+}
diff --git a/src/com/android/settings/wrapper/RecoverySystemWrapper.java b/src/com/android/settings/wrapper/RecoverySystemWrapper.java
new file mode 100644
index 0000000..c7ce244
--- /dev/null
+++ b/src/com/android/settings/wrapper/RecoverySystemWrapper.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wrapper;
+
+import android.content.Context;
+import android.os.RecoverySystem;
+
+/**
+ * This class replicates a subset of the {@link RecoverySystem}.
+ * The interface exists so that we can use a thin wrapper around the RecoverySystem in
+ * production code and a mock in tests.
+ */
+public class RecoverySystemWrapper {
+
+    /**
+     * Returns whether wipe Euicc data successfully or not.
+     *
+     * @param packageName the package name of the caller app.
+     */
+    public boolean wipeEuiccData(
+            Context context, final String packageName) {
+        return RecoverySystem.wipeEuiccData(context, packageName);
+    }
+}
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index ebcea43..be910e1 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -24,3 +24,4 @@
 com.android.settings.wifi.SavedAccessPointsWifiSettings
 com.android.settings.notification.ZenModeEventRuleSettings
 com.android.settings.notification.ZenModeScheduleRuleSettings
+com.android.settings.fuelgauge.RestrictedAppDetails
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index a3205f3..f437f96 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -54,4 +54,5 @@
     <bool name="config_show_vibrate_input_devices">false</bool>
     <bool name="config_show_color_correction_preference">false</bool>
     <bool name="config_show_color_inversion_preference">false</bool>
+    <bool name="config_wifi_support_connected_mac_randomization">false</bool>
 </resources>
diff --git a/tests/robotests/res/values/config.xml b/tests/robotests/res/values/config.xml
index 4004106..359df6c 100644
--- a/tests/robotests/res/values/config.xml
+++ b/tests/robotests/res/values/config.xml
@@ -21,4 +21,5 @@
     <bool name="config_show_camera_laser_sensor">true</bool>
     <bool name="config_show_connectivity_monitor">true</bool>
     <bool name="config_display_recent_apps">true</bool>
+    <bool name="config_wifi_support_connected_mac_randomization">true</bool>
 </resources>
\ No newline at end of file
diff --git a/tests/robotests/src/android/service/notification/NotifyingApp.java b/tests/robotests/src/android/service/notification/NotifyingApp.java
new file mode 100644
index 0000000..f36069b
--- /dev/null
+++ b/tests/robotests/src/android/service/notification/NotifyingApp.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.notification;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Stub implementation of framework's NotifyingApp for Robolectric tests. Otherwise Robolectric
+ * throws ClassNotFoundError.
+ *
+ * TODO: Remove this class when Robolectric supports P
+ */
+public final class NotifyingApp implements Comparable<NotifyingApp> {
+
+    private int mUid;
+    private String mPkg;
+    private long mLastNotified;
+
+    public NotifyingApp() {}
+
+    public int getUid() {
+        return mUid;
+    }
+
+    /**
+     * Sets the uid of the package that sent the notification. Returns self.
+     */
+    public NotifyingApp setUid(int mUid) {
+        this.mUid = mUid;
+        return this;
+    }
+
+    public String getPackage() {
+        return mPkg;
+    }
+
+    /**
+     * Sets the package that sent the notification. Returns self.
+     */
+    public NotifyingApp setPackage(@NonNull String mPkg) {
+        this.mPkg = mPkg;
+        return this;
+    }
+
+    public long getLastNotified() {
+        return mLastNotified;
+    }
+
+    /**
+     * Sets the time the notification was originally sent. Returns self.
+     */
+    public NotifyingApp setLastNotified(long mLastNotified) {
+        this.mLastNotified = mLastNotified;
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NotifyingApp that = (NotifyingApp) o;
+        return getUid() == that.getUid()
+                && getLastNotified() == that.getLastNotified()
+                && Objects.equals(mPkg, that.mPkg);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getUid(), mPkg, getLastNotified());
+    }
+
+    /**
+     * Sorts notifying apps from newest last notified date to oldest.
+     */
+    @Override
+    public int compareTo(NotifyingApp o) {
+        if (getLastNotified() == o.getLastNotified()) {
+            if (getUid() == o.getUid()) {
+                return getPackage().compareTo(o.getPackage());
+            }
+            return Integer.compare(getUid(), o.getUid());
+        }
+
+        return -Long.compare(getLastNotified(), o.getLastNotified());
+    }
+
+    @Override
+    public String toString() {
+        return "NotifyingApp{"
+                + "mUid=" + mUid
+                + ", mPkg='" + mPkg + '\''
+                + ", mLastNotified=" + mLastNotified
+                + '}';
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/MasterClearTest.java b/tests/robotests/src/com/android/settings/MasterClearTest.java
index 361bc8f..ac753c1 100644
--- a/tests/robotests/src/com/android/settings/MasterClearTest.java
+++ b/tests/robotests/src/com/android/settings/MasterClearTest.java
@@ -19,20 +19,29 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
 
+import android.accounts.Account;
+import android.accounts.AccountManager;
 import android.app.Activity;
 import android.app.Fragment;
 import android.content.ComponentName;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.CheckBox;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
@@ -46,6 +55,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowAccountManager;
 import org.robolectric.shadows.ShadowActivity;
 
 @RunWith(SettingsRobolectricTestRunner.class)
@@ -55,6 +65,9 @@
     shadows = {ShadowUtils.class}
 )
 public class MasterClearTest {
+    private static final String TEST_ACCOUNT_TYPE = "android.test.account.type";
+    private static final String TEST_CONFIRMATION_PACKAGE = "android.test.confirmation.pkg";
+    private static final String TEST_ACCOUNT_NAME = "test@example.com";
 
     @Mock
     private MasterClear mMasterClear;
@@ -62,7 +75,18 @@
     private ScrollView mScrollView;
     @Mock
     private LinearLayout mLinearLayout;
+
+    @Mock
+    private PackageManager mPackageManager;
+
+    @Mock
+    private AccountManager mAccountManager;
+
+    @Mock
+    private Activity mMockActivity;
+
     private ShadowActivity mShadowActivity;
+    private ShadowAccountManager mShadowAccountManager;
     private Activity mActivity;
     private View mContentView;
 
@@ -86,6 +110,7 @@
         mMasterClear = spy(new MasterClear());
         mActivity = Robolectric.setupActivity(Activity.class);
         mShadowActivity = shadowOf(mActivity);
+        // mShadowAccountManager = shadowOf(AccountManager.get(mActivity));
         mContentView = LayoutInflater.from(mActivity).inflate(R.layout.master_clear, null);
 
         // Make scrollView only have one child
@@ -94,6 +119,32 @@
     }
 
     @Test
+    public void testShowFinalConfirmation_eraseEsimChecked() {
+        ActivityForTest testActivity = new ActivityForTest();
+        when(mMasterClear.getActivity()).thenReturn(testActivity);
+
+        mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim);
+        mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external);
+        mMasterClear.mEsimStorage.setChecked(true);
+        mMasterClear.showFinalConfirmation();
+        assertThat(testActivity.getArgs().getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false))
+                .isTrue();
+    }
+
+    @Test
+    public void testShowFinalConfirmation_eraseEsimUnchecked() {
+        ActivityForTest testActivity = new ActivityForTest();
+        when(mMasterClear.getActivity()).thenReturn(testActivity);
+
+        mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim);
+        mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external);
+        mMasterClear.mEsimStorage.setChecked(false);
+        mMasterClear.showFinalConfirmation();
+        assertThat(testActivity.getArgs().getBoolean(MasterClear.ERASE_ESIMS_EXTRA, true))
+                .isFalse();
+    }
+
+    @Test
     public void testShowWipeEuicc_euiccDisabled() {
         prepareEuiccState(
                 false /* isEuiccEnabled */, true /* isEuiccProvisioned */);
@@ -162,9 +213,61 @@
 
     @Test
     public void testTryShowAccountConfirmation_unsupported() {
-      doReturn(mActivity).when(mMasterClear).getActivity();
-      /* Using the default resources, account confirmation shouldn't trigger */
-      assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
+        when(mMasterClear.getActivity()).thenReturn(mActivity);
+        /* Using the default resources, account confirmation shouldn't trigger */
+        assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
+    }
+
+    @Test
+    public void testTryShowAccountConfirmation_no_relevant_accounts() {
+        when(mMasterClear.getActivity()).thenReturn(mMockActivity);
+        when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
+        when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
+
+        Account[] accounts = new Account[0];
+        when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
+        when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
+        assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
+    }
+
+    @Test
+    public void testTryShowAccountConfirmation_unresolved() {
+        when(mMasterClear.getActivity()).thenReturn(mMockActivity);
+        when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
+        when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
+        Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
+        when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
+        when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
+        // The package manager should not resolve the confirmation intent targeting the non-existent
+        // confirmation package.
+        when(mMockActivity.getPackageManager()).thenReturn(mPackageManager);
+        assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
+    }
+
+    @Test
+    public void testTryShowAccountConfirmation_ok() {
+        when(mMasterClear.getActivity()).thenReturn(mMockActivity);
+        // Only try to show account confirmation if the appropriate resource overlays are available.
+        when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
+        when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
+        // Add accounts to trigger the search for a resolving intent.
+        Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
+        when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
+        when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
+        // The package manager should not resolve the confirmation intent targeting the non-existent
+        // confirmation package.
+        when(mMockActivity.getPackageManager()).thenReturn(mPackageManager);
+
+        ActivityInfo activityInfo = new ActivityInfo();
+        activityInfo.packageName = TEST_CONFIRMATION_PACKAGE;
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = activityInfo;
+        when(mPackageManager.resolveActivity(any(), eq(0))).thenReturn(resolveInfo);
+
+        // Finally mock out the startActivityForResultCall
+        doNothing().when(mMockActivity).startActivityForResult(any(), eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST));
+
+        assertThat(mMasterClear.tryShowAccountConfirmation()).isTrue();
     }
 
     private void initScrollView(int height, int scrollY, int childBottom) {
diff --git a/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java b/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java
new file mode 100644
index 0000000..f4b5f4c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.content.Context;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.RecoverySystemWrapper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(
+    manifest = TestConfig.MANIFEST_PATH,
+    sdk = TestConfig.SDK_VERSION
+)
+public class ResetNetworkConfirmTest {
+
+    private Activity mActivity;
+    @Mock
+    private ResetNetworkConfirm mResetNetworkConfirm;
+    @Mock
+    private RecoverySystemWrapper mRecoverySystem;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mResetNetworkConfirm = spy(new ResetNetworkConfirm());
+        mRecoverySystem = spy(new RecoverySystemWrapper());
+        ResetNetworkConfirm.mRecoverySystem = mRecoverySystem;
+        mActivity = Robolectric.setupActivity(Activity.class);
+    }
+
+    @Test
+    public void testResetNetworkData_resetEsim() {
+        mResetNetworkConfirm.mEraseEsim = true;
+        doReturn(true)
+                .when(mRecoverySystem).wipeEuiccData(any(Context.class), anyString());
+
+        mResetNetworkConfirm.esimFactoryReset(mActivity, "" /* packageName */);
+        try {
+            // Waiting the Async task finished
+            Thread.sleep(10000); // 10 sec
+        } catch (InterruptedException ignore) {
+
+        }
+
+        Assert.assertNotNull(mResetNetworkConfirm.mEraseEsimTask);
+        verify(mRecoverySystem).wipeEuiccData(any(Context.class), anyString());
+    }
+
+    @Test
+    public void testResetNetworkData_notResetEsim() {
+        mResetNetworkConfirm.mEraseEsim = false;
+
+        mResetNetworkConfirm.esimFactoryReset(mActivity, "" /* packageName */);
+
+        Assert.assertNull(mResetNetworkConfirm.mEraseEsimTask);
+        verify(mRecoverySystem, never())
+                .wipeEuiccData(any(Context.class), anyString());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/SettingsDialogFragmentTest.java b/tests/robotests/src/com/android/settings/SettingsDialogFragmentTest.java
index 3a7d094..942634a 100644
--- a/tests/robotests/src/com/android/settings/SettingsDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/SettingsDialogFragmentTest.java
@@ -17,7 +17,6 @@
 
 import android.app.Dialog;
 import android.app.Fragment;
-import android.content.Context;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -40,8 +39,6 @@
     private static final int DIALOG_ID = 15;
 
     @Mock
-    private Context mContext;
-    @Mock
     private DialogCreatableFragment mDialogCreatable;
     private SettingsPreferenceFragment.SettingsDialogFragment mDialogFragment;
 
@@ -56,10 +53,9 @@
 
         mDialogFragment =
                 new SettingsPreferenceFragment.SettingsDialogFragment(mDialogCreatable, DIALOG_ID);
-        mDialogFragment.onAttach(mContext);
         mDialogFragment.getMetricsCategory();
 
-        // getDialogMetricsCategory called in onAttach, and explicitly in test.
+        // getDialogMetricsCategory called in constructor, and explicitly in test.
         verify(mDialogCreatable, times(2)).getDialogMetricsCategory(DIALOG_ID);
     }
 
@@ -70,7 +66,6 @@
         try {
             mDialogFragment = new SettingsPreferenceFragment.SettingsDialogFragment(
                     mDialogCreatable, DIALOG_ID);
-            mDialogFragment.onAttach(mContext);
         } catch (IllegalStateException e) {
             // getDialogMetricsCategory called in constructor
             verify(mDialogCreatable).getDialogMetricsCategory(DIALOG_ID);
diff --git a/tests/robotests/src/com/android/settings/applications/instantapps/InstantAppButtonsControllerTest.java b/tests/robotests/src/com/android/settings/applications/instantapps/InstantAppButtonsControllerTest.java
index f85d43a..5c0badc 100644
--- a/tests/robotests/src/com/android/settings/applications/instantapps/InstantAppButtonsControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/instantapps/InstantAppButtonsControllerTest.java
@@ -42,9 +42,9 @@
 
 import com.android.settings.R;
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.wrapper.PackageManagerWrapper;
 
 import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
index 71020be..e9d37f6 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
@@ -32,11 +32,11 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java
index 828b5a1..b973edb 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java
@@ -29,13 +29,13 @@
 import com.android.settings.R;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settings.widget.MasterSwitchController;
 import com.android.settings.widget.MasterSwitchPreference;
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.BeforeClass;
diff --git a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
index c3b22b3..8666ce3 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
@@ -26,11 +26,11 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java b/tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java
index c561d0d..8ded9d6 100644
--- a/tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java
+++ b/tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java
@@ -2,6 +2,8 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.mockito.Mockito.mock;
+
 import android.content.Context;
 import android.content.res.XmlResourceParser;
 import android.provider.SearchIndexableResource;
@@ -13,10 +15,12 @@
 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.SearchFeatureProvider;
+import com.android.settings.search.SearchFeatureProviderImpl;
 import com.android.settings.search.XmlParserUtils;
 import com.android.settings.security.SecuritySettings;
 import com.android.settings.security.SecuritySettingsV2;
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.After;
@@ -82,19 +86,21 @@
                     + "IllegalAccessException. Please fix the following classes:\n";
 
     Context mContext;
-
-    private Set<Class> mProviderClassesCopy;
+    SearchFeatureProvider mSearchProvider;
+    private FakeFeatureFactory mFakeFeatureFactory;
 
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.application;
-        mProviderClassesCopy = new HashSet<>(SearchIndexableResources.providerValues());
+        mSearchProvider = new SearchFeatureProviderImpl();
+        mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
+        mFakeFeatureFactory.searchFeatureProvider = mSearchProvider;
     }
 
     @After
     public void cleanUp() {
-        SearchIndexableResources.providerValues().clear();
-        SearchIndexableResources.providerValues().addAll(mProviderClassesCopy);
+        mFakeFeatureFactory.searchFeatureProvider = mock(
+                SearchFeatureProvider.class);
     }
 
     @Test
@@ -156,7 +162,8 @@
     private Set<Integer> getIndexableXml() {
         Set<Integer> xmlResSet = new HashSet();
 
-        Collection<Class> indexableClasses = SearchIndexableResources.providerValues();
+        Collection<Class> indexableClasses =
+                mSearchProvider.getSearchIndexableResources().getProviderValues();
         indexableClasses.removeAll(illegalClasses);
 
         for (Class clazz : indexableClasses) {
diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java b/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java
index 867b5df..4455549 100644
--- a/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java
+++ b/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java
@@ -20,7 +20,6 @@
 import android.util.ArraySet;
 
 import com.android.settings.core.codeinspection.CodeInspector;
-import com.android.settingslib.core.instrumentation.Instrumentable;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentedDialogFragmentTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentedDialogFragmentTest.java
index 8ad2d69..9e37896 100644
--- a/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentedDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentedDialogFragmentTest.java
@@ -21,7 +21,6 @@
 
 import com.android.settings.TestConfig;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java
index 2950c07..da48f15 100644
--- a/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java
@@ -31,9 +31,6 @@
 import com.android.settings.TestConfig;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settingslib.core.instrumentation.LogWriter;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -61,6 +58,7 @@
     @Mock private VisibilityLoggerMixin mockVisibilityLogger;
 
     private Context mContext;
+    private MetricsFeatureProvider mProvider;
 
     @Captor
     private ArgumentCaptor<Pair> mPairCaptor;
@@ -69,6 +67,12 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
+        mProvider = new MetricsFeatureProvider();
+        List<LogWriter> writers = new ArrayList<>();
+        writers.add(mockLogWriter);
+        ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers);
+
+        when(mockVisibilityLogger.elapsedTimeSinceVisible()).thenReturn(ELAPSED_TIME);
     }
 
     @Test
@@ -80,4 +84,60 @@
 
         assertThat(feature1 == feature2).isTrue();
     }
+
+    @Test
+    public void logDashboardStartIntent_intentEmpty_shouldNotLog() {
+        mProvider.logDashboardStartIntent(mContext, null /* intent */,
+                MetricsEvent.SETTINGS_GESTURES);
+
+        verifyNoMoreInteractions(mockLogWriter);
+    }
+
+    @Test
+    public void logDashboardStartIntent_intentHasNoComponent_shouldLog() {
+        final Intent intent = new Intent(Intent.ACTION_ASSIST);
+
+        mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
+
+        verify(mockLogWriter).action(
+                eq(mContext),
+                eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
+                anyString(),
+                eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
+    }
+
+    @Test
+    public void logDashboardStartIntent_intentIsExternal_shouldLog() {
+        final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
+
+        mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
+
+        verify(mockLogWriter).action(
+                eq(mContext),
+                eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
+                anyString(),
+                eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
+    }
+
+    @Test
+    public void action_BooleanLogsElapsedTime() {
+        mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_BOOLEAN);
+        verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_BOOLEAN), mPairCaptor.capture());
+
+        Pair value = mPairCaptor.getValue();
+        assertThat(value.first instanceof Integer).isTrue();
+        assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS);
+        assertThat(value.second).isEqualTo(ELAPSED_TIME);
+    }
+
+    @Test
+    public void action_IntegerLogsElapsedTime() {
+        mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_INTEGER);
+        verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_INTEGER), mPairCaptor.capture());
+
+        Pair value = mPairCaptor.getValue();
+        assertThat(value.first instanceof Integer).isTrue();
+        assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS);
+        assertThat(value.second).isEqualTo(ELAPSED_TIME);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java
new file mode 100644
index 0000000..c80e3a8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.core.instrumentation;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Pair;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import com.google.common.truth.Platform;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SharedPreferenceLoggerTest {
+
+    private static final String TEST_TAG = "tag";
+    private static final String TEST_KEY = "key";
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+
+    private ArgumentMatcher<Pair<Integer, Object>> mNamePairMatcher;
+    private FakeFeatureFactory mFactory;
+    private MetricsFeatureProvider mMetricsFeature;
+    private SharedPreferencesLogger mSharedPrefLogger;
+
+    @Before
+    public void init() {
+        MockitoAnnotations.initMocks(this);
+        mFactory = FakeFeatureFactory.setupForTest();
+        mMetricsFeature = mFactory.metricsFeatureProvider;
+
+        mSharedPrefLogger = new SharedPreferencesLogger(mContext, TEST_TAG);
+        mNamePairMatcher = pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, String.class);
+    }
+
+    @Test
+    public void putInt_shouldNotLogInitialPut() {
+        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
+        editor.putInt(TEST_KEY, 1);
+        editor.putInt(TEST_KEY, 1);
+        editor.putInt(TEST_KEY, 1);
+        editor.putInt(TEST_KEY, 2);
+        editor.putInt(TEST_KEY, 2);
+        editor.putInt(TEST_KEY, 2);
+        editor.putInt(TEST_KEY, 2);
+
+        verify(mMetricsFeature, times(6)).action(any(Context.class), anyInt(),
+                argThat(mNamePairMatcher),
+                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class)));
+    }
+
+    @Test
+    public void putBoolean_shouldNotLogInitialPut() {
+        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
+        editor.putBoolean(TEST_KEY, true);
+        editor.putBoolean(TEST_KEY, true);
+        editor.putBoolean(TEST_KEY, false);
+        editor.putBoolean(TEST_KEY, false);
+        editor.putBoolean(TEST_KEY, false);
+
+
+        verify(mMetricsFeature).action(any(Context.class), anyInt(),
+                argThat(mNamePairMatcher),
+                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, true)));
+        verify(mMetricsFeature, times(3)).action(any(Context.class), anyInt(),
+                argThat(mNamePairMatcher),
+                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, false)));
+    }
+
+    @Test
+    public void putLong_shouldNotLogInitialPut() {
+        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
+        editor.putLong(TEST_KEY, 1);
+        editor.putLong(TEST_KEY, 1);
+        editor.putLong(TEST_KEY, 1);
+        editor.putLong(TEST_KEY, 1);
+        editor.putLong(TEST_KEY, 2);
+
+        verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(),
+                argThat(mNamePairMatcher),
+                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class)));
+    }
+
+    @Test
+    public void putLong_biggerThanIntMax_shouldLogIntMax() {
+        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
+        final long veryBigNumber = 500L + Integer.MAX_VALUE;
+        editor.putLong(TEST_KEY, 1);
+        editor.putLong(TEST_KEY, veryBigNumber);
+
+        verify(mMetricsFeature).action(any(Context.class), anyInt(),
+                argThat(mNamePairMatcher),
+                argThat(pairMatches(
+                        FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MAX_VALUE)));
+    }
+
+    @Test
+    public void putLong_smallerThanIntMin_shouldLogIntMin() {
+        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
+        final long veryNegativeNumber = -500L + Integer.MIN_VALUE;
+        editor.putLong(TEST_KEY, 1);
+        editor.putLong(TEST_KEY, veryNegativeNumber);
+
+        verify(mMetricsFeature).action(any(Context.class), anyInt(),
+                argThat(mNamePairMatcher),
+                argThat(pairMatches(
+                        FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MIN_VALUE)));
+    }
+
+    @Test
+    public void putFloat_shouldNotLogInitialPut() {
+        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
+        editor.putFloat(TEST_KEY, 1);
+        editor.putFloat(TEST_KEY, 1);
+        editor.putFloat(TEST_KEY, 1);
+        editor.putFloat(TEST_KEY, 1);
+        editor.putFloat(TEST_KEY, 2);
+
+        verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(),
+                argThat(mNamePairMatcher),
+                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, Float.class)));
+    }
+
+    @Test
+    public void logPackage_shouldUseLogPackageApi() {
+        mSharedPrefLogger.logPackageName("key", "com.android.settings");
+        verify(mMetricsFeature).action(any(Context.class),
+                eq(ACTION_SETTINGS_PREFERENCE_CHANGE),
+                eq("com.android.settings"),
+                any(Pair.class));
+    }
+
+    private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, Class clazz) {
+        return pair -> pair.first == tag && Platform.isInstanceOfType(pair.second, clazz);
+    }
+
+    private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, boolean bool) {
+        return pair -> pair.first == tag
+                && Platform.isInstanceOfType(pair.second, Integer.class)
+                && pair.second.equals((bool ? 1 : 0));
+    }
+
+    private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, int val) {
+        return pair -> pair.first == tag
+                && Platform.isInstanceOfType(pair.second, Integer.class)
+                && pair.second.equals(val);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/VisibilityLoggerMixinTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/VisibilityLoggerMixinTest.java
new file mode 100644
index 0000000..1a47a66
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/core/instrumentation/VisibilityLoggerMixinTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.core.instrumentation;
+
+import static com.android.settings.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
+
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.SettingsActivity;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class VisibilityLoggerMixinTest {
+
+    @Mock
+    private MetricsFeatureProvider mMetricsFeature;
+
+    private VisibilityLoggerMixin mMixin;
+
+    @Before
+    public void init() {
+        MockitoAnnotations.initMocks(this);
+        mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, mMetricsFeature);
+    }
+
+    @Test
+    public void shouldLogVisibleOnResume() {
+        mMixin.onResume();
+
+        verify(mMetricsFeature, times(1))
+                .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.VIEW_UNKNOWN),
+                        eq(TestInstrumentable.TEST_METRIC));
+    }
+
+    @Test
+    public void shouldLogVisibleWithSource() {
+        final Intent sourceIntent = new Intent()
+                .putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY,
+                        MetricsProto.MetricsEvent.SETTINGS_GESTURES);
+        final Activity activity = mock(Activity.class);
+        when(activity.getIntent()).thenReturn(sourceIntent);
+        mMixin.setSourceMetricsCategory(activity);
+        mMixin.onResume();
+
+        verify(mMetricsFeature, times(1))
+                .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.SETTINGS_GESTURES),
+                        eq(TestInstrumentable.TEST_METRIC));
+    }
+
+    @Test
+    public void shouldLogHideOnPause() {
+        mMixin.onPause();
+
+        verify(mMetricsFeature, times(1))
+                .hidden(nullable(Context.class), eq(TestInstrumentable.TEST_METRIC));
+    }
+
+    @Test
+    public void shouldNotLogIfMetricsFeatureIsNull() {
+        mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC);
+        mMixin.onResume();
+        mMixin.onPause();
+
+        verify(mMetricsFeature, never())
+                .hidden(nullable(Context.class), anyInt());
+    }
+
+    @Test
+    public void shouldNotLogIfMetricsCategoryIsUnknown() {
+        mMixin = new VisibilityLoggerMixin(METRICS_CATEGORY_UNKNOWN, mMetricsFeature);
+
+        mMixin.onResume();
+        mMixin.onPause();
+
+        verify(mMetricsFeature, never())
+                .hidden(nullable(Context.class), anyInt());
+    }
+
+    private final class TestInstrumentable implements Instrumentable {
+
+        public static final int TEST_METRIC = 12345;
+
+        @Override
+        public int getMetricsCategory() {
+            return TEST_METRIC;
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java
index afa914c..741f2bc 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java
@@ -51,7 +51,6 @@
 import com.android.settings.testutils.shadow.ShadowThreadUtils;
 import com.android.settings.testutils.shadow.ShadowTileUtils;
 import com.android.settings.testutils.shadow.ShadowUserManager;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settingslib.drawer.CategoryKey;
 import com.android.settingslib.drawer.CategoryManager;
 import com.android.settingslib.drawer.DashboardCategory;
@@ -373,7 +372,7 @@
         final Intent launchIntent = shadowActivity.getNextStartedActivityForResult().intent;
         assertThat(launchIntent.getAction())
                 .isEqualTo("TestAction");
-        assertThat(launchIntent.getIntExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY, 0))
+        assertThat(launchIntent.getIntExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, 0))
                 .isEqualTo(MetricsProto.MetricsEvent.SETTINGS_GESTURES);
     }
 
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
index 40e590a..6c663ab 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
@@ -33,10 +33,10 @@
 
 import com.android.settings.TestConfig;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settingslib.drawer.DashboardCategory;
 import com.android.settingslib.drawer.Tile;
 import com.android.settingslib.drawer.TileUtils;
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java b/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java
index 1a3fa5e..d077e6f 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java
@@ -22,7 +22,7 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
index 8b3c770..9ab88d3 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
@@ -60,7 +60,6 @@
 
     @Test
     public void resumePause_shouldListenUnlistenDataStateChange() {
-        mDataUsageList.onAttach(mContext);
         mDataUsageList.onResume();
 
         verify(mListener).setListener(true, 0, mContext);
diff --git a/tests/robotests/src/com/android/settings/datetime/ZonePickerTest.java b/tests/robotests/src/com/android/settings/datetime/ZonePickerTest.java
index 9f6d0ef..92807e9 100644
--- a/tests/robotests/src/com/android/settings/datetime/ZonePickerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/ZonePickerTest.java
@@ -28,8 +28,8 @@
 
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settings.testutils.shadow.ShadowZoneGetter;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarControllerTest.java
index ab32fa2..66ccc6e 100644
--- a/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarControllerTest.java
@@ -31,11 +31,11 @@
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
 import com.android.settings.widget.SwitchBar;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/robotests/src/com/android/settings/development/WifiConnectedMacRandomizationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/WifiConnectedMacRandomizationPreferenceControllerTest.java
new file mode 100644
index 0000000..9a80c5c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/WifiConnectedMacRandomizationPreferenceControllerTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.WifiConnectedMacRandomizationPreferenceController
+        .SETTING_VALUE_OFF;
+import static com.android.settings.development.WifiConnectedMacRandomizationPreferenceController
+        .SETTING_VALUE_ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WifiConnectedMacRandomizationPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private Context mContext;
+    private SwitchPreference mPreference;
+    private WifiConnectedMacRandomizationPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = new WifiConnectedMacRandomizationPreferenceController(mContext);
+        mPreference = new SwitchPreference(mContext);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void isAvailable_trueSupportFlag_shouldReturnTrue() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    @Config(qualifiers = "mcc999")
+    public void isAvailable_falseSupportFlag_shouldReturnFalse() {
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void onPreferenceChange_settingEnabled_shouldEnableConnectedMacRandomization() {
+        mController.onPreferenceChange(mPreference, true /* new value */);
+
+        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, -1 /* default */);
+
+        assertThat(mode).isEqualTo(SETTING_VALUE_ON);
+    }
+
+    @Test
+    public void onPreferenceChange_settingDisabled_shouldDisableConnectedMacRandomization() {
+        mController.onPreferenceChange(mPreference, false /* new value */);
+
+        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, -1 /* default */);
+
+        assertThat(mode).isEqualTo(SETTING_VALUE_OFF);
+    }
+
+    @Test
+    public void updateState_settingEnabled_shouldEnablePreference() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, SETTING_VALUE_ON);
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void updateState_settingDisabled_shouldDisablePreference() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, SETTING_VALUE_OFF);
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchEnabled_shouldEnablePreference() {
+        mController.onDeveloperOptionsSwitchEnabled();
+
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() {
+        mController.onDeveloperOptionsSwitchDisabled();
+
+        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, -1 /* default */);
+
+        assertThat(mode).isEqualTo(SETTING_VALUE_OFF);
+        assertThat(mPreference.isChecked()).isFalse();
+        assertThat(mPreference.isEnabled()).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/BrandedAccountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/BrandedAccountPreferenceControllerTest.java
new file mode 100644
index 0000000..521800b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/BrandedAccountPreferenceControllerTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import android.accounts.Account;
+import android.content.Context;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BrandedAccountPreferenceControllerTest {
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    private BrandedAccountPreferenceController mController;
+    private FakeFeatureFactory fakeFeatureFactory;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        fakeFeatureFactory = FakeFeatureFactory.setupForTest();
+        mController = new BrandedAccountPreferenceController(mContext);
+    }
+
+    @Test
+    public void isAvailable_defaultOff() {
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_onWhenAccountIsAvailable() {
+        when(fakeFeatureFactory.mAccountFeatureProvider.getAccounts(any(Context.class))).thenReturn(
+                new Account[]
+                        {new Account("fake@account.foo", "fake.reallyfake")});
+        mController = new BrandedAccountPreferenceController(mContext);
+        assertThat(mController.isAvailable()).isTrue();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java
index 9c56611..90ce395 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java
@@ -37,13 +37,13 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.RoSystemProperties;
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.deletionhelper.ActivationWarningFragment;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
 import com.android.settings.widget.MasterSwitchPreference;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
index 2da756f..1a3139d 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -50,13 +50,13 @@
 import com.android.settings.SubSettings;
 import com.android.settings.TestConfig;
 import com.android.settings.applications.manageapplications.ManageApplications;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.deviceinfo.PrivateVolumeSettings;
 import com.android.settings.deviceinfo.StorageItemPreference;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settingslib.applications.StorageStatsSource;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.deviceinfo.StorageVolumeProvider;
 
 import org.junit.After;
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java
index e251be0..6ad37ce 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java
@@ -38,10 +38,10 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.deviceinfo.StorageVolumeProvider;
 
 import org.junit.After;
diff --git a/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java
index c003f17..e1ce694 100644
--- a/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java
@@ -32,11 +32,11 @@
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.search.InlinePayload;
 import com.android.settings.search.InlineSwitchPayload;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.ShadowSecureSettings;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java
index 33bc4f8..d4af8b8 100644
--- a/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java
@@ -20,8 +20,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -34,16 +38,20 @@
 import com.android.settings.TimeoutListPreference;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.ShadowDevicePolicyManagerWrapper;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
@@ -93,4 +101,34 @@
         mController.updateState(mPreference);
         verify(mPreference).removeUnusableTimeouts(timeout, null);
     }
+
+    @Test
+    public void testUpdateStateWithAdminTimeoutsAndRestriction() {
+        final int profileUserId = UserHandle.myUserId();
+        final long timeout = 100;
+        when(mUserManager.getProfiles(profileUserId)).thenReturn(Collections.emptyList());
+        ShadowDevicePolicyManagerWrapper.setMaximumTimeToLock(profileUserId, timeout);
+
+        int userId = UserHandle.myUserId();
+        List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
+        // Add two enforcing users so that RestrictedLockUtils.checkIfRestrictionEnforced returns
+        // non-null.
+        enforcingUsers.add(new UserManager.EnforcingUser(userId,
+                UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
+        enforcingUsers.add(new UserManager.EnforcingUser(userId,
+                UserManager.RESTRICTION_SOURCE_PROFILE_OWNER));
+        when(mUserManager.getUserRestrictionSources(
+                UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT, UserHandle.of(userId)))
+                .thenReturn(enforcingUsers);
+
+        mController.updateState(mPreference);
+
+        ArgumentCaptor<Long> longCaptor = ArgumentCaptor.forClass(Long.class);
+        ArgumentCaptor<EnforcedAdmin> adminCaptor = ArgumentCaptor.forClass(EnforcedAdmin.class);
+
+        verify(mPreference, times(2)).removeUnusableTimeouts(
+                longCaptor.capture(), adminCaptor.capture());
+        assertEquals(0, (long)longCaptor.getValue());
+        assertTrue(adminCaptor.getValue() != null);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java
index 5418ead..c590fd3 100644
--- a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java
+++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java
@@ -36,7 +36,6 @@
 import com.android.settings.R;
 import com.android.settings.TestConfig;
 import com.android.settings.password.ChooseLockSettingsHelper;
-import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.ShadowUtils;
 import com.android.settings.testutils.shadow.ShadowVibrator;
@@ -70,15 +69,12 @@
 
     private FingerprintEnrollEnrolling mActivity;
 
-    private FakeFeatureFactory mFactory;
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         ShadowUtils.setFingerprintManager(mFingerprintManager);
         ShadowVibrator.addToServiceMap();
 
-        mFactory = FakeFeatureFactory.setupForTest();
         mActivity = Robolectric.buildActivity(
                 FingerprintEnrollEnrolling.class,
                 new Intent()
diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java
index be53aa5..d495b74 100644
--- a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java
+++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java
@@ -33,7 +33,6 @@
 import com.android.settings.R;
 import com.android.settings.TestConfig;
 import com.android.settings.password.ChooseLockSettingsHelper;
-import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
@@ -70,13 +69,10 @@
 
     private FingerprintEnrollFindSensor mActivity;
 
-    private FakeFeatureFactory mFactory;
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         ShadowUtils.setFingerprintManager(mFingerprintManager);
-        mFactory = FakeFeatureFactory.setupForTest();
 
         mActivity = Robolectric.buildActivity(
                 FingerprintEnrollFindSensor.class,
diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintSuggestionActivityTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintSuggestionActivityTest.java
index f52f437..0254bcb 100644
--- a/tests/robotests/src/com/android/settings/fingerprint/FingerprintSuggestionActivityTest.java
+++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintSuggestionActivityTest.java
@@ -30,7 +30,6 @@
 
 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.testutils.shadow.ShadowEventLogWriter;
 import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
@@ -64,12 +63,9 @@
 
     private ActivityController<FingerprintSuggestionActivity> mController;
 
-    private FakeFeatureFactory mFactory;
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mFactory = FakeFeatureFactory.setupForTest();
 
         final Intent intent = new Intent();
         mController = Robolectric.buildActivity(FingerprintSuggestionActivity.class, intent);
diff --git a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java
index c786608..c3899e9 100644
--- a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java
+++ b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java
@@ -27,7 +27,6 @@
 import com.android.settings.R;
 import com.android.settings.TestConfig;
 import com.android.settings.password.ChooseLockSettingsHelper;
-import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
@@ -62,13 +61,10 @@
 
     private SetupFingerprintEnrollFindSensor mActivity;
 
-    private FakeFeatureFactory mFactory;
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         ShadowUtils.setFingerprintManager(mFingerprintManager);
-        mFactory = FakeFeatureFactory.setupForTest();
     }
 
     private void createActivity(Intent intent) {
diff --git a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java
index f5b0c8a..2d98bf4 100644
--- a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java
+++ b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java
@@ -35,7 +35,6 @@
 import com.android.settings.password.SetupChooseLockGeneric.SetupChooseLockGenericFragment;
 import com.android.settings.password.SetupSkipDialog;
 import com.android.settings.password.StorageManagerWrapper;
-import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.ShadowEventLogWriter;
 import com.android.settings.testutils.shadow.ShadowFingerprintManager;
@@ -73,7 +72,6 @@
 
     @Mock
     private UserInfo mUserInfo;
-    private FakeFeatureFactory mFactory;
 
     private ActivityController<SetupFingerprintEnrollIntroduction> mController;
 
@@ -85,8 +83,6 @@
                 .setSystemFeature(PackageManager.FEATURE_FINGERPRINT, true);
         ShadowFingerprintManager.addToServiceMap();
 
-        mFactory = FakeFeatureFactory.setupForTest();
-
         final Intent intent = new Intent();
         mController = Robolectric.buildActivity(SetupFingerprintEnrollIntroduction.class, intent);
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
index 3d04ac5..30fdccb 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
@@ -18,6 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -34,10 +39,12 @@
 import android.os.Build;
 import android.os.UserManager;
 import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
 import android.widget.Button;
 
 import com.android.settings.R;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settings.testutils.shadow.ShadowFragment;
 import com.android.settings.wrapper.DevicePolicyManagerWrapper;
@@ -87,19 +94,21 @@
     private DevicePolicyManager mDevicePolicyManager;
     @Mock
     private DevicePolicyManagerWrapper mDevicePolicyManagerWrapper;
-    @Mock
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private AdvancedPowerUsageDetail mFragment;
     @Mock
     private PowerWhitelistBackend mPowerWhitelistBackend;
     private BackgroundActivityPreferenceController mController;
-    private SwitchPreference mPreference;
+    private Preference mPreference;
     private Context mShadowContext;
+    private BatteryUtils mBatteryUtils;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         mShadowContext = RuntimeEnvironment.application;
+        FakeFeatureFactory.setupForTest();
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
@@ -115,58 +124,45 @@
         mHighApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
         mLowApplicationInfo.targetSdkVersion = Build.VERSION_CODES.L;
 
-        mPreference = new SwitchPreference(mShadowContext);
+        mBatteryUtils = spy(new BatteryUtils(mShadowContext));
+        doNothing().when(mBatteryUtils).setForceAppStandby(anyInt(), anyString(), anyInt());
+
+        mPreference = new Preference(mShadowContext);
+        mPreference.setKey(BackgroundActivityPreferenceController.KEY_BACKGROUND_ACTIVITY);
         mController = spy(new BackgroundActivityPreferenceController(
                 mContext, mFragment, UID_LOW_SDK, LOW_SDK_PACKAGE, mPowerWhitelistBackend));
         mController.mDpm = mDevicePolicyManagerWrapper;
+        mController.mBatteryUtils = mBatteryUtils;
     }
 
     @Test
-    public void testOnPreferenceChange_TurnOnCheck_MethodInvoked() {
-        mController.onPreferenceChange(mPreference, true);
+    public void testHandlePreferenceTreeClick_restrictApp_showDialog() {
+        doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).checkOpNoThrow(anyInt(),
+                anyInt(), anyString());
 
-        verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_LOW_SDK,
-                LOW_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED);
-        verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
-                LOW_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED);
+        mController.handlePreferenceTreeClick(mPreference);
 
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on));
+        verify(mController).showDialog();
     }
 
     @Test
-    public void testOnPreferenceChange_TurnOnCheckHighSDK_MethodInvoked() {
-        mController = new BackgroundActivityPreferenceController(mContext, mFragment, UID_HIGH_SDK,
-                HIGH_SDK_PACKAGE, mPowerWhitelistBackend);
-        mController.onPreferenceChange(mPreference, true);
-        verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_HIGH_SDK,
-                HIGH_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED);
-        verify(mAppOpsManager, never()).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID_HIGH_SDK,
-                HIGH_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED);
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on));
+    public void testHandlePreferenceTreeClick_unRestrictApp_setModeAllowed() {
+        doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager).checkOpNoThrow(anyInt(),
+                anyInt(), anyString());
+
+        mController.handlePreferenceTreeClick(mPreference);
+
+        verify(mBatteryUtils).setForceAppStandby(UID_LOW_SDK, LOW_SDK_PACKAGE,
+                AppOpsManager.MODE_ALLOWED);
     }
 
     @Test
-    public void testUpdateState_CheckOn_SetCheckedTrue() {
+    public void testUpdateState_noError_setEnabled() {
         when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
                 LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_ALLOWED);
 
         mController.updateState(mPreference);
 
-        assertThat(mPreference.isChecked()).isTrue();
-        assertThat(mPreference.isEnabled()).isTrue();
-        verify(mController).updateSummary(mPreference);
-    }
-
-    @Test
-    public void testUpdateState_CheckOff_SetCheckedFalse() {
-        when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
-                LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED);
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.isChecked()).isFalse();
         assertThat(mPreference.isEnabled()).isTrue();
         verify(mController).updateSummary(mPreference);
     }
@@ -175,7 +171,6 @@
     public void testUpdateState_whitelisted() {
         when(mPowerWhitelistBackend.isWhitelisted(LOW_SDK_PACKAGE)).thenReturn(true);
         mController.updateState(mPreference);
-        assertThat(mPreference.isChecked()).isTrue();
         assertThat(mPreference.isEnabled()).isFalse();
         assertThat(mPreference.getSummary()).isEqualTo(
                 mShadowContext.getText(R.string.background_activity_summary_whitelisted));
@@ -193,37 +188,23 @@
     }
 
     @Test
-    public void testUpdateSummary_modeDefault_showSummaryOn() {
+    public void testUpdateSummary_modeDefault_showNotRestricted() {
         when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
                 LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_DEFAULT);
-        final CharSequence expectedSummary = mShadowContext.getText(
-                R.string.background_activity_summary_on);
 
         mController.updateSummary(mPreference);
 
-        assertThat(mPreference.getSummary()).isEqualTo(expectedSummary);
+        assertThat(mPreference.getSummary()).isEqualTo("No");
     }
 
     @Test
-    public void testUpdateSummary_modeIgnored_showSummaryOff() {
+    public void testUpdateSummary_modeIgnored_showRestricted() {
         when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
                 LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED);
-        final CharSequence expectedSummary = mShadowContext.getText(
-                R.string.background_activity_summary_off);
 
         mController.updateSummary(mPreference);
 
-        assertThat(mPreference.getSummary()).isEqualTo(expectedSummary);
-    }
-
-    @Test
-    public void testIsLegacyApp_SdkLowerThanO_ReturnTrue() {
-        assertThat(mController.isLegacyApp(LOW_SDK_PACKAGE)).isTrue();
-    }
-
-    @Test
-    public void testIsLegacyApp_SdkLargerOrEqualThanO_ReturnFalse() {
-        assertThat(mController.isLegacyApp(HIGH_SDK_PACKAGE)).isFalse();
+        assertThat(mPreference.getSummary()).isEqualTo("Yes");
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index 844aca4..fe90751 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -103,6 +103,8 @@
     private static final double PRECISION = 0.001;
     private static final int SDK_VERSION = Build.VERSION_CODES.L;
     private static final String PACKAGE_NAME = "com.android.app";
+    private static final String HIGH_SDK_PACKAGE = "com.android.package.high";
+    private static final String LOW_SDK_PACKAGE = "com.android.package.low";
 
     @Mock
     private BatteryStats.Uid mUid;
@@ -137,16 +139,18 @@
     @Mock
     private ApplicationInfo mApplicationInfo;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private Context mContext;
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private BatteryStatsHelper mBatteryStatsHelper;
+    @Mock
+    private ApplicationInfo mHighApplicationInfo;
+    @Mock
+    private ApplicationInfo mLowApplicationInfo;
     private BatteryUtils mBatteryUtils;
     private FakeFeatureFactory mFeatureFactory;
     private PowerUsageFeatureProvider mProvider;
     private List<BatterySipper> mUsageList;
 
     @Before
-    public void setUp() {
+    public void setUp() throws PackageManager.NameNotFoundException {
         MockitoAnnotations.initMocks(this);
 
         mFeatureFactory = FakeFeatureFactory.setupForTest();
@@ -165,6 +169,14 @@
         when(mBatteryStatsHelper.getStats().computeBatteryRealtime(anyLong(), anyInt())).thenReturn(
                 TIME_SINCE_LAST_FULL_CHARGE_US);
 
+        when(mPackageManager.getApplicationInfo(HIGH_SDK_PACKAGE, PackageManager.GET_META_DATA))
+                .thenReturn(mHighApplicationInfo);
+        when(mPackageManager.getApplicationInfo(LOW_SDK_PACKAGE, PackageManager.GET_META_DATA))
+                .thenReturn(mLowApplicationInfo);
+        mHighApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
+        mLowApplicationInfo.targetSdkVersion = Build.VERSION_CODES.L;
+
+
         mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
         mNormalBatterySipper.totalPowerMah = TOTAL_BATTERY_USAGE;
 
@@ -501,4 +513,14 @@
         assertThat(mBatteryUtils.calculateScreenUsageTime(mBatteryStatsHelper)).isEqualTo(
                 TIME_EXPECTED_FOREGROUND);
     }
+
+    @Test
+    public void testIsLegacyApp_SdkLowerThanO_ReturnTrue() {
+        assertThat(mBatteryUtils.isLegacyApp(LOW_SDK_PACKAGE)).isTrue();
+    }
+
+    @Test
+    public void testIsLegacyApp_SdkLargerOrEqualThanO_ReturnFalse() {
+        assertThat(mBatteryUtils.isLegacyApp(HIGH_SDK_PACKAGE)).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java
index c944148..5e43d1d 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java
@@ -19,13 +19,18 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.support.v14.preference.PreferenceFragment;
 import android.support.v7.preference.Preference;
 
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
 import com.android.settings.TestConfig;
 
 import org.junit.Before;
@@ -48,6 +53,10 @@
     private AppOpsManager mAppOpsManager;
     @Mock
     private AppOpsManager.PackageOps mPackageOps;
+    @Mock
+    private SettingsActivity mSettingsActivity;
+    @Mock
+    private PreferenceFragment mFragment;
     private List<AppOpsManager.PackageOps> mPackageOpsList;
     private RestrictAppPreferenceController mRestrictAppPreferenceController;
     private Preference mPreference;
@@ -59,9 +68,12 @@
 
         mContext = spy(RuntimeEnvironment.application);
         doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
-        mRestrictAppPreferenceController = new RestrictAppPreferenceController(mContext);
+        doReturn(mContext).when(mSettingsActivity).getApplicationContext();
+        mRestrictAppPreferenceController = new RestrictAppPreferenceController(mSettingsActivity,
+                mFragment);
         mPackageOpsList = new ArrayList<>();
         mPreference = new Preference(mContext);
+        mPreference.setKey(mRestrictAppPreferenceController.getPreferenceKey());
     }
 
     @Test
@@ -85,4 +97,22 @@
         assertThat(mPreference.getSummary()).isEqualTo("2 apps");
     }
 
+    @Test
+    public void testUpdateState_zeroApp_disabled() {
+        doReturn(mPackageOpsList).when(mAppOpsManager).getPackagesForOps(any());
+
+        mRestrictAppPreferenceController.updateState(mPreference);
+
+        assertThat(mPreference.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick_startFragment() {
+        mRestrictAppPreferenceController.handlePreferenceTreeClick(mPreference);
+
+        verify(mSettingsActivity).startPreferencePanelAsUser(eq(mFragment),
+                eq(RestrictedAppDetails.class.getName()), any(), eq(R.string.restricted_app_title),
+                any(), any());
+    }
+
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/RestrictedAppDetailsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedAppDetailsTest.java
new file mode 100644
index 0000000..a9de061
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedAppDetailsTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceManager;
+
+import com.android.settings.TestConfig;
+
+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.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class RestrictedAppDetailsTest {
+    private static final String PACKAGE_NAME = "com.android.app";
+    private static final String APP_NAME = "app";
+    private static final int UID = 1234;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PreferenceManager mPreferenceManager;
+    private RestrictedAppDetails mFragment;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(RuntimeEnvironment.application);
+        mFragment = spy(new RestrictedAppDetails());
+
+        doReturn(mPreferenceManager).when(mFragment).getPreferenceManager();
+        doReturn(mContext).when(mPreferenceManager).getContext();
+        mFragment.mPackageManager = mPackageManager;
+        mFragment.mPackageOpsList = new ArrayList<>();
+        mFragment.mPackageOpsList.add(
+                new AppOpsManager.PackageOps(PACKAGE_NAME, UID, null /* entries */));
+        mFragment.mRestrictedAppListGroup = spy(new PreferenceCategory(mContext));
+        doReturn(mPreferenceManager).when(mFragment.mRestrictedAppListGroup).getPreferenceManager();
+    }
+
+    @Test
+    public void testRefreshUi_displayPreference() throws Exception {
+        doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(PACKAGE_NAME, 0);
+        doReturn(APP_NAME).when(mPackageManager).getApplicationLabel(mApplicationInfo);
+
+        mFragment.refreshUi();
+
+        assertThat(mFragment.mRestrictedAppListGroup.getPreferenceCount()).isEqualTo(1);
+        final Preference preference = mFragment.mRestrictedAppListGroup.getPreference(0);
+        assertThat(preference.getKey()).isEqualTo(PACKAGE_NAME);
+        assertThat(preference.getTitle()).isEqualTo(APP_NAME);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java
index 3e33823..38391c9 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java
@@ -24,6 +24,7 @@
 import android.util.Pair;
 
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.fuelgauge.anomaly.action.StopAndBackgroundCheckAction;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
@@ -31,7 +32,6 @@
 import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
 import com.android.settings.testutils.shadow.ShadowKeyValueListParserWrapperImpl;
 import com.android.settings.fuelgauge.anomaly.checker.WakeupAlarmAnomalyDetector;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
index 53c9766..83b3225 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.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;
@@ -54,7 +55,7 @@
             BatteryTip.TipType.BATTERY_SAVER,
             BatteryTip.TipType.LOW_BATTERY,
             BatteryTip.TipType.SUMMARY};
-    @Mock
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private BatteryStatsHelper mBatteryStatsHelper;
     @Mock
     private PowerManager mPowerManager;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
index bb9a37b..78c86f8 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
@@ -24,6 +24,7 @@
 
 import android.content.Context;
 import android.provider.Settings;
+import android.text.format.DateUtils;
 
 import com.android.settings.TestConfig;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -42,6 +43,8 @@
             + ",battery_saver_tip_enabled=false"
             + ",high_usage_enabled=true"
             + ",high_usage_app_count=5"
+            + ",high_usage_period_ms=2000"
+            + ",high_usage_battery_draining=30"
             + ",app_restriction_enabled=true"
             + ",reduced_battery_enabled=true"
             + ",reduced_battery_percent=30"
@@ -66,6 +69,8 @@
         assertThat(batteryTipPolicy.batterySaverTipEnabled).isFalse();
         assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
         assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(5);
+        assertThat(batteryTipPolicy.highUsagePeriodMs).isEqualTo(2000);
+        assertThat(batteryTipPolicy.highUsageBatteryDraining).isEqualTo(30);
         assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
         assertThat(batteryTipPolicy.reducedBatteryEnabled).isTrue();
         assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(30);
@@ -85,6 +90,8 @@
         assertThat(batteryTipPolicy.batterySaverTipEnabled).isTrue();
         assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
         assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(3);
+        assertThat(batteryTipPolicy.highUsagePeriodMs).isEqualTo(2 * DateUtils.HOUR_IN_MILLIS);
+        assertThat(batteryTipPolicy.highUsageBatteryDraining).isEqualTo(25);
         assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
         assertThat(batteryTipPolicy.reducedBatteryEnabled).isFalse();
         assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(50);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParserTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParserTest.java
new file mode 100644
index 0000000..5bdae0c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParserTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batterytip;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryStats;
+import android.text.format.DateUtils;
+
+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.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.time.Duration;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class HighUsageDataParserTest {
+    private static final long PERIOD_ONE_MINUTE_MS = Duration.ofMinutes(1).toMillis();
+    private static final long END_TIME_MS = 2 * PERIOD_ONE_MINUTE_MS;
+    private static final int THRESHOLD_LOW = 10;
+    private static final int THRESHOLD_HIGH = 20;
+    private HighUsageDataParser mDataParser;
+    private BatteryStats.HistoryItem mFirstItem;
+    private BatteryStats.HistoryItem mSecondItem;
+    private BatteryStats.HistoryItem mThirdItem;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mFirstItem = new BatteryStats.HistoryItem();
+        mFirstItem.batteryLevel = 100;
+        mFirstItem.currentTime = 0;
+        mSecondItem = new BatteryStats.HistoryItem();
+        mSecondItem.batteryLevel = 95;
+        mSecondItem.currentTime = PERIOD_ONE_MINUTE_MS;
+        mThirdItem = new BatteryStats.HistoryItem();
+        mThirdItem.batteryLevel = 80;
+        mThirdItem.currentTime = END_TIME_MS;
+    }
+
+    @Test
+    public void testDataParser_thresholdLow_isHeavilyUsed() {
+        mDataParser = new HighUsageDataParser(PERIOD_ONE_MINUTE_MS, THRESHOLD_LOW);
+        parseData();
+
+        assertThat(mDataParser.isDeviceHeavilyUsed()).isTrue();
+    }
+
+    @Test
+    public void testDataParser_thresholdHigh_notHeavilyUsed() {
+        mDataParser = new HighUsageDataParser(PERIOD_ONE_MINUTE_MS, THRESHOLD_HIGH);
+        parseData();
+
+        assertThat(mDataParser.isDeviceHeavilyUsed()).isFalse();
+    }
+
+    private void parseData() {
+        mDataParser.onParsingStarted(0, END_TIME_MS);
+        mDataParser.onDataPoint(0, mFirstItem);
+        mDataParser.onDataPoint(PERIOD_ONE_MINUTE_MS, mSecondItem);
+        mDataParser.onDataPoint(END_TIME_MS, mThirdItem);
+
+        mDataParser.onParsingDone();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java
index 2a71991..8df7c56 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
@@ -31,6 +33,7 @@
 import com.android.settings.fuelgauge.BatteryInfo;
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.HighUsageDataParser;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
@@ -55,6 +58,8 @@
     private BatteryUtils mBatteryUtils;
     @Mock
     private BatterySipper mBatterySipper;
+    @Mock
+    private HighUsageDataParser mDataParser;
 
     private BatteryTipPolicy mPolicy;
     private HighUsageDetector mHighUsageDetector;
@@ -66,8 +71,10 @@
 
         mContext = RuntimeEnvironment.application;
         mPolicy = spy(new BatteryTipPolicy(mContext));
-        mHighUsageDetector = new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper);
+        mHighUsageDetector = spy(new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper));
         mHighUsageDetector.mBatteryUtils = mBatteryUtils;
+        mHighUsageDetector.mDataParser = mDataParser;
+        doNothing().when(mHighUsageDetector).parseBatteryData();
 
         mUsageList = new ArrayList<>();
         mUsageList.add(mBatterySipper);
@@ -82,8 +89,7 @@
 
     @Test
     public void testDetect_containsHighUsageApp_tipVisible() {
-        doReturn(2 * DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).calculateScreenUsageTime(
-                mBatteryStatsHelper);
+        doReturn(true).when(mDataParser).isDeviceHeavilyUsed();
         doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
         doReturn(DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).getProcessTimeMs(
                 BatteryUtils.StatusType.FOREGROUND, mBatterySipper.uidObj,
diff --git a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
index 737c16d..1ee52ca 100644
--- a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
+++ b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
@@ -20,7 +20,6 @@
 import android.view.View;
 import android.widget.TextView;
 import com.android.settings.TestConfig;
-import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment;
 
@@ -28,7 +27,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
@@ -41,11 +39,6 @@
 
     private LocaleListEditor mLocaleListEditor;
 
-    @Mock
-    private Context mContext;
-
-    private FakeFeatureFactory mFactory;
-
     @Before
     public void setUp() {
         mLocaleListEditor = new LocaleListEditor();
@@ -55,13 +48,11 @@
                 RuntimeEnvironment.application.getSystemService(Context.RESTRICTIONS_SERVICE));
         ReflectionHelpers.setField(mLocaleListEditor, "mUserManager",
                 RuntimeEnvironment.application.getSystemService(Context.USER_SERVICE));
-        mFactory = FakeFeatureFactory.setupForTest();
     }
 
     @Test
     public void testDisallowConfigLocale_unrestrict() {
         ReflectionHelpers.setField(mLocaleListEditor, "mIsUiRestricted", true);
-        mLocaleListEditor.onAttach(mContext);
         mLocaleListEditor.onResume();
         Assert.assertEquals(View.GONE, mLocaleListEditor.getEmptyTextView().getVisibility());
     }
@@ -69,7 +60,6 @@
     @Test
     public void testDisallowConfigLocale_restrict() {
         ReflectionHelpers.setField(mLocaleListEditor, "mIsUiRestricted", false);
-        mLocaleListEditor.onAttach(mContext);
         mLocaleListEditor.onResume();
         Assert.assertEquals(View.VISIBLE, mLocaleListEditor.getEmptyTextView().getVisibility());
     }
diff --git a/tests/robotests/src/com/android/settings/notification/RecentNotifyingAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/RecentNotifyingAppsPreferenceControllerTest.java
new file mode 100644
index 0000000..a25bb00
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/RecentNotifyingAppsPreferenceControllerTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.doNothing;
+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.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.notification.NotifyingApp;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+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.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+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 RecentNotifyingAppsPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private PreferenceCategory mCategory;
+    @Mock
+    private Preference mSeeAllPref;
+    @Mock
+    private PreferenceCategory mDivider;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private ApplicationsState mAppState;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ApplicationsState.AppEntry mAppEntry;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
+    @Mock
+    private NotificationBackend mBackend;
+
+    private Context mContext;
+    private RecentNotifyingAppsPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+
+        mController = new RecentNotifyingAppsPreferenceController(
+                mContext, mBackend, mAppState, null);
+        when(mScreen.findPreference(anyString())).thenReturn(mCategory);
+
+        when(mScreen.findPreference(RecentNotifyingAppsPreferenceController.KEY_SEE_ALL))
+                .thenReturn(mSeeAllPref);
+        when(mScreen.findPreference(RecentNotifyingAppsPreferenceController.KEY_DIVIDER))
+                .thenReturn(mDivider);
+        when(mCategory.getContext()).thenReturn(mContext);
+    }
+
+    @Test
+    public void isAlwaysAvailable() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void doNotIndexCategory() {
+        final List<String> nonIndexable = new ArrayList<>();
+
+        mController.updateNonIndexableKeys(nonIndexable);
+
+        assertThat(nonIndexable).containsAllOf(mController.getPreferenceKey(),
+                RecentNotifyingAppsPreferenceController.KEY_DIVIDER);
+    }
+
+    @Test
+    public void onDisplayAndUpdateState_shouldRefreshUi() {
+        mController = spy(new RecentNotifyingAppsPreferenceController(
+                mContext, null, (ApplicationsState) null, null));
+
+        doNothing().when(mController).refreshUi(mContext);
+
+        mController.displayPreference(mScreen);
+        mController.updateState(mCategory);
+
+        verify(mController, times(2)).refreshUi(mContext);
+    }
+
+    @Test
+    @Config(qualifiers = "mcc999")
+    public void display_shouldNotShowRecents_showAppInfoPreference() {
+        mController.displayPreference(mScreen);
+
+        verify(mCategory, never()).addPreference(any(Preference.class));
+        verify(mCategory).setTitle(null);
+        verify(mSeeAllPref).setTitle(R.string.notifications_title);
+        verify(mSeeAllPref).setIcon(null);
+        verify(mDivider).setVisible(false);
+    }
+
+    @Test
+    public void display_showRecents() {
+        final List<NotifyingApp> apps = new ArrayList<>();
+        final NotifyingApp app1 = new NotifyingApp()
+                .setPackage("pkg.class")
+                .setLastNotified(System.currentTimeMillis());
+        final NotifyingApp app2 = new NotifyingApp()
+                .setLastNotified(System.currentTimeMillis())
+                .setPackage("com.android.settings");
+        final NotifyingApp app3 = new NotifyingApp()
+                .setLastNotified(System.currentTimeMillis() - 1000)
+                .setPackage("pkg.class2");
+
+        apps.add(app1);
+        apps.add(app2);
+        apps.add(app3);
+
+        // app1, app2 are valid apps. app3 is invalid.
+        when(mAppState.getEntry(app1.getPackage(), UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mAppState.getEntry(app2.getPackage(), UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mAppState.getEntry(app3.getPackage(), UserHandle.myUserId()))
+                .thenReturn(null);
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(
+                new ResolveInfo());
+        when(mBackend.getRecentApps()).thenReturn(apps);
+        mAppEntry.info = mApplicationInfo;
+
+        mController.displayPreference(mScreen);
+
+        verify(mCategory).setTitle(R.string.recent_notifications);
+        // Only add app1. app2 is skipped because of the package name, app3 skipped because
+        // it's invalid app.
+        verify(mCategory, times(1)).addPreference(any(Preference.class));
+
+        verify(mSeeAllPref).setSummary(null);
+        verify(mSeeAllPref).setIcon(R.drawable.ic_chevron_right_24dp);
+        verify(mDivider).setVisible(true);
+    }
+
+    @Test
+    public void display_showRecentsWithInstantApp() {
+        // Regular app.
+        final List<NotifyingApp> apps = new ArrayList<>();
+        final NotifyingApp app1 = new NotifyingApp().
+                setLastNotified(System.currentTimeMillis())
+                .setPackage("com.foo.bar");
+        apps.add(app1);
+
+        // Instant app.
+        final NotifyingApp app2 = new NotifyingApp()
+                .setLastNotified(System.currentTimeMillis() + 200)
+                .setPackage("com.foo.barinstant");
+        apps.add(app2);
+
+        ApplicationsState.AppEntry app1Entry = mock(ApplicationsState.AppEntry.class);
+        ApplicationsState.AppEntry app2Entry = mock(ApplicationsState.AppEntry.class);
+        app1Entry.info = mApplicationInfo;
+        app2Entry.info = mApplicationInfo;
+
+        when(mAppState.getEntry(app1.getPackage(), UserHandle.myUserId())).thenReturn(app1Entry);
+        when(mAppState.getEntry(app2.getPackage(), UserHandle.myUserId())).thenReturn(app2Entry);
+
+        // Only the regular app app1 should have its intent resolve.
+        when(mPackageManager.resolveActivity(argThat(intentMatcher(app1.getPackage())),
+                anyInt())).thenReturn(new ResolveInfo());
+
+        when(mBackend.getRecentApps()).thenReturn(apps);
+
+        // Make sure app2 is considered an instant app.
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (ApplicationInfo info) -> {
+                    if (info == app2Entry.info) {
+                        return true;
+                    } else {
+                        return false;
+                    }
+                });
+
+        mController.displayPreference(mScreen);
+
+        ArgumentCaptor<Preference> prefCaptor = ArgumentCaptor.forClass(Preference.class);
+        verify(mCategory, times(2)).addPreference(prefCaptor.capture());
+        List<Preference> prefs = prefCaptor.getAllValues();
+        assertThat(prefs.get(1).getKey()).isEqualTo(app1.getPackage());
+        assertThat(prefs.get(0).getKey()).isEqualTo(app2.getPackage());
+    }
+
+    @Test
+    public void display_hasRecentButNoneDisplayable_showAppInfo() {
+        final List<NotifyingApp> apps = new ArrayList<>();
+        final NotifyingApp app1 = new NotifyingApp()
+                .setPackage("com.android.phone")
+                .setLastNotified(System.currentTimeMillis());
+        final NotifyingApp app2 = new NotifyingApp()
+                .setPackage("com.android.settings")
+                .setLastNotified(System.currentTimeMillis());
+        apps.add(app1);
+        apps.add(app2);
+
+        // app1, app2 are not displayable
+        when(mAppState.getEntry(app1.getPackage(), UserHandle.myUserId()))
+                .thenReturn(mock(ApplicationsState.AppEntry.class));
+        when(mAppState.getEntry(app2.getPackage(), UserHandle.myUserId()))
+                .thenReturn(mock(ApplicationsState.AppEntry.class));
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(
+                new ResolveInfo());
+        when(mBackend.getRecentApps()).thenReturn(apps);
+
+        mController.displayPreference(mScreen);
+
+        verify(mCategory, never()).addPreference(any(Preference.class));
+        verify(mCategory).setTitle(null);
+        verify(mSeeAllPref).setTitle(R.string.notifications_title);
+        verify(mSeeAllPref).setIcon(null);
+    }
+
+    @Test
+    public void display_showRecents_formatSummary() {
+        final List<NotifyingApp> apps = new ArrayList<>();
+        final NotifyingApp app1 = new NotifyingApp()
+                .setLastNotified(System.currentTimeMillis())
+                .setPackage("pkg.class");
+        apps.add(app1);
+
+        when(mAppState.getEntry(app1.getPackage(), UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(
+                new ResolveInfo());
+        when(mBackend.getRecentApps()).thenReturn(apps);
+        mAppEntry.info = mApplicationInfo;
+
+        mController.displayPreference(mScreen);
+
+        verify(mCategory).addPreference(argThat(summaryMatches("0 min. ago")));
+    }
+
+    private static ArgumentMatcher<Preference> summaryMatches(String expected) {
+        return preference -> TextUtils.equals(expected, preference.getSummary());
+    }
+
+    // Used for matching an intent with a specific package name.
+    private static ArgumentMatcher<Intent> intentMatcher(String packageName) {
+        return intent -> packageName.equals(intent.getPackage());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java
index f84f9a2..d610363 100644
--- a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java
+++ b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java
@@ -121,7 +121,8 @@
                 continue;
             }
             // Must be in SearchProviderRegistry
-            if (!SearchIndexableResources.providerValues().contains(clazz)) {
+            SearchFeatureProvider provider = new SearchFeatureProviderImpl();
+            if (!provider.getSearchIndexableResources().getProviderValues().contains(clazz)) {
                 if (!notInSearchIndexableRegistryGrandfatherList.remove(className)) {
                     notInSearchProviderRegistry.add(className);
                 }
diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java b/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java
index eedb324..72dd94c 100644
--- a/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java
@@ -21,12 +21,14 @@
 
 import static junit.framework.Assert.fail;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
 import android.database.Cursor;
 import android.text.TextUtils;
 
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.wifi.WifiSettings;
 
@@ -36,49 +38,56 @@
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import java.util.HashSet;
-import java.util.Set;
-
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class SearchIndexableResourcesTest {
 
-    Set<Class> sProviderClassCopy;
+    SearchFeatureProviderImpl mSearchProvider;
+    private FakeFeatureFactory mFakeFeatureFactory;
 
     @Before
     public void setUp() {
-        sProviderClassCopy = new HashSet<>(SearchIndexableResources.sProviders);
+        mSearchProvider = new SearchFeatureProviderImpl();
+        mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
+        mFakeFeatureFactory.searchFeatureProvider = mSearchProvider;
     }
 
     @After
     public void cleanUp() {
-        SearchIndexableResources.sProviders.clear();
-        SearchIndexableResources.sProviders.addAll(sProviderClassCopy);
+        mFakeFeatureFactory.searchFeatureProvider = mock(
+                SearchFeatureProvider.class);
     }
 
     @Test
     public void testAddIndex() {
         final Class stringClass = java.lang.String.class;
         // Confirms that String.class isn't contained in SearchIndexableResources.
-        assertThat(SearchIndexableResources.sProviders).doesNotContain(stringClass);
-        final int beforeCount = SearchIndexableResources.providerValues().size();
+        assertThat(mSearchProvider.getSearchIndexableResources().getProviderValues())
+                .doesNotContain(stringClass);
+        final int beforeCount =
+                mSearchProvider.getSearchIndexableResources().getProviderValues().size();
 
-        SearchIndexableResources.addIndex(java.lang.String.class);
+        ( (SearchIndexableResourcesImpl) mSearchProvider.getSearchIndexableResources())
+                .addIndex(java.lang.String.class);
 
-        assertThat(SearchIndexableResources.sProviders).contains(stringClass);
-        final int afterCount = SearchIndexableResources.providerValues().size();
+        assertThat(mSearchProvider.getSearchIndexableResources().getProviderValues())
+                .contains(stringClass);
+        final int afterCount =
+                mSearchProvider.getSearchIndexableResources().getProviderValues().size();
         assertThat(afterCount).isEqualTo(beforeCount + 1);
     }
 
     @Test
     public void testIndexHasWifiSettings() {
-        assertThat(sProviderClassCopy).contains(WifiSettings.class);
+        assertThat(mSearchProvider.getSearchIndexableResources().getProviderValues())
+                .contains(WifiSettings.class);
     }
 
     @Test
     public void testNonIndexableKeys_GetsKeyFromProvider() {
-        SearchIndexableResources.sProviders.clear();
-        SearchIndexableResources.addIndex(FakeIndexProvider.class);
+        mSearchProvider.getSearchIndexableResources().getProviderValues().clear();
+        ( (SearchIndexableResourcesImpl) mSearchProvider.getSearchIndexableResources())
+                .addIndex(FakeIndexProvider.class);
 
         SettingsSearchIndexablesProvider provider = spy(new SettingsSearchIndexablesProvider());
 
@@ -97,7 +106,7 @@
 
     @Test
     public void testAllClassNamesHaveProviders() {
-        for (Class clazz: sProviderClassCopy) {
+        for (Class clazz: mSearchProvider.getSearchIndexableResources().getProviderValues()) {
             if(DatabaseIndexingUtils.getSearchIndexProvider(clazz) == null) {
                 fail(clazz.getName() + "is not an index provider");
             }
diff --git a/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java b/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java
index efeaed7..cca2794 100644
--- a/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java
+++ b/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java
@@ -2,6 +2,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
 import android.Manifest;
@@ -14,6 +15,7 @@
 import com.android.settings.R;
 import com.android.settings.TestConfig;
 import com.android.settings.search.indexing.FakeSettingsFragment;
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.After;
@@ -23,9 +25,6 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import java.util.HashSet;
-import java.util.Set;
-
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class SettingsSearchIndexablesProviderTest {
@@ -33,8 +32,8 @@
     private final String BASE_AUTHORITY = "com.android.settings";
 
     private SettingsSearchIndexablesProvider mProvider;
-
-    Set<Class> sProviderClasses;
+    private SearchFeatureProvider mFeatureProvider;
+    private FakeFeatureFactory mFakeFeatureFactory;
     Context mContext;
 
     @Before
@@ -49,15 +48,18 @@
         info.readPermission = Manifest.permission.READ_SEARCH_INDEXABLES;
         mProvider.attachInfo(mContext, info);
 
-        sProviderClasses = new HashSet<>(SearchIndexableResources.sProviders);
-        SearchIndexableResources.sProviders.clear();
-        SearchIndexableResources.sProviders.add(FakeSettingsFragment.class);
+        mFeatureProvider = new SearchFeatureProviderImpl();
+        mFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
+        mFeatureProvider.getSearchIndexableResources().getProviderValues()
+                .add(FakeSettingsFragment.class);
+        mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
+        mFakeFeatureFactory.searchFeatureProvider = mFeatureProvider;
     }
 
     @After
     public void cleanUp() {
-        SearchIndexableResources.sProviders.clear();
-        SearchIndexableResources.sProviders.addAll(sProviderClasses);
+        mFakeFeatureFactory.searchFeatureProvider = mock(
+                SearchFeatureProvider.class);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
index efd1cc5..f5d5ff0 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
@@ -19,6 +19,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
+
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -26,8 +28,9 @@
 
 import com.android.settings.TestConfig;
 import com.android.settings.search.FakeIndexProvider;
-import com.android.settings.search.SearchIndexableResources;
-import com.android.settings.testutils.DatabaseTestUtils;
+import com.android.settings.search.SearchFeatureProvider;
+import com.android.settings.search.SearchFeatureProviderImpl;
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.After;
@@ -37,9 +40,6 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import java.util.HashSet;
-import java.util.Set;
-
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class SliceBroadcastReceiverTest {
@@ -54,30 +54,30 @@
     private Context mContext;
     private SQLiteDatabase mDb;
     private SliceBroadcastReceiver mReceiver;
-
-    private Set<Class> mProviderClassesCopy;
+    private SearchFeatureProvider mSearchFeatureProvider;
+    private FakeFeatureFactory mFakeFeatureFactory;
 
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.application;
         mDb = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase();
         mReceiver = new SliceBroadcastReceiver();
-        mProviderClassesCopy = new HashSet<>(SearchIndexableResources.providerValues());
         SlicesDatabaseHelper helper = SlicesDatabaseHelper.getInstance(mContext);
         helper.setIndexedState();
+        mSearchFeatureProvider = new SearchFeatureProviderImpl();
+        mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
+        mFakeFeatureFactory.searchFeatureProvider = mSearchFeatureProvider;
     }
 
     @After
     public void cleanUp() {
-        DatabaseTestUtils.clearDb(mContext);
-        SearchIndexableResources.providerValues().clear();
-        SearchIndexableResources.providerValues().addAll(mProviderClassesCopy);
+        mFakeFeatureFactory.searchFeatureProvider = mock(SearchFeatureProvider.class);
     }
 
     @Test
     public void testOnReceive_toggleChanged() {
         String key = "key";
-        SearchIndexableResources.providerValues().clear();
+        mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
         insertSpecialCase(key);
         // Turn on toggle setting
         FakeToggleController fakeToggleController = new FakeToggleController(mContext, key);
diff --git a/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java b/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
index b5c0b5f..1d0ac41 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
@@ -18,11 +18,15 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
+
 import android.content.Context;
 
 import com.android.settings.TestConfig;
 import com.android.settings.search.FakeIndexProvider;
-import com.android.settings.search.SearchIndexableResources;
+import com.android.settings.search.SearchFeatureProvider;
+import com.android.settings.search.SearchFeatureProviderImpl;
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.After;
@@ -32,9 +36,7 @@
 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)
@@ -49,28 +51,31 @@
 
     Context mContext;
 
-    private Set<Class> mProviderClassesCopy;
-
     SliceDataConverter mSliceDataConverter;
+    SearchFeatureProvider mSearchFeatureProvider;
+    private FakeFeatureFactory mFakeFeatureFactory;
 
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.application;
-        mProviderClassesCopy = new HashSet<>(SearchIndexableResources.providerValues());
         mSliceDataConverter = new SliceDataConverter(mContext);
+        mSearchFeatureProvider = new SearchFeatureProviderImpl();
+        mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
+        mFakeFeatureFactory.searchFeatureProvider = mSearchFeatureProvider;
     }
 
     @After
     public void cleanUp() {
-        SearchIndexableResources.providerValues().clear();
-        SearchIndexableResources.providerValues().addAll(mProviderClassesCopy);
+        mFakeFeatureFactory.searchFeatureProvider = mock(
+                SearchFeatureProvider.class);
     }
 
     @Test
     @Config(qualifiers = "mcc999")
     public void testFakeProvider_convertsFakeData() {
-        SearchIndexableResources.providerValues().clear();
-        SearchIndexableResources.providerValues().add(FakeIndexProvider.class);
+        mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
+        mSearchFeatureProvider.getSearchIndexableResources().getProviderValues()
+                .add(FakeIndexProvider.class);
 
         List<SliceData> sliceDataList = mSliceDataConverter.getSliceData();
 
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index b4592b8..ad51f79 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -21,9 +21,11 @@
 
 import android.content.Context;
 
+import com.android.settings.accounts.AccountFeatureProvider;
 import com.android.settings.applications.ApplicationFeatureProvider;
 import com.android.settings.bluetooth.BluetoothFeatureProvider;
 import com.android.settings.connecteddevice.SmsMirroringFeatureProvider;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
 import com.android.settings.datausage.DataPlanFeatureProvider;
@@ -38,7 +40,6 @@
 import com.android.settings.security.SecurityFeatureProvider;
 import com.android.settings.slices.SlicesFeatureProvider;
 import com.android.settings.users.UserFeatureProvider;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.mockito.Answers;
 
@@ -55,7 +56,6 @@
     public final LocaleFeatureProvider localeFeatureProvider;
     public final ApplicationFeatureProvider applicationFeatureProvider;
     public final EnterprisePrivacyFeatureProvider enterprisePrivacyFeatureProvider;
-    public final SearchFeatureProvider searchFeatureProvider;
     public final SurveyFeatureProvider surveyFeatureProvider;
     public final SecurityFeatureProvider securityFeatureProvider;
     public final SuggestionFeatureProvider suggestionsFeatureProvider;
@@ -65,6 +65,8 @@
     public final DataPlanFeatureProvider dataPlanFeatureProvider;
     public final SmsMirroringFeatureProvider smsMirroringFeatureProvider;
     public final SlicesFeatureProvider slicesFeatureProvider;
+    public SearchFeatureProvider searchFeatureProvider;
+    public final AccountFeatureProvider mAccountFeatureProvider;
 
     /**
      * Call this in {@code @Before} method of the test class to use fake factory.
@@ -104,6 +106,7 @@
         dataPlanFeatureProvider = mock(DataPlanFeatureProvider.class);
         smsMirroringFeatureProvider = mock(SmsMirroringFeatureProvider.class);
         slicesFeatureProvider = mock(SlicesFeatureProvider.class);
+        mAccountFeatureProvider = mock(AccountFeatureProvider.class);
     }
 
     @Override
@@ -190,4 +193,9 @@
     public SlicesFeatureProvider getSlicesFeatureProvider() {
         return slicesFeatureProvider;
     }
+
+    @Override
+    public AccountFeatureProvider getAccountFeatureProvider() {
+        return mAccountFeatureProvider;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowEventLogWriter.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowEventLogWriter.java
index 9caf09f..dcced4e 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowEventLogWriter.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowEventLogWriter.java
@@ -19,7 +19,7 @@
 
 import android.content.Context;
 
-import com.android.settingslib.core.instrumentation.EventLogWriter;
+import com.android.settings.core.instrumentation.EventLogWriter;
 
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
diff --git a/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java b/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java
index 59a08ae..e44be0e 100644
--- a/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java
+++ b/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java
@@ -44,10 +44,10 @@
 
 import com.android.settings.TestConfig;
 import com.android.settings.applications.defaultapps.DefaultAppInfo;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.widget.RadioButtonPreference;
 import com.android.settings.wrapper.UserPackageWrapper;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.wrapper.PackageManagerWrapper;
 
 import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java
index 84549a6..63f89e6 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java
@@ -22,9 +22,9 @@
 
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.widget.SwitchWidgetController;
 import com.android.settings.wrapper.ConnectivityManagerWrapper;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiMasterSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiMasterSwitchPreferenceControllerTest.java
index 82569c7..1708e36 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiMasterSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiMasterSwitchPreferenceControllerTest.java
@@ -30,10 +30,10 @@
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.widget.MasterSwitchPreference;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
index ca2cac0..4f77435 100644
--- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
@@ -65,6 +65,7 @@
 import com.android.settings.R;
 import com.android.settings.TestConfig;
 import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.ShadowBidiFormatter;
 import com.android.settings.testutils.shadow.ShadowDevicePolicyManagerWrapper;
@@ -75,7 +76,6 @@
 import com.android.settings.widget.EntityHeaderController;
 import com.android.settings.wifi.WifiDetailPreference;
 import com.android.settings.wrapper.ConnectivityManagerWrapper;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.wifi.AccessPoint;
 
diff --git a/tests/robotests/src/com/android/settings/TetherServiceTest.java b/tests/robotests/src/com/android/settings/wifi/tether/TetherServiceTest.java
similarity index 72%
rename from tests/robotests/src/com/android/settings/TetherServiceTest.java
rename to tests/robotests/src/com/android/settings/wifi/tether/TetherServiceTest.java
index 0275c15..583bd52 100644
--- a/tests/robotests/src/com/android/settings/TetherServiceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/TetherServiceTest.java
@@ -13,17 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.settings;
+package com.android.settings.wifi.tether;
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.net.wifi.WifiManager;
 
+import com.android.settings.TestConfig;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import java.util.ArrayList;
@@ -87,4 +93,22 @@
         mService.cancelAlarmIfNecessary();
         verify(mContext).unregisterReceiver(any(HotspotOffReceiver.class));
     }
+
+    @Test
+    public void onDestroy_shouldUnregisterReceiver() {
+        final ArrayList<Integer> tethers = new ArrayList<>();
+        ReflectionHelpers.setField(mService, "mCurrentTethers", tethers);
+        ReflectionHelpers.setField(mService, "mBase", mContext);
+        final SharedPreferences prefs = mock(SharedPreferences .class);
+        final SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class);
+        when(mContext.getSharedPreferences(anyString(), anyInt())).thenReturn(prefs);
+        when(prefs.edit()).thenReturn(editor);
+        when(editor.putString(anyString(), anyString())).thenReturn(editor);
+        final HotspotOffReceiver hotspotOffReceiver = mock(HotspotOffReceiver.class);
+        mService.setHotspotOffReceiver(hotspotOffReceiver);
+
+        mService.onDestroy();
+
+        verify(hotspotOffReceiver).unregister();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
index 00d9585..dca6974 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
@@ -18,11 +18,15 @@
 
 import static android.arch.lifecycle.Lifecycle.Event.ON_START;
 import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.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;
 
@@ -65,6 +69,7 @@
         shadows = {
                 WifiTetherPreferenceControllerTest.ShadowWifiTetherSettings.class,
                 WifiTetherPreferenceControllerTest.ShadowWifiTetherSwitchBarController.class,
+                WifiTetherPreferenceControllerTest.ShadowWifiTetherSoftApManager.class
         })
 public class WifiTetherPreferenceControllerTest {
 
@@ -94,8 +99,9 @@
         when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
         when(mScreen.findPreference(anyString())).thenReturn(mPreference);
 
-        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[] {"1", "2"});
-        mController = new WifiTetherPreferenceController(mContext, mLifecycle);
+        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
+        mController = new WifiTetherPreferenceController(mContext, mLifecycle,
+                false /* initSoftApManager */);
     }
 
     @After
@@ -105,8 +111,9 @@
 
     @Test
     public void isAvailable_noTetherRegex_shouldReturnFalse() {
-        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[] {});
-        mController = new WifiTetherPreferenceController(mContext, mLifecycle);
+        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{});
+        mController = new WifiTetherPreferenceController(mContext, mLifecycle,
+                false /* initSoftApManager */);
 
         assertThat(mController.isAvailable()).isFalse();
     }
@@ -244,6 +251,19 @@
         }
     }
 
+    @Implements(WifiTetherSoftApManager.class)
+    public static final class ShadowWifiTetherSoftApManager {
+        @Implementation
+        public void registerSoftApCallback() {
+            // do nothing
+        }
+
+        @Implementation
+        public void unRegisterSoftApCallback() {
+            // do nothing
+        }
+    }
+
     @Implements(WifiTetherSwitchBarController.class)
     public static final class ShadowWifiTetherSwitchBarController {
 
diff --git a/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java b/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java
index 86e8dc1..b16c700 100644
--- a/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java
+++ b/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java
@@ -26,6 +26,7 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.util.ArraySet;
 
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.DatabaseIndexingUtils;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableResources;
@@ -54,7 +55,10 @@
     public void controllersInSearchShouldImplementPreferenceControllerMixin() {
         final Set<String> errorClasses = new ArraySet<>();
 
-        for (Class clazz : SearchIndexableResources.providerValues()) {
+        final SearchIndexableResources resources =
+                FeatureFactory.getFactory(mContext).getSearchFeatureProvider()
+                        .getSearchIndexableResources();
+        for (Class<?> clazz : resources.getProviderValues()) {
 
             final Indexable.SearchIndexProvider provider =
                     DatabaseIndexingUtils.getSearchIndexProvider(clazz);
diff --git a/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java b/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
index 36865a4..8fe2358 100644
--- a/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
+++ b/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
@@ -31,6 +31,7 @@
 import android.util.Log;
 import android.util.Xml;
 
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.DatabaseIndexingUtils;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
@@ -100,7 +101,10 @@
         final Set<String> uniqueKeys = new HashSet<>();
         final Set<String> nullKeyClasses = new HashSet<>();
         final Set<String> duplicatedKeys = new HashSet<>();
-        for (Class<?> clazz : SearchIndexableResources.providerValues()) {
+        final SearchIndexableResources resources =
+                FeatureFactory.getFactory(mContext).getSearchFeatureProvider()
+                        .getSearchIndexableResources();
+        for (Class<?> clazz : resources.getProviderValues()) {
             verifyPreferenceKeys(uniqueKeys, duplicatedKeys, nullKeyClasses, clazz);
         }
 
diff --git a/tests/unit/src/com/android/settings/core/UserRestrictionTest.java b/tests/unit/src/com/android/settings/core/UserRestrictionTest.java
index f37c30b..6d6f06c 100644
--- a/tests/unit/src/com/android/settings/core/UserRestrictionTest.java
+++ b/tests/unit/src/com/android/settings/core/UserRestrictionTest.java
@@ -32,6 +32,7 @@
 import android.util.Log;
 import android.util.Xml;
 
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.DatabaseIndexingUtils;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableResources;
@@ -65,7 +66,8 @@
             UserManager.DISALLOW_CONFIG_VPN,
             UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
             UserManager.DISALLOW_AIRPLANE_MODE,
-            UserManager.DISALLOW_CONFIG_BRIGHTNESS
+            UserManager.DISALLOW_CONFIG_BRIGHTNESS,
+            UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT
     );
 
     @Before
@@ -79,7 +81,10 @@
     @Test
     public void userRestrictionAttributeShouldBeValid()
             throws IOException, XmlPullParserException, Resources.NotFoundException {
-        for (Class<?> clazz : SearchIndexableResources.providerValues()) {
+        final SearchIndexableResources resources =
+                FeatureFactory.getFactory(mContext).getSearchFeatureProvider()
+                        .getSearchIndexableResources();
+        for (Class<?> clazz : resources.getProviderValues()) {
             verifyUserRestriction(clazz);
         }
     }
diff --git a/tests/unit/src/com/android/settings/TetherServiceTest.java b/tests/unit/src/com/android/settings/wifi/tether/TetherServiceTest.java
similarity index 98%
rename from tests/unit/src/com/android/settings/TetherServiceTest.java
rename to tests/unit/src/com/android/settings/wifi/tether/TetherServiceTest.java
index 899ea7a..7bf5798 100644
--- a/tests/unit/src/com/android/settings/TetherServiceTest.java
+++ b/tests/unit/src/com/android/settings/wifi/tether/TetherServiceTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.settings;
+package com.android.settings.wifi.tether;
 
 import static org.junit.Assert.*;
 import static org.mockito.Matchers.*;
@@ -49,17 +49,13 @@
 import android.content.SharedPreferences.Editor;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
-import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.test.ServiceTestCase;
-import android.test.mock.MockResources;
 import android.util.Log;
 
-import com.android.settings.TetherService;
-
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;