Merge changes from topic "location settings"

* changes:
  Display app stats for location permission
  Display recent location access in the widget
diff --git a/OWNERS b/OWNERS
index d2bc1ff..37124b2 100644
--- a/OWNERS
+++ b/OWNERS
@@ -7,8 +7,11 @@
 dehboxturtle@google.com
 dhnishi@google.com
 dling@google.com
+edgarwang@google.com
 jackqdyulei@google.com
 mfritze@google.com
+rafftsai@google.com
+tmfang@google.com
 zhfan@google.com
 
 # Emergency approvers in case the above are not available
diff --git a/res/drawable/ic_battery_saver_accent_24dp.xml b/res/drawable/ic_battery_saver_accent_24dp.xml
deleted file mode 100644
index c8def54..0000000
--- a/res/drawable/ic_battery_saver_accent_24dp.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<!--
-  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.
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorAccent">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M15,14l-2,0l0,2l-2,0l0,-2l-2,0l0,-2l2,0l0,-2l2,0l0,2l2,0z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M16.2,22.5H7.8c-1.3,0 -2.3,-1 -2.3,-2.3V5.8c0,-1.3 1,-2.3 2.3,-2.3h0.7v-2h7v2h0.7c1.3,0 2.3,1.1 2.3,2.3v14.3C18.5,21.5 17.5,22.5 16.2,22.5zM7.8,5.5c-0.2,0 -0.3,0.2 -0.3,0.3v14.3c0,0.2 0.2,0.3 0.3,0.3h8.3c0.2,0 0.3,-0.1 0.3,-0.3V5.8c0,-0.2 -0.1,-0.3 -0.3,-0.3h-2.7v-2h-3v2H7.8z"/>
-</vector>
diff --git a/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml b/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
index 0c938f8..4217867 100644
--- a/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
+++ b/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
@@ -24,28 +24,24 @@
 
     <include layout="@layout/wifi_dpp_fragment_header"/>
 
-    <FrameLayout
+    <com.android.settings.wifi.qrcode.QrPreviewLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-        <SurfaceView
+        android:layout_height="match_parent"
+        android:layout_gravity="center">
+        <TextureView
             android:id="@+id/preview_view"
-            android:layout_width="426dp"
-            android:layout_height="320dp"
-            android:layout_gravity="center"/>
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"/>
         <com.android.settings.wifi.qrcode.QrDecorateView
             android:id="@+id/decorate_view"
-            android:layout_width="426dp"
-            android:layout_height="320dp"
-            android:layout_gravity="center"/>
-    </FrameLayout>
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"/>
+    </com.android.settings.wifi.qrcode.QrPreviewLayout>
 
     <TextView android:id="@+id/error_message"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"/>
 
-    <include layout="@layout/wifi_dpp_fragment_footer"
-        android:gravity="center|bottom"/>
-
 </LinearLayout>
 
diff --git a/res/layout/face_enroll_introduction_footer.xml b/res/layout/face_enroll_introduction_footer.xml
index d73a8ed..15993f0 100644
--- a/res/layout/face_enroll_introduction_footer.xml
+++ b/res/layout/face_enroll_introduction_footer.xml
@@ -39,6 +39,6 @@
         style="@style/SuwGlifButton.Primary"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/suw_next_button_label" />
+        android:text="@string/wizard_next" />
 
 </com.google.android.setupdesign.view.ButtonBarLayout>
diff --git a/res/layout/fingerprint_enroll_introduction_footer.xml b/res/layout/fingerprint_enroll_introduction_footer.xml
index 1298d82..47d7657 100644
--- a/res/layout/fingerprint_enroll_introduction_footer.xml
+++ b/res/layout/fingerprint_enroll_introduction_footer.xml
@@ -39,6 +39,6 @@
         style="@style/SuwGlifButton.Primary"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/suw_next_button_label" />
+        android:text="@string/wizard_next" />
 
 </com.google.android.setupdesign.view.ButtonBarLayout>
diff --git a/res/layout/storage_wizard_footer.xml b/res/layout/storage_wizard_footer.xml
index 0ff40aa..a4cda18 100644
--- a/res/layout/storage_wizard_footer.xml
+++ b/res/layout/storage_wizard_footer.xml
@@ -27,7 +27,7 @@
         android:layout_height="wrap_content"
         android:onClick="onNavigateBack"
         android:visibility="gone"
-        android:text="@string/suw_back_button_label" />
+        android:text="@string/wizard_back" />
 
     <Space
         android:layout_width="0dp"
@@ -41,6 +41,6 @@
         android:layout_height="wrap_content"
         android:onClick="onNavigateNext"
         android:visibility="gone"
-        android:text="@string/suw_next_button_label" />
+        android:text="@string/wizard_next" />
 
 </LinearLayout>
diff --git a/res/layout/wifi_dpp_activity.xml b/res/layout/wifi_dpp_activity.xml
index a833dcc..cb82f66 100644
--- a/res/layout/wifi_dpp_activity.xml
+++ b/res/layout/wifi_dpp_activity.xml
@@ -22,12 +22,6 @@
     android:layout_height="match_parent"
     android:orientation="vertical">
 
-    <ImageView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/ic_wifi_signal_4"
-        android:layout_gravity="center"/>
-
     <LinearLayout
         android:id="@+id/fragment_container"
         android:layout_width="match_parent"
diff --git a/res/layout/wifi_dpp_fragment_header.xml b/res/layout/wifi_dpp_fragment_header.xml
index 99b246f..266a3e8 100644
--- a/res/layout/wifi_dpp_fragment_header.xml
+++ b/res/layout/wifi_dpp_fragment_header.xml
@@ -17,18 +17,42 @@
 
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/EntityHeader"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical">
+    android:layout_centerHorizontal="true"
+    android:gravity="center_horizontal"
+    android:orientation="vertical"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp">
 
-    <TextView android:id="@+id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"/>
+    <ImageView
+        android:id="@+id/header_icon"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:scaleType="fitCenter"/>
 
-    <TextView android:id="@+id/description"
-        android:layout_width="wrap_content"
+    <TextView
+        android:id="@+id/title"
+        style="@style/TextAppearance.EntityHeaderTitle"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"/>
+        android:gravity="center_horizontal"
+        android:textAlignment="center"
+        android:layout_marginTop="8dp"
+        android:paddingStart="32dp"
+        android:paddingEnd="32dp"/>
+
+    <TextView
+        android:id="@+id/description"
+        style="@style/TextAppearance.EntityHeaderSummary"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:singleLine="false"
+        android:gravity="center_horizontal"
+        android:textAlignment="center"
+        android:layout_marginTop="2dp"
+        android:paddingStart="32dp"
+        android:paddingEnd="32dp"/>
 
 </LinearLayout>
diff --git a/res/layout/wifi_dpp_qrcode_scanner_fragment.xml b/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
index 0beacff..913998f 100644
--- a/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
+++ b/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
@@ -24,20 +24,20 @@
 
     <include layout="@layout/wifi_dpp_fragment_header"/>
 
-    <FrameLayout
+    <com.android.settings.wifi.qrcode.QrPreviewLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-        <SurfaceView
+        android:layout_height="match_parent">
+        <TextureView
             android:id="@+id/preview_view"
-            android:layout_width="320dp"
-            android:layout_height="426dp"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
             android:layout_gravity="center"/>
         <com.android.settings.wifi.qrcode.QrDecorateView
             android:id="@+id/decorate_view"
-            android:layout_width="320dp"
-            android:layout_height="426dp"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
             android:layout_gravity="center"/>
-    </FrameLayout>
+    </com.android.settings.wifi.qrcode.QrPreviewLayout>
 
     <TextView android:id="@+id/error_message"
         android:layout_width="wrap_content"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index be9cc14..1aaa374 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6766,7 +6766,7 @@
     <string name="wizard_back">Back</string>
     <!-- Wizard next button label [CHAR LIMIT=25] -->
     <string name="wizard_next">Next</string>
-    <!-- Wizard next button label [CHAR LIMIT=25] -->
+    <!-- Wizard finish button label [CHAR LIMIT=25] -->
     <string name="wizard_finish">Finish</string>
 
     <!-- An option in a photo selection dialog, if there is no photo yet [CHAR LIMIT=50] -->
@@ -9041,12 +9041,6 @@
     <!-- Summary of condition that do not disturb is on [CHAR LIMIT=36] -->
     <string name="condition_zen_summary">Impacts what you hear and see</string>
 
-    <!-- Title of condition that battery saver is on [CHAR LIMIT=30] -->
-    <string name="condition_battery_title">Battery Saver is on</string>
-
-    <!-- Summary of condition that battery saver is on [CHAR LIMIT=NONE] -->
-    <string name="condition_battery_summary">Features restricted</string>
-
     <!-- Title of condition that cellular data is off [CHAR LIMIT=50] -->
     <string name="condition_cellular_title">Mobile data is off</string>
 
diff --git a/res/xml/battery_saver_settings.xml b/res/xml/battery_saver_settings.xml
index a086018..89f7eab 100644
--- a/res/xml/battery_saver_settings.xml
+++ b/res/xml/battery_saver_settings.xml
@@ -26,18 +26,6 @@
         android:title="@string/battery_saver_schedule_settings_title"
         settings:controller="com.android.settings.fuelgauge.batterysaver.BatterySaverSchedulePreferenceController"/>
 
-    <!-- Turn on automatically -->
-    <SwitchPreference
-        android:key="auto_battery_saver"
-        android:title="@string/battery_saver_auto_title"
-        settings:controller="com.android.settings.fuelgauge.batterysaver.AutoBatterySaverPreferenceController" />
-
-    <com.android.settings.widget.SeekBarPreference
-        android:key="battery_saver_seek_bar"
-        android:title="@string/battery_saver_seekbar_title_placeholder"
-        android:max="15"
-        android:min="1" />
-
     <com.android.settings.widget.TwoStateButtonPreference
         android:key="battery_saver"
         android:title="@string/battery_saver"
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 8e745a3..61a247d 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -66,7 +66,9 @@
 import com.android.settingslib.RestrictedLockUtilsInternal;
 
 import com.google.android.setupcompat.TemplateLayout;
-import com.google.android.setupdesign.template.ButtonFooterMixin;
+import com.google.android.setupcompat.item.FooterButton;
+import com.google.android.setupcompat.item.FooterButton.ButtonType;
+import com.google.android.setupcompat.template.ButtonFooterMixin;
 
 import java.util.List;
 
@@ -96,7 +98,7 @@
 
     private View mContentView;
     @VisibleForTesting
-    Button mInitiateButton;
+    FooterButton mInitiateButton;
     private View mExternalStorageContainer;
     @VisibleForTesting
     CheckBox mExternalStorage;
@@ -416,12 +418,15 @@
 
         final TemplateLayout layout = mContentView.findViewById(R.id.setup_wizard_layout);
         final ButtonFooterMixin buttonFooterMixin = layout.getMixin(ButtonFooterMixin.class);
-        buttonFooterMixin.removeAllViews();
-        buttonFooterMixin.addSpace();
-        buttonFooterMixin.addSpace();
-        mInitiateButton = buttonFooterMixin.addButton(R.string.master_clear_button_text,
-                R.style.SuwGlifButton_Primary);
-        mInitiateButton.setOnClickListener(mInitiateListener);
+        buttonFooterMixin.setPrimaryButton(
+                new FooterButton(
+                        getActivity(),
+                        R.string.master_clear_button_text,
+                        mInitiateListener,
+                        ButtonType.OTHER,
+                        R.style.SuwGlifButton_Primary)
+        );
+        mInitiateButton = buttonFooterMixin.getPrimaryButton();
     }
 
     private void getContentDescription(View v, StringBuffer description) {
diff --git a/src/com/android/settings/MasterClearConfirm.java b/src/com/android/settings/MasterClearConfirm.java
index ffbb2aa..b2bf838 100644
--- a/src/com/android/settings/MasterClearConfirm.java
+++ b/src/com/android/settings/MasterClearConfirm.java
@@ -45,7 +45,9 @@
 import com.android.settingslib.RestrictedLockUtilsInternal;
 
 import com.google.android.setupcompat.TemplateLayout;
-import com.google.android.setupdesign.template.ButtonFooterMixin;
+import com.google.android.setupcompat.item.FooterButton;
+import com.google.android.setupcompat.item.FooterButton.ButtonType;
+import com.google.android.setupcompat.template.ButtonFooterMixin;
 
 /**
  * Confirm and execute a reset of the device to a clean "just out of the box"
@@ -153,11 +155,14 @@
         final TemplateLayout layout = mContentView.findViewById(R.id.setup_wizard_layout);
 
         final ButtonFooterMixin buttonFooterMixin = layout.getMixin(ButtonFooterMixin.class);
-        buttonFooterMixin.removeAllViews();
-        buttonFooterMixin.addSpace();
-        buttonFooterMixin.addSpace();
-        buttonFooterMixin.addButton(R.string.master_clear_button_text,
-                R.style.SuwGlifButton_Primary).setOnClickListener(mFinalClickListener);
+        buttonFooterMixin.setPrimaryButton(
+                new FooterButton(
+                        getActivity(),
+                        R.string.master_clear_button_text,
+                        mFinalClickListener,
+                        ButtonType.OTHER,
+                        R.style.SuwGlifButton_Primary)
+        );
     }
 
     private void setUpActionBarAndTitle() {
diff --git a/src/com/android/settings/core/FeatureFlags.java b/src/com/android/settings/core/FeatureFlags.java
index b350778..7f14c0d 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 WIFI_MAC_RANDOMIZATION = "settings_wifi_mac_randomization";
     public static final String NETWORK_INTERNET_V2 = "settings_network_and_internet_v2";
     public static final String WIFI_SHARING = "settings_wifi_sharing";
+    public static final String SLICE_INJECTION = "settings_slice_injection";
 }
diff --git a/src/com/android/settings/fuelgauge/BatterySaverController.java b/src/com/android/settings/fuelgauge/BatterySaverController.java
index 28cc893..cd32c6c 100644
--- a/src/com/android/settings/fuelgauge/BatterySaverController.java
+++ b/src/com/android/settings/fuelgauge/BatterySaverController.java
@@ -15,6 +15,7 @@
  */
 package com.android.settings.fuelgauge;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.os.Handler;
@@ -22,6 +23,7 @@
 import android.os.PowerManager;
 import android.provider.Settings;
 
+import android.provider.Settings.Global;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
@@ -31,6 +33,7 @@
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
 
 public class BatterySaverController extends BasePreferenceController
         implements LifecycleObserver, OnStart, OnStop, BatterySaverReceiver.BatterySaverListener {
@@ -45,6 +48,7 @@
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mBatteryStateChangeReceiver = new BatterySaverReceiver(context);
         mBatteryStateChangeReceiver.setBatterySaverListener(this);
+        BatterySaverUtils.revertScheduleToNoneIfNeeded(context);
     }
 
     @Override
@@ -81,16 +85,23 @@
 
     @Override
     public CharSequence getSummary() {
+        final ContentResolver resolver = mContext.getContentResolver();
         final boolean isPowerSaveOn = mPowerManager.isPowerSaveMode();
-        final int percent = Settings.Global.getInt(mContext.getContentResolver(),
+        final int percent = Settings.Global.getInt(resolver,
                 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+        final int mode = Settings.Global.getInt(resolver,
+                Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_PERCENTAGE);
         if (isPowerSaveOn) {
             return mContext.getString(R.string.battery_saver_on_summary);
-        } else if (percent != 0) {
-            return mContext.getString(R.string.battery_saver_off_scheduled_summary,
-                    Utils.formatPercentage(percent));
+        } else if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
+            if (percent != 0) {
+                return mContext.getString(R.string.battery_saver_off_scheduled_summary,
+                        Utils.formatPercentage(percent));
+            } else {
+                return mContext.getString(R.string.battery_saver_off_summary);
+            }
         } else {
-            return mContext.getString(R.string.battery_saver_off_summary);
+            return mContext.getString(R.string.battery_saver_auto_routine);
         }
     }
 
diff --git a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceController.java
deleted file mode 100644
index c0ecbf9..0000000
--- a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceController.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.batterysaver;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-
-import com.android.settings.core.TogglePreferenceController;
-import com.android.settingslib.fuelgauge.BatterySaverUtils;
-
-/**
- * Controller that update whether to turn on battery saver automatically
- */
-public class AutoBatterySaverPreferenceController extends TogglePreferenceController implements
-        Preference.OnPreferenceChangeListener {
-
-    /**
-     * Default value for {@link Settings.Global#LOW_POWER_MODE_TRIGGER_LEVEL}.
-     */
-    static final int DEFAULT_TRIGGER_LEVEL = 0;
-
-    /**
-     * The default value to set to {@link Settings.Global#LOW_POWER_MODE_TRIGGER_LEVEL} when the
-     * user enables battery saver.
-     */
-    private final int mDefaultTriggerLevelForOn;
-
-    @VisibleForTesting
-    static final String KEY_AUTO_BATTERY_SAVER = "auto_battery_saver";
-
-    public AutoBatterySaverPreferenceController(Context context) {
-        super(context, KEY_AUTO_BATTERY_SAVER);
-        mDefaultTriggerLevelForOn = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_lowBatteryWarningLevel);
-    }
-
-    @Override
-    public int getAvailabilityStatus() {
-        return AVAILABLE;
-    }
-
-    @Override
-    public boolean isChecked() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, DEFAULT_TRIGGER_LEVEL) != 0;
-    }
-
-    @Override
-    public boolean setChecked(boolean isChecked) {
-        BatterySaverUtils.setAutoBatterySaverTriggerLevel(mContext,
-                isChecked ? mDefaultTriggerLevelForOn : 0);
-        return true;
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java
deleted file mode 100644
index 0fbba0b..0000000
--- a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.batterysaver;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.widget.SeekBarPreference;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnStart;
-import com.android.settingslib.core.lifecycle.events.OnStop;
-
-/**
- * Controller that update the battery saver seekbar
- */
-public class AutoBatterySeekBarPreferenceController extends BasePreferenceController implements
-        LifecycleObserver, OnStart, OnStop, SeekBarPreference.OnPreferenceChangeListener {
-    private static final String TAG = "AutoBatterySeekBarPreferenceController";
-    private static final int INTERVAL = 5;
-    @VisibleForTesting
-    static final String KEY_AUTO_BATTERY_SEEK_BAR = "battery_saver_seek_bar";
-    private SeekBarPreference mPreference;
-    private AutoBatterySaverSettingObserver mContentObserver;
-
-    public AutoBatterySeekBarPreferenceController(Context context, Lifecycle lifecycle) {
-        super(context, KEY_AUTO_BATTERY_SEEK_BAR);
-        mContentObserver = new AutoBatterySaverSettingObserver(new Handler(Looper.getMainLooper()));
-        if (lifecycle != null) {
-            lifecycle.addObserver(this);
-        }
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        mPreference = (SeekBarPreference) screen.findPreference(
-                KEY_AUTO_BATTERY_SEEK_BAR);
-        mPreference.setContinuousUpdates(true);
-        mPreference.setAccessibilityRangeInfoType(
-                AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_PERCENT);
-        updatePreference(mPreference);
-    }
-
-    @Override
-    public int getAvailabilityStatus() {
-        return AVAILABLE;
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        super.updateState(preference);
-        updatePreference(preference);
-    }
-
-    @Override
-    public void onStart() {
-        mContentObserver.registerContentObserver();
-    }
-
-    @Override
-    public void onStop() {
-        mContentObserver.unRegisterContentObserver();
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final int progress = (int) newValue;
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, progress * INTERVAL);
-        return true;
-    }
-
-    @VisibleForTesting
-    void updatePreference(Preference preference) {
-        final ContentResolver contentResolver = mContext.getContentResolver();
-
-        // Override the max value with LOW_POWER_MODE_TRIGGER_LEVEL_MAX, if set.
-        final int maxLevel = Settings.Global.getInt(contentResolver,
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, 0) / INTERVAL;
-        if (maxLevel > 0) {
-            if (!(preference instanceof SeekBarPreference)) {
-                Log.e(TAG, "Unexpected preference class: " + preference.getClass());
-            } else {
-                final SeekBarPreference seekBarPreference = (SeekBarPreference) preference;
-                if (maxLevel < seekBarPreference.getMin()) {
-                    Log.e(TAG, "LOW_POWER_MODE_TRIGGER_LEVEL_MAX too low; ignored.");
-                } else {
-                    seekBarPreference.setMax(maxLevel);
-                }
-            }
-        }
-
-        // Set the current value.
-        final int level = Settings.Global.getInt(contentResolver,
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL,
-                AutoBatterySaverPreferenceController.DEFAULT_TRIGGER_LEVEL);
-        if (level == 0) {
-            preference.setVisible(false);
-        } else {
-            preference.setVisible(true);
-            preference.setTitle(mContext.getString(R.string.battery_saver_seekbar_title,
-                    Utils.formatPercentage(level)));
-            SeekBarPreference seekBarPreference = (SeekBarPreference) preference;
-            seekBarPreference.setProgress(level / INTERVAL);
-            seekBarPreference.setSeekBarContentDescription(
-                    mContext.getString(R.string.battery_saver_turn_on_automatically_title));
-        }
-    }
-
-    /**
-     * Observer that listens to change from {@link Settings.Global#LOW_POWER_MODE_TRIGGER_LEVEL}
-     */
-    private final class AutoBatterySaverSettingObserver extends ContentObserver {
-        private final Uri mUri = Settings.Global.getUriFor(
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL);
-        private final ContentResolver mContentResolver;
-
-        public AutoBatterySaverSettingObserver(Handler handler) {
-            super(handler);
-            mContentResolver = mContext.getContentResolver();
-        }
-
-        public void registerContentObserver() {
-            mContentResolver.registerContentObserver(mUri, false, this);
-        }
-
-        public void unRegisterContentObserver() {
-            mContentResolver.unregisterContentObserver(this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, int userId) {
-            if (mUri.equals(uri)) {
-                updatePreference(mPreference);
-            }
-        }
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java
index cc6aa00..868c73f 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java
@@ -26,7 +26,13 @@
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
 
+/**
+ * Simple controller to navigate users to the scheduling page from
+ * "Settings > Battery > Battery Saver". Also updates the summary for preference based on
+ * the currently selected settings.
+ */
 public class BatterySaverSchedulePreferenceController extends BasePreferenceController {
 
     @VisibleForTesting
@@ -36,6 +42,7 @@
 
     public BatterySaverSchedulePreferenceController(Context context) {
         super(context, KEY_BATTERY_SAVER_SCHEDULE);
+        BatterySaverUtils.revertScheduleToNoneIfNeeded(context);
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java
new file mode 100644
index 0000000..d49eb0a
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.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.batterysaver;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+
+/**
+ * Responds to user actions in the Settings > Battery > Set a Schedule Screen
+ *
+ * Note that this is not a preference controller since that screen does not inherit from
+ * DashboardFragment.
+ *
+ * Will call the appropriate power manager APIs and modify the correct settings to enable
+ * users to control their automatic battery saver toggling preferences.
+ * See {@link Settings.Global#AUTOMATIC_POWER_SAVER_MODE} for more details.
+ */
+public class BatterySaverScheduleRadioButtonsController {
+
+    public static final String KEY_NO_SCHEDULE = "key_battery_saver_no_schedule";
+    public static final String KEY_ROUTINE = "key_battery_saver_routine";
+    public static final String KEY_PERCENTAGE = "key_battery_saver_percentage";
+
+    private Context mContext;
+    private BatterySaverScheduleSeekBarController mSeekBarController;
+
+    public BatterySaverScheduleRadioButtonsController(Context context,
+            BatterySaverScheduleSeekBarController seekbar) {
+        mContext = context;
+        mSeekBarController = seekbar;
+    }
+
+    public String getDefaultKey() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        // Note: this can also be obtained via PowerManager.getPowerSaveMode()
+        final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+        // if mode is "dynamic" we are in routine mode, percentage with non-zero threshold is
+        // percentage mode, otherwise it is no schedule mode
+        if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
+            final int threshold =
+                    Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+            if (threshold <= 0) {
+                return KEY_NO_SCHEDULE;
+            }
+            return KEY_PERCENTAGE;
+        }
+        return KEY_ROUTINE;
+    }
+
+    public boolean setDefaultKey(String key) {
+        final ContentResolver resolver = mContext.getContentResolver();
+        switch(key) {
+            case KEY_NO_SCHEDULE:
+                Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                        PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+                Settings.Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+                break;
+            case KEY_PERCENTAGE:
+                Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                        PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+                Settings.Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
+                break;
+            case KEY_ROUTINE:
+                Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                        PowerManager.POWER_SAVER_MODE_DYNAMIC);
+                break;
+            default:
+                throw new IllegalStateException(
+                        "Not a valid key for " + this.getClass().getSimpleName());
+        }
+        mSeekBarController.updateSeekBar();
+        return true;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java
new file mode 100644
index 0000000..215f400
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java
@@ -0,0 +1,100 @@
+/*
+ * 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.batterysaver;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.PreferenceScreen;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.widget.SeekBarPreference;
+
+/**
+ * Responds to user actions in the Settings > Battery > Set a Schedule Screen for the seekbar.
+ * Note that this seekbar is only visible when the radio button selected is "Percentage".
+ *
+ * Note that this is not a preference controller since that screen does not inherit from
+ * DashboardFragment.
+ *
+ * Will call the appropriate power manager APIs and modify the correct settings to enable
+ * users to control their automatic battery saver toggling preferences.
+ * See {@link Settings.Global#AUTOMATIC_POWER_SAVER_MODE} for more details.
+ */
+public class BatterySaverScheduleSeekBarController implements
+        OnPreferenceChangeListener {
+
+    public static final int MAX_SEEKBAR_VALUE = 15;
+    public static final int MIN_SEEKBAR_VALUE = 1;
+    public static final String KEY_BATTERY_SAVER_SEEK_BAR = "battery_saver_seek_bar";
+
+    @VisibleForTesting
+    public SeekBarPreference mSeekBarPreference;
+    private Context mContext;
+
+    public BatterySaverScheduleSeekBarController(Context context) {
+        mContext = context;
+        mSeekBarPreference = new SeekBarPreference(context);
+        mSeekBarPreference.setOnPreferenceChangeListener(this);
+        mSeekBarPreference.setMax(BatterySaverScheduleSeekBarController.MAX_SEEKBAR_VALUE);
+        mSeekBarPreference.setMin(BatterySaverScheduleSeekBarController.MIN_SEEKBAR_VALUE);
+        mSeekBarPreference.setKey(KEY_BATTERY_SAVER_SEEK_BAR);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        // The nits are in intervals of 5%
+        final int percentage = ((Integer) newValue) * 5;
+        Settings.Global.putInt(mContext.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL,
+                percentage);
+        preference.setTitle(mContext.getString(
+                R.string.battery_saver_seekbar_title, Utils.formatPercentage(percentage)));
+        return true;
+    }
+
+    public void updateSeekBar() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        // Note: this can also be obtained via PowerManager.getPowerSaveMode()
+        final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+        // if mode is "dynamic" we are in routine mode, percentage with non-zero threshold is
+        // percentage mode, otherwise it is no schedule mode
+        if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
+            final int threshold =
+                    Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+            if (threshold <= 0) {
+                mSeekBarPreference.setVisible(false);
+            } else {
+                mSeekBarPreference.setVisible(true);
+                mSeekBarPreference.setProgress(MIN_SEEKBAR_VALUE);
+                mSeekBarPreference.setTitle(mContext.getString(
+                        R.string.battery_saver_seekbar_title,
+                        Utils.formatPercentage(MIN_SEEKBAR_VALUE * 5)));
+            }
+        } else {
+            mSeekBarPreference.setVisible(false);
+        }
+    }
+
+    public void addToScreen(PreferenceScreen screen) {
+        screen.addPreference(mSeekBarPreference);
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java
index fad9f31..6e9c711 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java
@@ -18,24 +18,32 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.text.TextUtils;
 import android.view.View;
 import androidx.preference.PreferenceScreen;
 import com.android.settings.widget.RadioButtonPickerFragment;
 import com.android.settings.R;
 import com.android.settings.widget.RadioButtonPreference;
-import com.android.settings.widget.SeekBarPreference;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
 import com.android.settingslib.widget.CandidateInfo;
 import com.google.common.collect.Lists;
 import java.util.List;
 
+/**
+ * Fragment that allows users to customize their automatic battery saver mode settings.
+ *
+ * Location: Settings > Battery > Battery Saver > Set a Schedule
+ * See {@link BatterySaverSchedulePreferenceController} for the controller that manages navigation
+ * to this screen from "Settings > Battery > Battery Saver" and the summary.
+ * See {@link BatterySaverScheduleRadioButtonsController} &
+ * {@link BatterySaverScheduleSeekBarController} for the controller that manages user
+ * interactions in this screen.
+ */
 public class BatterySaverScheduleSettings extends RadioButtonPickerFragment {
 
-    private static final String KEY_NO_SCHEDULE = "key_battery_saver_no_schedule";
-    private static final String KEY_ROUTINE = "key_battery_saver_routine";
-    private static final String KEY_PERCENTAGE = "key_battery_saver_percentage";
-    public static final int MAX_SEEKBAR_VALUE = 15;
-    public static final int MIN_SEEKBAR_VALUE = 1;
-    public static final String KEY_BATTERY_SAVER_SEEK_BAR = "battery_saver_seek_bar";
+    public BatterySaverScheduleRadioButtonsController mRadioButtonController;
+    private BatterySaverScheduleSeekBarController mSeekBarController;
 
     @Override
     protected int getPreferenceScreenResId() {
@@ -43,23 +51,44 @@
     }
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mSeekBarController = new BatterySaverScheduleSeekBarController(context);
+        mRadioButtonController = new BatterySaverScheduleRadioButtonsController(
+                context, mSeekBarController);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
     protected List<? extends CandidateInfo> getCandidates() {
         Context context = getContext();
         List<CandidateInfo> candidates = Lists.newArrayList();
+        String routineProviderApp = getContext().getResources()
+                .getString(com.android.internal.R.string.config_batterySaverScheduleProvider);
         candidates.add(new BatterySaverScheduleCandidateInfo(
                 context.getText(R.string.battery_saver_auto_no_schedule),
                 /* summary */ null,
-                KEY_NO_SCHEDULE,
+                BatterySaverScheduleRadioButtonsController.KEY_NO_SCHEDULE,
                 /* enabled */ true));
-        candidates.add(new BatterySaverScheduleCandidateInfo(
-                context.getText(R.string.battery_saver_auto_routine),
-                context.getText(R.string.battery_saver_auto_routine_summary),
-                KEY_ROUTINE,
-                /* enabled */ true));
+        // only add routine option if an app has been specified
+        if (!TextUtils.isEmpty(routineProviderApp)) {
+            candidates.add(new BatterySaverScheduleCandidateInfo(
+                    context.getText(R.string.battery_saver_auto_routine),
+                    context.getText(R.string.battery_saver_auto_routine_summary),
+                    BatterySaverScheduleRadioButtonsController.KEY_ROUTINE,
+                    /* enabled */ true));
+        } else {
+            // Make sure routine is not selected if no provider app is configured
+            BatterySaverUtils.revertScheduleToNoneIfNeeded(context);
+        }
         candidates.add(new BatterySaverScheduleCandidateInfo(
                 context.getText(R.string.battery_saver_auto_percentage),
                 /* summary */ null,
-                KEY_PERCENTAGE,
+                BatterySaverScheduleRadioButtonsController.KEY_PERCENTAGE,
                 /* enabled */ true));
 
         return candidates;
@@ -79,22 +108,18 @@
 
     @Override
     protected void addStaticPreferences(PreferenceScreen screen) {
-        SeekBarPreference seekbar = new SeekBarPreference(getContext());
-        seekbar.setMax(MAX_SEEKBAR_VALUE);
-        seekbar.setMin(MIN_SEEKBAR_VALUE);
-        seekbar.setTitle(R.string.battery_saver_seekbar_title_placeholder);
-        seekbar.setKey(KEY_BATTERY_SAVER_SEEK_BAR);
-        screen.addPreference(seekbar);
+        mSeekBarController.updateSeekBar();
+        mSeekBarController.addToScreen(screen);
     }
 
     @Override
     protected String getDefaultKey() {
-        return null;
+        return mRadioButtonController.getDefaultKey();
     }
 
     @Override
     protected boolean setDefaultKey(String key) {
-        return false;
+        return mRadioButtonController.setDefaultKey(key);
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java
index 8b0409c..6aef9b5 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java
@@ -61,23 +61,10 @@
     }
 
     @Override
-    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
-        return buildPreferenceControllers(context, getSettingsLifecycle());
-    }
-
-    @Override
     public int getHelpResource() {
         return R.string.help_url_battery_saver_settings;
     }
 
-    private static List<AbstractPreferenceController> buildPreferenceControllers(
-            Context context, Lifecycle lifecycle) {
-        final List<AbstractPreferenceController> controllers = new ArrayList<>();
-        controllers.add(new AutoBatterySaverPreferenceController(context));
-        controllers.add(new AutoBatterySeekBarPreferenceController(context, lifecycle));
-        return controllers;
-    }
-
     /**
      * For Search.
      */
@@ -90,11 +77,5 @@
                     sir.xmlResId = R.xml.battery_saver_settings;
                     return Arrays.asList(sir);
                 }
-
-                @Override
-                public List<AbstractPreferenceController> createPreferenceControllers(
-                        Context context) {
-                    return buildPreferenceControllers(context, null);
-                }
             };
 }
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionController.java b/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionController.java
deleted file mode 100644
index bce7c5d..0000000
--- a/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionController.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.homepage.contextualcards.conditional;
-
-import android.content.Context;
-import android.os.PowerManager;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.R;
-import com.android.settings.core.SubSettingLauncher;
-import com.android.settings.fuelgauge.BatterySaverReceiver;
-import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
-import com.android.settings.homepage.contextualcards.ContextualCard;
-import com.android.settingslib.fuelgauge.BatterySaverUtils;
-
-import java.util.Objects;
-
-public class BatterySaverConditionController implements ConditionalCardController,
-        BatterySaverReceiver.BatterySaverListener {
-    static final int ID = Objects.hash("BatterySaverConditionController");
-
-    private final Context mAppContext;
-    private final ConditionManager mConditionManager;
-    private final BatterySaverReceiver mReceiver;
-    private final PowerManager mPowerManager;
-
-    public BatterySaverConditionController(Context appContext, ConditionManager conditionManager) {
-        mAppContext = appContext;
-        mConditionManager = conditionManager;
-        mPowerManager = appContext.getSystemService(PowerManager.class);
-        mReceiver = new BatterySaverReceiver(appContext);
-        mReceiver.setBatterySaverListener(this);
-    }
-
-    @Override
-    public long getId() {
-        return ID;
-    }
-
-    @Override
-    public boolean isDisplayable() {
-        return mPowerManager.isPowerSaveMode();
-    }
-
-    @Override
-    public void onPrimaryClick(Context context) {
-        new SubSettingLauncher(context)
-                .setDestination(BatterySaverSettings.class.getName())
-                .setSourceMetricsCategory(MetricsProto.MetricsEvent.DASHBOARD_SUMMARY)
-                .setTitleRes(R.string.battery_saver)
-                .launch();
-    }
-
-    @Override
-    public void onActionClick() {
-        BatterySaverUtils.setPowerSaveMode(mAppContext, false,
-                /*needFirstTimeWarning*/ false);
-    }
-
-    @Override
-    public ContextualCard buildContextualCard() {
-        return new ConditionalContextualCard.Builder()
-                .setConditionId(ID)
-                .setMetricsConstant(MetricsProto.MetricsEvent.SETTINGS_CONDITION_BATTERY_SAVER)
-                .setActionText(mAppContext.getText(R.string.condition_turn_off))
-                .setName(mAppContext.getPackageName() + "/"
-                        + mAppContext.getText(R.string.condition_battery_title))
-                .setTitleText(mAppContext.getText(R.string.condition_battery_title).toString())
-                .setSummaryText(mAppContext.getText(R.string.condition_battery_summary).toString())
-                .setIconDrawable(mAppContext.getDrawable(R.drawable.ic_battery_saver_accent_24dp))
-                .setIsHalfWidth(true)
-                .build();
-    }
-
-    @Override
-    public void startMonitoringStateChange() {
-        mReceiver.setListening(true);
-    }
-
-    @Override
-    public void stopMonitoringStateChange() {
-        mReceiver.setListening(false);
-    }
-
-    @Override
-    public void onPowerSaveModeChanged() {
-        mConditionManager.onConditionChanged();
-    }
-
-    @Override
-    public void onBatteryChanged(boolean pluggedIn) {
-
-    }
-}
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java b/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java
index c741b98..39f4903 100644
--- a/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java
+++ b/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java
@@ -154,7 +154,6 @@
         mCardControllers.add(new AirplaneModeConditionController(mAppContext, this /* manager */));
         mCardControllers.add(
                 new BackgroundDataConditionController(mAppContext, this /* manager */));
-        mCardControllers.add(new BatterySaverConditionController(mAppContext, this /* manager */));
         mCardControllers.add(new CellularDataConditionController(mAppContext, this /* manager */));
         mCardControllers.add(new DndConditionCardController(mAppContext, this /* manager */));
         mCardControllers.add(new HotspotConditionController(mAppContext, this /* manager */));
diff --git a/src/com/android/settings/network/MultiNetworkHeaderController.java b/src/com/android/settings/network/MultiNetworkHeaderController.java
index 881aaa2..8860c47 100644
--- a/src/com/android/settings/network/MultiNetworkHeaderController.java
+++ b/src/com/android/settings/network/MultiNetworkHeaderController.java
@@ -18,7 +18,9 @@
 
 import android.content.Context;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.wifi.WifiConnectionPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import androidx.annotation.VisibleForTesting;
@@ -29,9 +31,11 @@
 // are two or more active mobile subscriptions. It shows an overview of available network
 // connections with an entry for wifi (if connected) and an entry for each subscription.
 public class MultiNetworkHeaderController extends BasePreferenceController implements
+        WifiConnectionPreferenceController.UpdateListener,
         SubscriptionsPreferenceController.UpdateListener {
     public static final String TAG = "MultiNetworkHdrCtrl";
 
+    private WifiConnectionPreferenceController mWifiController;
     private SubscriptionsPreferenceController mSubscriptionsController;
     private PreferenceCategory mPreferenceCategory;
 
@@ -40,13 +44,22 @@
     }
 
     public void init(Lifecycle lifecycle) {
+        mWifiController = createWifiController(lifecycle);
         mSubscriptionsController = createSubscriptionsController(lifecycle);
-        // TODO(asargent) - add in a controller for showing wifi status here
+    }
+
+    @VisibleForTesting
+    WifiConnectionPreferenceController createWifiController(Lifecycle lifecycle) {
+        final int prefOrder = 0;
+        return new WifiConnectionPreferenceController(mContext, lifecycle, this, mPreferenceKey,
+                prefOrder, MetricsProto.MetricsEvent.SETTINGS_NETWORK_CATEGORY);
     }
 
     @VisibleForTesting
     SubscriptionsPreferenceController createSubscriptionsController(Lifecycle lifecycle) {
-        return new SubscriptionsPreferenceController(mContext, lifecycle, this, mPreferenceKey, 10);
+        final int prefStartOrder = 10;
+        return new SubscriptionsPreferenceController(mContext, lifecycle, this, mPreferenceKey,
+                prefStartOrder);
     }
 
     @Override
@@ -54,6 +67,7 @@
         super.displayPreference(screen);
         mPreferenceCategory = (PreferenceCategory) screen.findPreference(mPreferenceKey);
         mPreferenceCategory.setVisible(isAvailable());
+        mWifiController.displayPreference(screen);
         mSubscriptionsController.displayPreference(screen);
     }
 
diff --git a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
index c627a2e..30c2cd9 100644
--- a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
+++ b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
@@ -30,6 +30,7 @@
 import android.widget.BaseAdapter;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog;
 import androidx.preference.internal.PreferenceImageView;
 
@@ -46,7 +47,10 @@
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settings.wifi.NetworkRequestErrorDialogFragment.ERROR_DIALOG_TYPE;
 import com.android.settingslib.Utils;
+import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -62,10 +66,14 @@
     /** Message sent to us to stop scanning wifi and pop up timeout dialog. */
     private static final int MESSAGE_STOP_SCAN_WIFI_LIST = 0;
 
+    /** Spec defines there should be 5 wifi ap on the list at most. */
+    private static final int MAX_NUMBER_LIST_ITEM = 5;
+
     /** Delayed time to stop scanning wifi. */
     private static final int DELAY_TIME_STOP_SCAN_MS = 30 * 1000;
 
     private List<AccessPoint> mAccessPointList;
+    private FilterWifiTracker mFilterWifiTracker;
     private AccessPointAdapter mDialogAdapter;
     private NetworkRequestUserSelectionCallback mUserSelectionCallback;
 
@@ -159,6 +167,19 @@
         if (wifiManager != null) {
             wifiManager.unregisterNetworkRequestMatchCallback(this);
         }
+
+        if (mFilterWifiTracker != null) {
+            mFilterWifiTracker.onPause();
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mFilterWifiTracker != null) {
+            mFilterWifiTracker.onDestroy();
+            mFilterWifiTracker = null;
+        }
     }
 
     @Override
@@ -172,6 +193,11 @@
         }
         // Sets time-out to stop scanning.
         mHandler.sendEmptyMessageDelayed(MESSAGE_STOP_SCAN_WIFI_LIST, DELAY_TIME_STOP_SCAN_MS);
+
+        if (mFilterWifiTracker == null) {
+            mFilterWifiTracker = new FilterWifiTracker(getActivity(), getSettingsLifecycle());
+        }
+        mFilterWifiTracker.onResume();
     }
 
     private final Handler mHandler = new Handler() {
@@ -268,17 +294,33 @@
 
     @Override
     public void onMatch(List<ScanResult> scanResults) {
-        // TODO(b/119846365): Checks if we could escalate the converting effort.
-        // Converts ScanResult to WifiConfiguration.
-        List<WifiConfiguration> wifiConfigurations = null;
-        final WifiManager wifiManager = getContext().getApplicationContext()
-                .getSystemService(WifiManager.class);
-        if (wifiManager != null) {
-            wifiConfigurations = wifiManager.getAllMatchingWifiConfigs(scanResults);
+        mHandler.removeMessages(MESSAGE_STOP_SCAN_WIFI_LIST);
+        renewAccessPointList(scanResults);
+
+        notifyAdapterRefresh();
+    }
+
+    // Updates internal AccessPoint list from WifiTracker. scanResults are used to update key list
+    // of AccessPoint, and could be null if there is no necessary to update key list.
+    private void renewAccessPointList(List<ScanResult> scanResults) {
+        if (mFilterWifiTracker == null) {
+            return;
         }
 
-        setUpAccessPointList(wifiConfigurations);
+        // TODO(b/119846365): Checks if we could escalate the converting effort.
+        // Updates keys of scanResults into FilterWifiTracker for updating matched AccessPoints.
+        if (scanResults != null) {
+            mFilterWifiTracker.updateKeys(scanResults);
+        }
 
+        // Re-gets matched AccessPoints from WifiTracker.
+        final List<AccessPoint> list = getAccessPointList();
+        list.clear();
+        list.addAll(mFilterWifiTracker.getAccessPoints());
+    }
+
+    @VisibleForTesting
+    void notifyAdapterRefresh() {
         if (getDialogAdapter() != null) {
             getDialogAdapter().notifyDataSetChanged();
         }
@@ -286,48 +328,99 @@
 
     @Override
     public void onUserSelectionConnectSuccess(WifiConfiguration wificonfiguration) {
-        if (getDialogAdapter() != null) {
-            updateAccessPointListItem(wificonfiguration);
-            getDialogAdapter().notifyDataSetChanged();
-        }
+        // Dismisses current dialog, since connection is success.
+        dismiss();
     }
 
     @Override
     public void onUserSelectionConnectFailure(WifiConfiguration wificonfiguration) {
-        if (mDialogAdapter != null) {
-            updateAccessPointListItem(wificonfiguration);
-            getDialogAdapter().notifyDataSetChanged();
-        }
+        stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE.ABORT);
     }
 
-    private void updateAccessPointListItem(WifiConfiguration wificonfiguration) {
-        if (wificonfiguration == null) {
-            return;
+    private final class FilterWifiTracker {
+        private final List<String> mAccessPointKeys;
+        private final WifiTracker mWifiTracker;
+
+        public FilterWifiTracker(Context context, Lifecycle lifecycle) {
+            mWifiTracker = WifiTrackerFactory.create(context, mWifiListener,
+                    lifecycle, /* includeSaved */ true, /* includeScans */ true);
+            mAccessPointKeys = new ArrayList<>();
         }
 
-        final List<AccessPoint> accessPointList = getAccessPointList();
-        final int accessPointListSize = accessPointList.size();
-
-        for (int i = 0; i < accessPointListSize; i++) {
-            AccessPoint accessPoint = accessPointList.get(i);
-            // It is the same AccessPoint SSID, and should be replaced to update latest properties.
-            if (accessPoint.matches(wificonfiguration)) {
-                accessPointList.set(i, new AccessPoint(getContext(), wificonfiguration));
-                break;
+        /**
+         * Updates key list from input. {@code onMatch()} may be called in multi-times according
+         * wifi scanning result, so needs patchwork here.
+         */
+        public void updateKeys(List<ScanResult> scanResults) {
+            for (ScanResult scanResult : scanResults) {
+                final String key = AccessPoint.getKey(scanResult);
+                if (!mAccessPointKeys.contains(key)) {
+                    mAccessPointKeys.add(key);
+                }
             }
         }
-    }
 
-    private void setUpAccessPointList(List<WifiConfiguration> wifiConfigurations) {
-        // Grants for zero size input, since maybe current wifi is off or somethings are wrong.
-        if (wifiConfigurations == null) {
-            return;
+        /**
+         * Returns only AccessPoints whose key is in {@code mAccessPointKeys}.
+         *
+         * @return List of matched AccessPoints.
+         */
+        public List<AccessPoint> getAccessPoints() {
+            final List<AccessPoint> allAccessPoints = mWifiTracker.getAccessPoints();
+            final List<AccessPoint> result = new ArrayList<>();
+
+            // The order should be kept, because order means wifi score (sorting in WifiTracker).
+            int count = 0;
+            for (AccessPoint accessPoint : allAccessPoints) {
+                final String key = accessPoint.getKey();
+                if (mAccessPointKeys.contains(key)) {
+                    result.add(accessPoint);
+
+                    count++;
+                    // Limits how many count of items could show.
+                    if (count >= MAX_NUMBER_LIST_ITEM) {
+                        break;
+                    }
+                }
+            }
+
+            return result;
         }
 
-        final List<AccessPoint> accessPointList = getAccessPointList();
-        accessPointList.clear();
-        for (WifiConfiguration config : wifiConfigurations) {
-            accessPointList.add(new AccessPoint(getContext(), config));
+        private WifiTracker.WifiListener mWifiListener = new WifiTracker.WifiListener() {
+
+            @Override
+            public void onWifiStateChanged(int state) {
+                notifyAdapterRefresh();
+            }
+
+            @Override
+            public void onConnectedChanged() {
+                notifyAdapterRefresh();
+            }
+
+            @Override
+            public void onAccessPointsChanged() {
+                notifyAdapterRefresh();
+            }
+        };
+
+        public void onDestroy() {
+            if (mWifiTracker != null) {
+                mWifiTracker.onDestroy();
+            }
+        }
+
+        public void onResume() {
+            if (mWifiTracker != null) {
+                mWifiTracker.onStart();
+            }
+        }
+
+        public void onPause() {
+            if (mWifiTracker != null) {
+                mWifiTracker.onStop();
+            }
         }
     }
 }
diff --git a/src/com/android/settings/wifi/WifiConnectionPreferenceController.java b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
new file mode 100644
index 0000000..b73bce9
--- /dev/null
+++ b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
@@ -0,0 +1,177 @@
+/*
+ * 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.wifi;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.AccessPointPreference;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
+
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+/**
+ * This places a preference into a PreferenceGroup owned by some parent
+ * controller class when there is a wifi connection present.
+ */
+public class WifiConnectionPreferenceController extends AbstractPreferenceController implements
+        WifiTracker.WifiListener {
+
+    private static final String TAG = "WifiConnPrefCtrl";
+
+    private static final String KEY = "active_wifi_connection";
+
+    private UpdateListener mUpdateListener;
+    private Context mPrefContext;
+    private String mPreferenceGroupKey;
+    private PreferenceGroup mPreferenceGroup;
+    private WifiTracker mWifiTracker;
+    private AccessPointPreference mPreference;
+    private AccessPointPreference.UserBadgeCache mBadgeCache;
+    private int order;
+    private int mMetricsCategory;
+
+    /**
+     * Used to notify a parent controller that this controller has changed in availability, or has
+     * updated the content in the preference that it manages.
+     */
+    public interface UpdateListener {
+        void onChildrenUpdated();
+    }
+
+    /**
+     * @param context            the context for the UI where we're placing the preference
+     * @param lifecycle          for listening to lifecycle events for the UI
+     * @param updateListener     for notifying a parent controller of changes
+     * @param preferenceGroupKey the key to use to lookup the PreferenceGroup where this controller
+     *                           will add its preference
+     * @param order              the order that the preference added by this controller should use -
+     *                           useful when this preference needs to be ordered in a specific way
+     *                           relative to others in the PreferenceGroup
+     * @param metricsCategory    - the category to use as the source when handling the click on the
+     *                           pref to go to the wifi connection detail page
+     */
+    public WifiConnectionPreferenceController(Context context, Lifecycle lifecycle,
+            UpdateListener updateListener, String preferenceGroupKey, int order,
+            int metricsCategory) {
+        super(context);
+        mUpdateListener = updateListener;
+        mPreferenceGroupKey = preferenceGroupKey;
+        mWifiTracker = WifiTrackerFactory.create(context, this, lifecycle, true /* includeSaved */,
+                true /* includeScans */);
+        this.order = order;
+        mMetricsCategory = metricsCategory;
+        mBadgeCache = new AccessPointPreference.UserBadgeCache(context.getPackageManager());
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mWifiTracker.isConnected() && getCurrentAccessPoint() != null;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreferenceGroup = (PreferenceGroup) screen.findPreference(mPreferenceGroupKey);
+        mPrefContext = screen.getContext();
+        update();
+    }
+
+    private AccessPoint getCurrentAccessPoint() {
+        for (AccessPoint accessPoint : mWifiTracker.getAccessPoints()) {
+            if (accessPoint.isActive()) {
+                return accessPoint;
+            }
+        }
+        return null;
+    }
+
+    private void updatePreference(AccessPoint accessPoint) {
+        if (mPreference != null) {
+            mPreferenceGroup.removePreference(mPreference);
+            mPreference = null;
+        }
+        if (accessPoint == null) {
+            return;
+        }
+        if (mPrefContext != null) {
+            mPreference = new AccessPointPreference(accessPoint, mPrefContext, mBadgeCache,
+                    R.drawable.ic_wifi_signal_0, false /* forSavedNetworks */);
+            mPreference.setKey(KEY);
+            mPreference.refresh();
+            mPreference.setOrder(order);
+
+            mPreference.setOnPreferenceClickListener(pref -> {
+                Bundle args = new Bundle();
+                mPreference.getAccessPoint().saveWifiState(args);
+                new SubSettingLauncher(mPrefContext)
+                        .setTitleRes(R.string.pref_title_network_details)
+                        .setDestination(WifiNetworkDetailsFragment.class.getName())
+                        .setArguments(args)
+                        .setSourceMetricsCategory(mMetricsCategory)
+                        .launch();
+                return true;
+            });
+            mPreferenceGroup.addPreference(mPreference);
+        }
+    }
+
+    private void update() {
+        AccessPoint connectedAccessPoint = null;
+        if (mWifiTracker.isConnected()) {
+            connectedAccessPoint = getCurrentAccessPoint();
+        }
+        if (connectedAccessPoint == null) {
+            updatePreference(null);
+        } else {
+          if (mPreference == null || !mPreference.getAccessPoint().equals(connectedAccessPoint)) {
+              updatePreference(connectedAccessPoint);
+          } else if (mPreference != null) {
+              mPreference.refresh();
+          }
+        }
+        mUpdateListener.onChildrenUpdated();
+    }
+
+    @Override
+    public void onWifiStateChanged(int state) {
+        update();
+    }
+
+    @Override
+    public void onConnectedChanged() {
+        update();
+    }
+
+    @Override
+    public void onAccessPointsChanged() {
+        update();
+    }
+}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java b/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
index 177e79d..8d6aa68 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
@@ -18,6 +18,7 @@
 
 import android.os.Bundle;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 
 /**
@@ -31,6 +32,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
+    }
+
+    @Override
     public void onActivityCreated (Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
     }
diff --git a/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java b/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
index 7d031c1..66bc349 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
@@ -18,6 +18,7 @@
 
 import android.os.Bundle;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 
 /**
@@ -31,6 +32,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
+    }
+
+    @Override
     public void onActivityCreated (Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
     }
diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
index e4ae292..6c95f09 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
@@ -27,7 +27,6 @@
 import androidx.fragment.app.FragmentTransaction;
 
 import com.android.internal.logging.nano.MetricsProto;
-
 import com.android.settings.core.InstrumentedActivity;
 import com.android.settings.R;
 
@@ -67,8 +66,7 @@
 
     @Override
     public int getMetricsCategory() {
-        //TODO:Should we use a new metrics category for Wi-Fi DPP?
-        return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS;
+        return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
     }
 
     @Override
diff --git a/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java b/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java
index 39d993f..920e736 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java
@@ -27,7 +27,6 @@
 import androidx.fragment.app.FragmentTransaction;
 
 import com.android.internal.logging.nano.MetricsProto;
-
 import com.android.settings.core.InstrumentedActivity;
 import com.android.settings.R;
 
@@ -47,8 +46,7 @@
 
     @Override
     public int getMetricsCategory() {
-        //TODO:Should we use a new metrics category for Wi-Fi DPP?
-        return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS;
+        return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_ENROLLEE;
     }
 
     @Override
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
index e6427d9..6792dee 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
@@ -18,7 +18,6 @@
 
 import android.os.Bundle;
 import android.view.LayoutInflater;
-import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
@@ -27,10 +26,7 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
-import com.android.internal.logging.nano.MetricsProto;
-
 import com.android.settings.core.InstrumentedFragment;
-import com.android.settings.wifi.qrcode.QrDecorateView;
 import com.android.settings.R;
 
 /**
@@ -47,17 +43,12 @@
  * {@code WifiDppAddDeviceFragment}
  */
 public abstract class WifiDppQrCodeBaseFragment extends InstrumentedFragment {
+    private ImageView mHeaderIcon;
     private TextView mTitle;
     private TextView mDescription;
 
-    private SurfaceView mPreviewView;       //optional, for WifiDppQrCodeScannerFragment
-    private QrDecorateView mDecorateViiew;  //optional, for WifiDppQrCodeScannerFragment
     private TextView mErrorMessage;         //optional, for WifiDppQrCodeScannerFragment
-
-    private ImageView mBarcodeView;         //optional, for WifiDppQrCodeGeneratorFragment
-
     private ListView mSavedWifiNetworkList; //optional, for WifiDppChooseSavedWifiNetworkFragment
-
     private ProgressBar mProgressBar;       //optional, for WifiDppAddDeviceFragment
     private ImageView mWifiApPictureView;   //optional, for WifiDppAddDeviceFragment
     private TextView mChooseDifferentNetwork;//optional, for WifiDppAddDeviceFragment
@@ -70,12 +61,6 @@
     abstract protected int getLayout();
 
     @Override
-    public int getMetricsCategory() {
-        //TODO:Should we use a new metrics category for Wi-Fi DPP?
-        return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS;
-    }
-
-    @Override
     public final void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
     }
@@ -89,15 +74,11 @@
     }
 
     private void initView(View view) {
+        mHeaderIcon = view.findViewById(R.id.header_icon);
         mTitle = view.findViewById(R.id.title);
         mDescription = view.findViewById(R.id.description);
-
-        mPreviewView = view.findViewById(R.id.preview_view);
-        mDecorateViiew = view.findViewById(R.id.decorate_view);
         mErrorMessage = view.findViewById(R.id.error_message);
 
-        mBarcodeView = view.findViewById(R.id.barcode_view);
-
         mSavedWifiNetworkList = view.findViewById(R.id.saved_wifi_network_list);
 
         mProgressBar = view.findViewById(R.id.progress_bar);
@@ -108,6 +89,10 @@
         mButtonRight = view.findViewById(R.id.button_right);
     }
 
+    protected void setHeaderIconImageResource(int resId) {
+        mHeaderIcon.setImageResource(resId);
+    }
+
     protected void setTitle(String title) {
         mTitle.setText(title);
     }
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
index b064253..81def9b 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
@@ -23,6 +23,7 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 
 /**
@@ -35,6 +36,11 @@
         return R.layout.wifi_dpp_qrcode_generator_fragment;
     }
 
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
+    }
+
     // Container Activity must implement this interface
     public interface OnQrCodeGeneratorFragmentAddButtonClickedListener {
         public void onQrCodeGeneratorFragmentAddButtonClicked();
@@ -45,6 +51,7 @@
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
+        setHeaderIconImageResource(R.drawable.ic_qrcode_24dp);
         WifiNetworkConfig wifiNetworkConfig = ((WifiNetworkConfig.Retriever) getActivity())
                 .getWifiNetworkConfig();
         if (!WifiNetworkConfig.isValidConfig(wifiNetworkConfig)) {
@@ -79,7 +86,7 @@
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.next_label);
-        item.setIcon(R.drawable.ic_menu_add);
+        item.setIcon(R.drawable.ic_scan_24dp);
         item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
 
         super.onCreateOptionsMenu(menu, inflater);
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
index 342e693..8cd3c562 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
@@ -21,25 +21,28 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.Size;
 import android.view.Menu;
 import android.view.MenuInflater;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
 import android.view.View;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 import com.android.settings.wifi.qrcode.QrCamera;
 import com.android.settings.wifi.qrcode.QrDecorateView;
 
 public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment implements
-        SurfaceHolder.Callback,
+        SurfaceTextureListener,
         QrCamera.ScannerCallback {
     private QrCamera mCamera;
-    private SurfaceView mSurfaceView;
+    private TextureView mTextureView;
     private QrDecorateView mDecorateView;
 
     /** true if the fragment working for configurator, false enrollee*/
@@ -53,6 +56,15 @@
         return R.layout.wifi_dpp_qrcode_scanner_fragment;
     }
 
+    @Override
+    public int getMetricsCategory() {
+        if (mConfiguratorMode) {
+            return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
+        } else {
+            return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_ENROLLEE;
+        }
+    }
+
     /**
      * Configurator container activity of the fragment should create instance with this constructor.
      */
@@ -77,6 +89,8 @@
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
+        setHeaderIconImageResource(R.drawable.ic_scan_24dp);
+
         if (mConfiguratorMode) {
             setTitle(getString(R.string.wifi_dpp_add_device_to_network));
 
@@ -109,23 +123,13 @@
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
-        mSurfaceView = (SurfaceView) view.findViewById(R.id.preview_view);
-        final SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
-        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
-        surfaceHolder.addCallback(this);
+        mTextureView = (TextureView) view.findViewById(R.id.preview_view);
+        mTextureView.setSurfaceTextureListener(this);
 
         mDecorateView = (QrDecorateView) view.findViewById(R.id.decorate_view);
     }
 
     @Override
-    public void onDestroyView() {
-        SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
-        surfaceHolder.removeCallback(this);
-
-        super.onDestroyView();
-    }
-
-    @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         menu.removeItem(Menu.FIRST);
 
@@ -133,23 +137,29 @@
     }
 
     @Override
-    public void surfaceCreated(final SurfaceHolder holder) {
-        initCamera(holder);
+    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+        initCamera(surface);
     }
 
     @Override
-    public void surfaceDestroyed(SurfaceHolder holder) {
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+        // Do nothing
+    }
+
+    @Override
+    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
         destroyCamera();
+        return true;
     }
 
     @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
         // Do nothing
     }
 
     @Override
     public Size getViewSize() {
-        return new Size(mSurfaceView.getWidth(), mSurfaceView.getHeight());
+        return new Size(mTextureView.getWidth(), mTextureView.getHeight());
     }
 
     @Override
@@ -158,6 +168,11 @@
     }
 
     @Override
+    public void setTransform(Matrix transform) {
+        mTextureView.setTransform(transform);
+    }
+
+    @Override
     public void handleSuccessfulResult(String qrCode) {
         destroyCamera();
         mDecorateView.setFocused(true);
@@ -169,11 +184,11 @@
         destroyCamera();
     }
 
-    private void initCamera(SurfaceHolder holder) {
+    private void initCamera(SurfaceTexture surface) {
         // Check if the camera has already created.
         if (mCamera == null) {
             mCamera = new QrCamera(getContext(), this);
-            mCamera.start(holder);
+            mCamera.start(surface);
         }
     }
 
diff --git a/src/com/android/settings/wifi/qrcode/QrCamera.java b/src/com/android/settings/wifi/qrcode/QrCamera.java
index dc650b9..c60c30e 100644
--- a/src/com/android/settings/wifi/qrcode/QrCamera.java
+++ b/src/com/android/settings/wifi/qrcode/QrCamera.java
@@ -17,7 +17,10 @@
 package com.android.settings.wifi.qrcode;
 
 import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
 import android.hardware.Camera.CameraInfo;
 import android.hardware.Camera.Parameters;
@@ -29,7 +32,6 @@
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
-import android.view.SurfaceHolder;
 import android.view.WindowManager;
 import com.google.zxing.BarcodeFormat;
 import com.google.zxing.BinaryBitmap;
@@ -50,7 +52,7 @@
 
 /**
  * Manage the camera for the QR scanner and help the decoder to get the image inside the scanning
- * frame. Caller prepares a {@link SurfaceHolder} then call {@link #start(SurfaceHolder)} to
+ * frame. Caller prepares a {@link SurfaceTexture} then call {@link #start(SurfaceTexture)} to
  * start QR Code scanning. The scanning result will return by ScannerCallback interface. Caller
  * can also call {@link #stop()} to halt QR Code scanning before the result returned.
  */
@@ -90,12 +92,11 @@
      * The function start camera preview and capture pictures to decode QR code continuously in a
      * background task.
      *
-     * @param surfaceHolder the Surface to be used for live preview, must already contain a surface
-     *                      when this method is called.
+     * @param surface The surface to be used for live preview.
      */
-    public void start(SurfaceHolder surfaceHolder) {
+    public void start(SurfaceTexture surface) {
         if (mDecodeTask == null) {
-            mDecodeTask = new DecodingTask(surfaceHolder);
+            mDecodeTask = new DecodingTask(surface);
             // Execute in the separate thread pool to prevent block other AsyncTask.
             mDecodeTask.executeOnExecutor(Executors.newSingleThreadExecutor());
         }
@@ -144,6 +145,13 @@
          * @return The rectangle would like to crop from the camera preview shot.
          */
         Rect getFramePosition(Size previewSize, int cameraOrientation);
+
+        /**
+         * Sets the transform to associate with preview area.
+         *
+         * @param transform The transform to apply to the content of preview
+         */
+        void setTransform(Matrix transform);
     }
 
     private void setCameraParameter() {
@@ -200,15 +208,15 @@
 
     private class DecodingTask extends AsyncTask<Void, Void, String> {
         private QrYuvLuminanceSource mImage;
-        private SurfaceHolder mSurfaceHolder;
+        private SurfaceTexture mSurface;
 
-        private DecodingTask(SurfaceHolder surfaceHolder) {
-            mSurfaceHolder = surfaceHolder;
+        private DecodingTask(SurfaceTexture surface) {
+            mSurface = surface;
         }
 
         @Override
         protected String doInBackground(Void... tmp) {
-            if (!initCamera(mSurfaceHolder)) {
+            if (!initCamera(mSurface)) {
                 return null;
             }
 
@@ -253,7 +261,7 @@
             }
         }
 
-        private boolean initCamera(SurfaceHolder surfaceHolder) {
+        private boolean initCamera(SurfaceTexture surface) {
             final int numberOfCameras = Camera.getNumberOfCameras();
             Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
             try {
@@ -261,7 +269,7 @@
                     Camera.getCameraInfo(i, cameraInfo);
                     if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
                         mCamera = Camera.open(i);
-                        mCamera.setPreviewDisplay(surfaceHolder);
+                        mCamera.setPreviewTexture(surface);
                         mCameraOrientation = cameraInfo.orientation;
                         break;
                     }
@@ -272,6 +280,7 @@
                     return false;
                 }
                 setCameraParameter();
+                setTransformationMatrix(mScannerCallback.getViewSize());
                 if (!startPreview()) {
                     Log.e(TAG, "Error to init Camera");
                     mCamera = null;
@@ -288,6 +297,36 @@
         }
     }
 
+    /** Set transfom matrix to crop and center the preview picture */
+    private void setTransformationMatrix(Size viewSize) {
+        // Check aspect ratio, can only handle square view.
+        final int viewRatio = (int)getRatio(viewSize.getWidth(), viewSize.getHeight());
+        if (viewRatio != 1) {
+            throw new IllegalArgumentException("Preview area should be square");
+        }
+
+        final boolean isPortrait = mContext.get().getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_PORTRAIT ? true : false;
+
+        final int previewWidth = isPortrait ? mPreviewSize.getWidth() : mPreviewSize.getHeight();
+        final int previewHeight = isPortrait ? mPreviewSize.getHeight() : mPreviewSize.getWidth();
+        final float ratioPreview = (float) getRatio(previewWidth, previewHeight);
+
+        // Calculate transformation matrix.
+        float scaleX = 1.0f;
+        float scaleY = 1.0f;
+        if (previewWidth > previewHeight) {
+            scaleY = scaleX / ratioPreview;
+        } else {
+            scaleX = scaleY / ratioPreview;
+        }
+
+        // Set the transform matrix.
+        final Matrix matrix = new Matrix();
+        matrix.setScale(scaleX, scaleY);
+        mScannerCallback.setTransform(matrix);
+    }
+
     private QrYuvLuminanceSource getFrameImage(byte[] imageData) {
         final Rect frame = mScannerCallback.getFramePosition(mPreviewSize, mCameraOrientation);
         final Camera.Size size = mParameters.getPictureSize();
diff --git a/src/com/android/settings/wifi/qrcode/QrPreviewLayout.java b/src/com/android/settings/wifi/qrcode/QrPreviewLayout.java
new file mode 100644
index 0000000..56566ae
--- /dev/null
+++ b/src/com/android/settings/wifi/qrcode/QrPreviewLayout.java
@@ -0,0 +1,50 @@
+/*
+ * 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.wifi.qrcode;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.FrameLayout;
+
+/**
+ * A customize square {@link FrameLayout}.
+ * This is used for camera preview. Choose the smaller size of both dimensions as length and width.
+ */
+public class QrPreviewLayout extends FrameLayout {
+    public QrPreviewLayout(Context context) {
+        super(context);
+    }
+
+    public QrPreviewLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public QrPreviewLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // Choose the smaller size of the two dimensions.
+        if (MeasureSpec.getSize(widthMeasureSpec) > MeasureSpec.getSize(heightMeasureSpec)) {
+            super.onMeasure(heightMeasureSpec, heightMeasureSpec);
+        } else {
+            super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+        }
+    }
+}
diff --git a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
index befd880..ce03d95 100644
--- a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
+++ b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
@@ -7,7 +7,6 @@
 com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController
 com.android.settings.datausage.DataUsageSummaryPreferenceController
 com.android.settings.fuelgauge.RestrictAppPreferenceController
-com.android.settings.fuelgauge.batterysaver.AutoBatterySeekBarPreferenceController
 com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceController
 com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController
 com.android.settings.security.VisiblePatternProfilePreferenceController
diff --git a/tests/robotests/src/com/android/settings/MasterClearTest.java b/tests/robotests/src/com/android/settings/MasterClearTest.java
index 21cb09c..388440e 100644
--- a/tests/robotests/src/com/android/settings/MasterClearTest.java
+++ b/tests/robotests/src/com/android/settings/MasterClearTest.java
@@ -51,6 +51,7 @@
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.settings.testutils.shadow.ShadowUtils;
+import com.google.android.setupcompat.item.FooterButton;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -386,7 +387,7 @@
     public void testOnGlobalLayout_shouldNotRemoveListener() {
         final ViewTreeObserver viewTreeObserver = mock(ViewTreeObserver.class);
         mMasterClear.mScrollView = mScrollView;
-        mMasterClear.mInitiateButton = mock(Button.class);
+        mMasterClear.mInitiateButton = mock(FooterButton.class);
         doReturn(true).when(mMasterClear).hasReachedBottom(any());
         when(mScrollView.getViewTreeObserver()).thenReturn(viewTreeObserver);
 
diff --git a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java
index ce438b0..28e394f 100644
--- a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java
+++ b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java
@@ -71,7 +71,9 @@
                 .that(badClasses.toString())
                 .isEmpty();
 
-        assertWithMessage("Something in the grandfather list is no longer relevant. Please remove")
+        assertWithMessage("Something in the grandfather list is no longer relevant. Please remove"
+            + "it from packages/apps/Settings/tests/robotests/assets/"
+            + "grandfather_invalid_base_preference_controller_constructor")
                 .that(grandfather)
                 .isEmpty();
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceControllerTest.java
deleted file mode 100644
index df1d909..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceControllerTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- */
-
-package com.android.settings.fuelgauge.batterysaver;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.preference.SwitchPreference;
-
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class AutoBatterySaverPreferenceControllerTest {
-
-    private AutoBatterySaverPreferenceController mController;
-    private Context mContext;
-    private SwitchPreference mPreference;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mContext = RuntimeEnvironment.application;
-        mPreference = new SwitchPreference(mContext);
-        mController = new AutoBatterySaverPreferenceController(mContext);
-    }
-
-    @Test
-    public void testUpdateState_lowPowerLevelZero_preferenceNotChecked() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.isChecked()).isFalse();
-    }
-
-    @Test
-    public void testUpdateState_lowPowerLevelZero_preferenceChecked() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 15);
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.isChecked()).isTrue();
-    }
-
-    @Test
-    public void testOnPreferenceChange_turnOn_setValueNotZero() {
-        mController.onPreferenceChange(mPreference, true);
-
-        assertThat(Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)).isNotEqualTo(0);
-    }
-
-    @Test
-    public void testOnPreferenceChange_turnOff_setValueZero() {
-        mController.onPreferenceChange(mPreference, false);
-
-        assertThat(Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)).isEqualTo(0);
-    }
-
-    @Test
-    public void testIsChecked_useDefaultValue_returnFalse() {
-        assertThat(mController.isChecked()).isFalse();
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java
deleted file mode 100644
index c13b913..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.batterysaver;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.lifecycle.LifecycleOwner;
-
-import com.android.settings.widget.SeekBarPreference;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class AutoBatterySeekBarPreferenceControllerTest {
-
-    private static final int TRIGGER_LEVEL = 20;
-    private static final int INTERVAL = 5;
-
-    private AutoBatterySeekBarPreferenceController mController;
-    private Context mContext;
-    private SeekBarPreference mPreference;
-    private Lifecycle mLifecycle;
-    private LifecycleOwner mLifecycleOwner;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mLifecycleOwner = () -> mLifecycle;
-        mLifecycle = new Lifecycle(mLifecycleOwner);
-
-        mContext = RuntimeEnvironment.application;
-        mPreference = new SeekBarPreference(mContext);
-        mPreference.setMax(100);
-        mController = new AutoBatterySeekBarPreferenceController(mContext, mLifecycle);
-    }
-
-    @Test
-    public void testPreference_lowPowerLevelZero_preferenceInvisible() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.isVisible()).isFalse();
-    }
-
-    @Test
-    public void testPreference_defaultValue_preferenceNotVisible() {
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.isVisible()).isFalse();
-    }
-
-    @Test
-    public void testPreference_lowPowerLevelNotZero_updatePreference() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, TRIGGER_LEVEL);
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.isVisible()).isTrue();
-        assertThat(mPreference.getTitle()).isEqualTo("20%");
-        assertThat(mPreference.getProgress()).isEqualTo(TRIGGER_LEVEL / INTERVAL);
-    }
-
-    @Test
-    public void testOnPreferenceChange_updateValue() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
-
-        mController.onPreferenceChange(mPreference, TRIGGER_LEVEL / INTERVAL);
-
-        assertThat(Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)).isEqualTo(TRIGGER_LEVEL);
-    }
-
-    @Test
-    public void testOnPreferenceChange_changeMax() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, 50);
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.getMax()).isEqualTo(50 / INTERVAL);
-    }
-
-    @Test
-    public void testOnPreferenceChange_noChangeMax() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, 0);
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.getMax()).isEqualTo(100);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java
new file mode 100644
index 0000000..6d412ee
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java
@@ -0,0 +1,57 @@
+package com.android.settings.fuelgauge.batterysaver;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatterySaverScheduleRadioButtonsControllerTest {
+    private Context mContext;
+    private ContentResolver mResolver;
+    private BatterySaverScheduleRadioButtonsController mController;
+    private BatterySaverScheduleSeekBarController mSeekBarController;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mSeekBarController = new BatterySaverScheduleSeekBarController(mContext);
+        mController = new BatterySaverScheduleRadioButtonsController(
+                mContext, mSeekBarController);
+        mResolver = mContext.getContentResolver();
+    }
+
+    @Test
+    public void getDefaultKey_routine_returnsCorrectValue() {
+        Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                PowerManager.POWER_SAVER_MODE_DYNAMIC);
+        assertThat(mController.getDefaultKey())
+                .isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_ROUTINE);
+    }
+
+    @Test
+    public void getDefaultKey_automatic_returnsCorrectValue() {
+        Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+        Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
+        assertThat(mController.getDefaultKey())
+                .isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_PERCENTAGE);
+    }
+
+    @Test
+    public void getDefaultKey_none_returnsCorrectValue() {
+        Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+        Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+        assertThat(mController.getDefaultKey())
+                .isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_NO_SCHEDULE);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java
new file mode 100644
index 0000000..8aac9d8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java
@@ -0,0 +1,65 @@
+package com.android.settings.fuelgauge.batterysaver;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatterySaverScheduleSeekBarControllerTest {
+
+    private Context mContext;
+    private ContentResolver mResolver;
+    private BatterySaverScheduleSeekBarController mController;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mController = new BatterySaverScheduleSeekBarController(mContext);
+        mResolver = mContext.getContentResolver();
+    }
+
+    @Test
+    public void onPreferenceChange_updatesSettingsGlobal() {
+        Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
+        mController.onPreferenceChange(mController.mSeekBarPreference, 10);
+        assertThat(Settings.Global.getInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
+                .isEqualTo(50);
+
+        assertThat(mController.mSeekBarPreference.getTitle()).isEqualTo("50%");
+    }
+
+    @Test
+    public void updateSeekBar_routineMode_hasCorrectProperties() {
+        Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                PowerManager.POWER_SAVER_MODE_DYNAMIC);
+        mController.updateSeekBar();
+        assertThat(mController.mSeekBarPreference.isVisible()).isFalse();
+    }
+
+    @Test
+    public void updateSeekBar_percentageMode_hasCorrectProperties() {
+        Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+        Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
+        mController.updateSeekBar();
+        assertThat(mController.mSeekBarPreference.isVisible()).isTrue();
+    }
+
+    @Test
+    public void updateSeekBar_noneMode_hasCorrectProperties() {
+        Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+        Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+        mController.updateSeekBar();
+        assertThat(mController.mSeekBarPreference.isVisible()).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionControllerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionControllerTest.java
deleted file mode 100644
index e4ca6c0..0000000
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionControllerTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.homepage.contextualcards.conditional;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.IntentFilter;
-import android.os.PowerManager;
-
-import com.android.settings.fuelgauge.BatterySaverReceiver;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.shadows.ShadowPowerManager;
-
-@RunWith(RobolectricTestRunner.class)
-public class BatterySaverConditionControllerTest {
-    @Mock
-    private ConditionManager mConditionManager;
-
-    private ShadowPowerManager mPowerManager;
-    private Context mContext;
-    private BatterySaverConditionController mController;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(RuntimeEnvironment.application);
-        mPowerManager = Shadows.shadowOf(mContext.getSystemService(PowerManager.class));
-        mController = new BatterySaverConditionController(mContext, mConditionManager);
-    }
-
-    @Test
-    public void startMonitor_shouldRegisterReceiver() {
-        mController.startMonitoringStateChange();
-
-        verify(mContext).registerReceiver(any(BatterySaverReceiver.class), any(IntentFilter.class));
-    }
-
-    @Test
-    public void stopMonitor_shouldUnregisterReceiver() {
-        mController.startMonitoringStateChange();
-        mController.stopMonitoringStateChange();
-
-        verify(mContext).unregisterReceiver(any(BatterySaverReceiver.class));
-    }
-
-    @Test
-    public void isDisplayable_PowerSaverOn_true() {
-        mPowerManager.setIsPowerSaveMode(true);
-
-        assertThat(mController.isDisplayable()).isTrue();
-    }
-
-    @Test
-    public void isDisplayable_PowerSaverOff_false() {
-        mPowerManager.setIsPowerSaveMode(false);
-
-        assertThat(mController.isDisplayable()).isFalse();
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java b/tests/robotests/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
index fbd7867..b4ebcc4 100644
--- a/tests/robotests/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.telephony.SubscriptionManager;
 
+import com.android.settings.wifi.WifiConnectionPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -55,6 +56,8 @@
     @Mock
     private PreferenceCategory mPreferenceCategory;
     @Mock
+    private WifiConnectionPreferenceController mWifiController;
+    @Mock
     private SubscriptionsPreferenceController mSubscriptionsController;
     @Mock
     private SubscriptionManager mSubscriptionManager;
@@ -74,6 +77,7 @@
         when(mPreferenceScreen.findPreference(eq(KEY_HEADER))).thenReturn(mPreferenceCategory);
 
         mHeaderController = spy(new MultiNetworkHeaderController(mContext, KEY_HEADER));
+        doReturn(mWifiController).when(mHeaderController).createWifiController(mLifecycle);
         doReturn(mSubscriptionsController).when(mHeaderController).createSubscriptionsController(
                 mLifecycle);
     }
@@ -85,8 +89,9 @@
 
     // When calling displayPreference, the header itself should only be visible if the
     // subscriptions controller says it is available. This is a helper for test cases of this logic.
-    private void displayPreferenceTest(boolean subscriptionsAvailable,
+    private void displayPreferenceTest(boolean wifiAvailable, boolean subscriptionsAvailable,
             boolean setVisibleExpectedValue) {
+        when(mWifiController.isAvailable()).thenReturn(wifiAvailable);
         when(mSubscriptionsController.isAvailable()).thenReturn(subscriptionsAvailable);
 
         mHeaderController.init(mLifecycle);
@@ -96,13 +101,23 @@
     }
 
     @Test
-    public void displayPreference_subscriptionsNotAvailable_categoryIsNotVisible() {
-        displayPreferenceTest(false, false);
+    public void displayPreference_bothNotAvailable_categoryIsNotVisible() {
+        displayPreferenceTest(false, false, false);
     }
 
     @Test
-    public void displayPreference_subscriptionsAvailable_categoryIsVisible() {
-        displayPreferenceTest(true, true);
+    public void displayPreference_wifiAvailableButNotSubscriptions_categoryIsNotVisible() {
+        displayPreferenceTest(true, false, false);
+    }
+
+    @Test
+    public void displayPreference_subscriptionsAvailableButNotWifi_categoryIsVisible() {
+        displayPreferenceTest(false, true, true);
+    }
+
+    @Test
+    public void displayPreference_bothAvailable_categoryIsVisible() {
+        displayPreferenceTest(true, true, true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java
new file mode 100644
index 0000000..7037318
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+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.content.Context;
+
+import com.android.settings.wifi.WifiConnectionPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.AccessPointPreference;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
+
+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.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiConnectionPreferenceControllerTest {
+    private static final String KEY = "wifi_connection";
+
+    @Mock
+    WifiTracker mWifiTracker;
+    @Mock
+    PreferenceScreen mScreen;
+    @Mock
+    PreferenceCategory mPreferenceCategory;
+
+    private Context mContext;
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+    private WifiConnectionPreferenceController mController;
+    private int mOnChildUpdatedCount;
+    private WifiConnectionPreferenceController.UpdateListener mUpdateListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        WifiTrackerFactory.setTestingWifiTracker(mWifiTracker);
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+        when(mScreen.findPreference(eq(KEY))).thenReturn(mPreferenceCategory);
+        when(mScreen.getContext()).thenReturn(mContext);
+        mUpdateListener = () -> mOnChildUpdatedCount++;
+
+        mController = new WifiConnectionPreferenceController(mContext, mLifecycle, mUpdateListener,
+                KEY, 0, 0);
+    }
+
+    @Test
+    public void isAvailable_noWiFiConnection_availableIsFalse() {
+        when(mWifiTracker.isConnected()).thenReturn(false);
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void displayPreference_noWiFiConnection_noPreferenceAdded() {
+        when(mWifiTracker.isConnected()).thenReturn(false);
+        when(mWifiTracker.getAccessPoints()).thenReturn(new ArrayList<>());
+        mController.displayPreference(mScreen);
+        verify(mPreferenceCategory, never()).addPreference(any());
+    }
+
+    @Test
+    public void displayPreference_hasWiFiConnection_preferenceAdded() {
+        when(mWifiTracker.isConnected()).thenReturn(true);
+        final AccessPoint accessPoint = mock(AccessPoint.class);
+        when(accessPoint.isActive()).thenReturn(true);
+        when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint));
+        mController.displayPreference(mScreen);
+        verify(mPreferenceCategory).addPreference(any(AccessPointPreference.class));
+    }
+
+    @Test
+    public void onConnectedChanged_wifiBecameDisconnected_preferenceRemoved() {
+        when(mWifiTracker.isConnected()).thenReturn(true);
+        final AccessPoint accessPoint = mock(AccessPoint.class);
+
+        when(accessPoint.isActive()).thenReturn(true);
+        when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint));
+        mController.displayPreference(mScreen);
+        final ArgumentCaptor<AccessPointPreference> captor = ArgumentCaptor.forClass(
+                AccessPointPreference.class);
+        verify(mPreferenceCategory).addPreference(captor.capture());
+        final AccessPointPreference pref = captor.getValue();
+
+        when(mWifiTracker.isConnected()).thenReturn(false);
+        when(mWifiTracker.getAccessPoints()).thenReturn(new ArrayList<>());
+        final int onUpdatedCountBefore = mOnChildUpdatedCount;
+        mController.onConnectedChanged();
+        verify(mPreferenceCategory).removePreference(pref);
+        assertThat(mOnChildUpdatedCount).isEqualTo(onUpdatedCountBefore + 1);
+    }
+
+
+    @Test
+    public void onAccessPointsChanged_wifiBecameConnectedToDifferentAP_preferenceReplaced() {
+        when(mWifiTracker.isConnected()).thenReturn(true);
+        final AccessPoint accessPoint1 = mock(AccessPoint.class);
+
+        when(accessPoint1.isActive()).thenReturn(true);
+        when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint1));
+        mController.displayPreference(mScreen);
+        final ArgumentCaptor<AccessPointPreference> captor = ArgumentCaptor.forClass(
+                AccessPointPreference.class);
+
+
+        final AccessPoint accessPoint2 = mock(AccessPoint.class);
+        when(accessPoint1.isActive()).thenReturn(false);
+        when(accessPoint2.isActive()).thenReturn(true);
+        when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint1, accessPoint2));
+        final int onUpdatedCountBefore = mOnChildUpdatedCount;
+        mController.onAccessPointsChanged();
+
+        verify(mPreferenceCategory, times(2)).addPreference(captor.capture());
+        final AccessPointPreference pref1 = captor.getAllValues().get(0);
+        final AccessPointPreference pref2 = captor.getAllValues().get(1);
+        assertThat(pref1.getAccessPoint()).isEqualTo(accessPoint1);
+        assertThat(pref2.getAccessPoint()).isEqualTo(accessPoint2);
+        verify(mPreferenceCategory).removePreference(eq(pref1));
+        assertThat(mOnChildUpdatedCount).isEqualTo(onUpdatedCountBefore + 1);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogActivityTest.java
index 48f8ec0..107da79 100644
--- a/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogActivityTest.java
@@ -18,9 +18,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
+
 import androidx.appcompat.app.AlertDialog;
 
 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -34,6 +38,10 @@
 
     @Test
     public void LaunchActivity_shouldShowNetworkRequestDialog() {
+        // Mocks fake WifiTracker, in case of exception in NetworkRequestDialogFragment.onResume().
+        WifiTracker wifiTracker = mock(WifiTracker.class);
+        WifiTrackerFactory.setTestingWifiTracker(wifiTracker);
+
         Robolectric.setupActivity(NetworkRequestDialogActivity.class);
 
         AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
diff --git a/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java
index 343d170..e64fae7 100644
--- a/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java
@@ -51,6 +51,10 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
+
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
+
 import org.robolectric.shadows.ShadowLooper;
 
 @RunWith(RobolectricTestRunner.class)
@@ -58,16 +62,21 @@
 public class NetworkRequestDialogFragmentTest {
 
     private static final String KEY_SSID = "key_ssid";
+    private static final String KEY_SECURITY = "key_security";
 
     private FragmentActivity mActivity;
     private NetworkRequestDialogFragment networkRequestDialogFragment;
     private Context mContext;
+    private WifiTracker mWifiTracker;
 
     @Before
     public void setUp() {
         mActivity = Robolectric.setupActivity(FragmentActivity.class);
         networkRequestDialogFragment = spy(NetworkRequestDialogFragment.newInstance());
         mContext = spy(RuntimeEnvironment.application);
+
+        mWifiTracker = mock(WifiTracker.class);
+        WifiTrackerFactory.setTestingWifiTracker(mWifiTracker);
     }
 
     @Test
@@ -140,71 +149,47 @@
     }
 
     @Test
-    public void updateAccessPointList_onUserSelectionConnectSuccess_updateCorrectly() {
-        List<AccessPoint> accessPointList = spy(new ArrayList<>());
-        Bundle bundle = new Bundle();
-        bundle.putString(KEY_SSID, "Test AP 1");
-        accessPointList.add(new AccessPoint(mContext, bundle));
-        bundle.putString(KEY_SSID, "Test AP 2");
-        accessPointList.add(new AccessPoint(mContext, bundle));
-        bundle.putString(KEY_SSID, "Test AP 3");
-        accessPointList.add(new AccessPoint(mContext, bundle));
-        bundle.putString(KEY_SSID, "Test AP 4");
-        accessPointList.add(new AccessPoint(mContext, bundle));
-
+    public void updateAccessPointList_onUserSelectionConnectSuccess_shouldCloseTheDialog() {
+        List<AccessPoint> accessPointList = createAccessPointList();
         when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList);
         networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null);
+        AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(alertDialog.isShowing()).isTrue();
 
         // Test if config would update list.
         WifiConfiguration config = new WifiConfiguration();
         config.SSID = "Test AP 3";
         networkRequestDialogFragment.onUserSelectionConnectSuccess(config);
 
-        AccessPoint verifyAccessPoint = new AccessPoint(mContext, config);
-        verify(accessPointList, times(1)).set(2, verifyAccessPoint);
+        assertThat(alertDialog.isShowing()).isFalse();
     }
 
     @Test
-    public void updateAccessPointList_onUserSelectionConnectFailure_updateCorrectly() {
-        List<AccessPoint> accessPointList = spy(new ArrayList<>());
-        Bundle bundle = new Bundle();
-        bundle.putString(KEY_SSID, "Test AP 1");
-        accessPointList.add(new AccessPoint(mContext, bundle));
-        bundle.putString(KEY_SSID, "Test AP 2");
-        accessPointList.add(new AccessPoint(mContext, bundle));
-        bundle.putString(KEY_SSID, "Test AP 3");
-        accessPointList.add(new AccessPoint(mContext, bundle));
-        bundle.putString(KEY_SSID, "Test AP 4");
-        accessPointList.add(new AccessPoint(mContext, bundle));
+    public void updateAccessPointList_onUserSelectionConnectFailure_shouldCallTimeoutDialog() {
+        FakeNetworkRequestDialogFragment fakeFragment = new FakeNetworkRequestDialogFragment();
+        FakeNetworkRequestDialogFragment spyFakeFragment = spy(fakeFragment);
+        List<AccessPoint> accessPointList = createAccessPointList();
+        when(spyFakeFragment.getAccessPointList()).thenReturn(accessPointList);
+        spyFakeFragment.show(mActivity.getSupportFragmentManager(), null);
 
-        when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList);
-        networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null);
+        AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(alertDialog.isShowing()).isTrue();
 
         // Test if config would update list.
         WifiConfiguration config = new WifiConfiguration();
         config.SSID = "Test AP 3";
-        networkRequestDialogFragment.onUserSelectionConnectFailure(config);
+        fakeFragment.onUserSelectionConnectFailure(config);
 
-        AccessPoint verifyAccessPoint = new AccessPoint(mContext, config);
-        verify(accessPointList, times(1)).set(2, verifyAccessPoint);
+        assertThat(fakeFragment.bCalledStopAndPop).isTrue();
     }
 
     @Test
-    public void onUserSelectionCallbackRegistration_shouldCallSelect() {
-        List<AccessPoint> accessPointList = spy(new ArrayList<>());
-        Bundle bundle = new Bundle();
-        bundle.putString(KEY_SSID, "Test AP 1");
-        accessPointList.add(new AccessPoint(mContext, bundle));
-        bundle.putString(KEY_SSID, "Test AP 2");
-        accessPointList.add(new AccessPoint(mContext, bundle));
-
-        bundle.putString(KEY_SSID, "Test AP 3");
-        AccessPoint clickedAccessPoint = new AccessPoint(mContext, bundle);
+    public void onUserSelectionCallbackRegistration_onClick_shouldCallSelect() {
+        // Assert.
+        final int indexClickItem = 3;
+        List<AccessPoint> accessPointList = createAccessPointList();
+        AccessPoint clickedAccessPoint = accessPointList.get(indexClickItem);
         clickedAccessPoint.generateOpenNetworkConfig();
-        accessPointList.add(clickedAccessPoint);
-
-        bundle.putString(KEY_SSID, "Test AP 4");
-        accessPointList.add(new AccessPoint(mContext, bundle));
         when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList);
 
         NetworkRequestUserSelectionCallback selectionCallback = mock(
@@ -212,40 +197,66 @@
         AlertDialog dialog = mock(AlertDialog.class);
         networkRequestDialogFragment.onUserSelectionCallbackRegistration(selectionCallback);
 
-        networkRequestDialogFragment.onClick(dialog, 2);
+        // Act.
+        networkRequestDialogFragment.onClick(dialog, indexClickItem);
 
+        // Check.
         verify(selectionCallback, times(1)).select(clickedAccessPoint.getConfig());
     }
 
     @Test
     public void onMatch_shouldUpdatedList() {
-        // Prepares WifiManager.
+        // Assert.
         when(networkRequestDialogFragment.getContext()).thenReturn(mContext);
         Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext());
         when(mContext.getApplicationContext()).thenReturn(applicationContext);
         WifiManager wifiManager = mock(WifiManager.class);
         when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager);
+        networkRequestDialogFragment.onResume();
 
-        List<WifiConfiguration> wifiConfigurationList = new ArrayList<>();
-        WifiConfiguration config = new WifiConfiguration();
+        List<AccessPoint> accessPointList = createAccessPointList();
+        when(mWifiTracker.getAccessPoints()).thenReturn(accessPointList);
+
         final String SSID_AP1 = "Test AP 1";
-        config.SSID = SSID_AP1;
-        wifiConfigurationList.add(config);
-        config = new WifiConfiguration();
         final String SSID_AP2 = "Test AP 2";
-        config.SSID = SSID_AP2;
-        wifiConfigurationList.add(config);
-
-        // Prepares callback converted data.
         List<ScanResult> scanResults = new ArrayList<>();
-        when(wifiManager.getAllMatchingWifiConfigs(scanResults)).thenReturn(wifiConfigurationList);
+        ScanResult scanResult = new ScanResult();
+        scanResult.SSID = SSID_AP1;
+        scanResult.capabilities = "WEP";
+        scanResults.add(scanResult);
+        scanResult = new ScanResult();
+        scanResult.SSID = SSID_AP2;
+        scanResult.capabilities = "WEP";
+        scanResults.add(scanResult);
 
+        // Act.
         networkRequestDialogFragment.onMatch(scanResults);
 
-        List<AccessPoint> accessPointList = networkRequestDialogFragment.getAccessPointList();
-        assertThat(accessPointList).isNotEmpty();
-        assertThat(accessPointList.size()).isEqualTo(2);
-        assertThat(accessPointList.get(0).getSsid()).isEqualTo(SSID_AP1);
-        assertThat(accessPointList.get(1).getSsid()).isEqualTo(SSID_AP2);
+        // Check.
+        List<AccessPoint> returnList = networkRequestDialogFragment.getAccessPointList();
+        assertThat(returnList).isNotEmpty();
+        assertThat(returnList.size()).isEqualTo(2);
+        assertThat(returnList.get(0).getSsid()).isEqualTo(SSID_AP1);
+        assertThat(returnList.get(1).getSsid()).isEqualTo(SSID_AP2);
+    }
+
+    private List<AccessPoint> createAccessPointList() {
+        List<AccessPoint> accessPointList = spy(new ArrayList<>());
+        Bundle bundle = new Bundle();
+        bundle.putString(KEY_SSID, "Test AP 1");
+        bundle.putInt(KEY_SECURITY, 1);
+        accessPointList.add(new AccessPoint(mContext, bundle));
+        bundle.putString(KEY_SSID, "Test AP 2");
+        bundle.putInt(KEY_SECURITY, 1);
+        accessPointList.add(new AccessPoint(mContext, bundle));
+        bundle.putString(KEY_SSID, "Test AP 3");
+        bundle.putInt(KEY_SECURITY, 2);
+        AccessPoint clickedAccessPoint = new AccessPoint(mContext, bundle);
+        accessPointList.add(clickedAccessPoint);
+        bundle.putString(KEY_SSID, "Test AP 4");
+        bundle.putInt(KEY_SECURITY, 0);
+        accessPointList.add(new AccessPoint(mContext, bundle));
+
+        return accessPointList;
     }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java b/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java
index e32ac6b..0ef0273 100644
--- a/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java
@@ -22,9 +22,11 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
 import android.util.Size;
-import android.view.SurfaceHolder;
 
 import com.android.settings.R;
 
@@ -48,7 +50,7 @@
 public class QrCameraTest {
 
     @Mock
-    private SurfaceHolder mSurfaceHolder;
+    private SurfaceTexture mSurfaceTexture;
 
     private QrCamera mCamera;
     private Context mContext;
@@ -78,6 +80,11 @@
             mCameraCallbacked = true;
             mCallbackSignal.countDown();
         }
+
+        @Override
+        public void setTransform(Matrix transform) {
+            // Do nothing
+        }
     }
 
     private ScannerTestCallback mScannerCallback;
@@ -87,7 +94,7 @@
         mContext = RuntimeEnvironment.application;
         mScannerCallback = new ScannerTestCallback();
         mCamera = new QrCamera(mContext, mScannerCallback);
-        mSurfaceHolder = mock(SurfaceHolder.class);
+        mSurfaceTexture = mock(SurfaceTexture.class);
         mQrCode = "";
         mCameraCallbacked = false;
         mCallbackSignal = null;
@@ -96,7 +103,7 @@
     @Test
     public void testCamera_Init_Callback() throws InterruptedException {
         mCallbackSignal = new CountDownLatch(1);
-        mCamera.start(mSurfaceHolder);
+        mCamera.start(mSurfaceTexture);
         mCallbackSignal.await(5000, TimeUnit.MILLISECONDS);
         assertThat(mCameraCallbacked).isTrue();
     }