Merge "Remove some dead code"
diff --git a/res/layout/nfc_payment_how_it_works.xml b/res/layout/nfc_payment_how_it_works.xml
index 565a16b..87a0095 100644
--- a/res/layout/nfc_payment_how_it_works.xml
+++ b/res/layout/nfc_payment_how_it_works.xml
@@ -15,44 +15,52 @@
             android:background="#FFB2DFDB" >
         </LinearLayout>
 
-        <LinearLayout
-            android:id="@+id/nfc_how_it_works_image_text"
+        <ScrollView
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:paddingLeft="16dp"
-            android:paddingRight="16dp"
-            android:paddingTop="72dp"
-            android:orientation="vertical">
+            android:paddingBottom="48dp"
+            android:fillViewport="true">
 
-          <ImageView
-              android:id="@+id/nfc_how_it_works_image"
-              android:layout_width="match_parent"
-              android:layout_height="188dp"
-              android:gravity="center"
-              android:src="@drawable/nfc_how_it_works" />
+            <LinearLayout
+                android:id="@+id/nfc_how_it_works_image_text"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:paddingLeft="16dp"
+                android:paddingRight="16dp"
+                android:paddingTop="72dp"
+                android:orientation="vertical">
 
-          <TextView
-              android:id="@+id/nfc_how_it_works_title"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:text="@string/nfc_how_it_works_title"
-              android:paddingLeft="16dp"
-              android:paddingRight="16dp"
-              android:textColor="#FF263238"
-              android:textSize="24sp" />
+                <ImageView
+                    android:id="@+id/nfc_how_it_works_image"
+                    android:layout_width="match_parent"
+                    android:layout_height="188dp"
+                    android:gravity="center"
+                    android:src="@drawable/nfc_how_it_works" />
 
-          <TextView
-              android:id="@+id/nfc_how_it_works_content"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:paddingTop="10dp"
-              android:paddingBottom="18dp"
-              android:paddingLeft="16dp"
-              android:paddingRight="16dp"
-              android:text="@string/nfc_how_it_works_content"
-              android:textColor="#FF263238"
-              android:textSize="16sp" />
-        </LinearLayout>
+                <TextView
+                    android:id="@+id/nfc_how_it_works_title"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/nfc_how_it_works_title"
+                    android:paddingLeft="16dp"
+                    android:paddingRight="16dp"
+                    android:textColor="#FF263238"
+                    android:textSize="24sp" />
+
+                <TextView
+                    android:id="@+id/nfc_how_it_works_content"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:paddingTop="10dp"
+                    android:paddingBottom="18dp"
+                    android:paddingLeft="16dp"
+                    android:paddingRight="16dp"
+                    android:text="@string/nfc_how_it_works_content"
+                    android:textColor="#FF263238"
+                    android:textSize="16sp" />
+            </LinearLayout>
+        </ScrollView>
+
         <RelativeLayout
             android:layout_width="match_parent"
             android:layout_height="48dp"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6e5f8f4..6130aec 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3091,11 +3091,11 @@
 
 
     <!-- Title for a screen containing all device reset options [CHAR LIMIT=50] -->
-    <string name="reset_dashboard_title">Reset</string>
+    <string name="reset_dashboard_title">Reset options</string>
 
     <!-- Reset Network -->
-    <!-- SD card & phone storage settings screen, setting option name under Backup & Restore heading -->
-    <string name="reset_network_title">Network settings reset</string>
+    <!-- Button title to reset Wi-Fi settings, Mobile data setting, bluetooth settings -->
+    <string name="reset_network_title">Reset Wi-Fi, mobile &amp; Bluetooth</string>
     <!-- SD card & phone storage settings screen, message on screen after user selects Reset network settings [CHAR LIMIT=NONE] -->
     <string name="reset_network_desc">This will reset all network settings, including:\n\n<li>Wi\u2011Fi</li>\n<li>Mobile data</li>\n<li>Bluetooth</li>"</string>
     <!-- SD card & phone storage settings screen, button on screen after user selects Reset network settings -->
@@ -3112,8 +3112,8 @@
     <string name="reset_network_complete_toast">Network settings have been reset</string>
 
     <!-- Master Clear -->
-    <!-- SD card & phone storage settings screen, setting option name under Internal phone storage heading -->
-    <string name="master_clear_title">Factory data reset</string>
+    <!-- Button title to factory data reset the entire device -->
+    <string name="master_clear_title">Erase all data (Factory reset)</string>
     <!-- Summary text for factory data reset describing what will be reset [CHAR_LIMIT=NONE]-->
     <plurals name="master_clear_with_account_summary">
         <item quantity="one">1 account will be reset</item>
diff --git a/res/xml/app_and_notification.xml b/res/xml/app_and_notification.xml
index 47fc378..4a5ee1d 100644
--- a/res/xml/app_and_notification.xml
+++ b/res/xml/app_and_notification.xml
@@ -40,6 +40,11 @@
         </Preference>
     </PreferenceCategory>
 
+    <!-- Empty category to draw divider -->
+    <PreferenceCategory
+        android:key="all_app_info_divider"
+        android:order="-190"/>
+
     <Preference
         android:key="manage_perms"
         android:title="@string/app_permissions"
diff --git a/res/xml/assist_gesture_settings.xml b/res/xml/assist_gesture_settings.xml
index d941c80..cf84e8a 100644
--- a/res/xml/assist_gesture_settings.xml
+++ b/res/xml/assist_gesture_settings.xml
@@ -31,10 +31,4 @@
         android:summary="@string/assist_gesture_summary"
         app:keywords="@string/keywords_squeeze_to_launch_gesture"/>
 
-    <com.android.settings.widget.SeekBarPreference
-        android:key="gesture_assist_sensitivity"
-        android:title="@string/assist_gesture_sensitivity_title"
-        android:defaultValue="5"
-        android:max="10" />
-
 </PreferenceScreen>
diff --git a/res/xml/inapp_notification_settings.xml b/res/xml/inapp_notification_settings.xml
new file mode 100644
index 0000000..7547f3c
--- /dev/null
+++ b/res/xml/inapp_notification_settings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto">
+
+    <Preference
+        android:key="app_link"
+        android:title="@string/app_settings_link"
+        android:order="500"
+        settings:allowDividerAbove="true"/>
+
+</PreferenceScreen>
diff --git a/res/xml/tether_prefs.xml b/res/xml/tether_prefs.xml
index a506291..709c425 100644
--- a/res/xml/tether_prefs.xml
+++ b/res/xml/tether_prefs.xml
@@ -17,10 +17,16 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                   xmlns:settings="http://schemas.android.com/apk/res-auto">
 
+    <Preference
+        android:key="wifi_tether"
+        android:title="@string/wifi_tether_checkbox_text"
+        android:summary="@string/summary_placeholder"
+        android:fragment="com.android.settings.wifi.tether.WifiTetherSettings" />
+
     <SwitchPreference
         android:key="usb_tether_settings"
         android:title="@string/usb_tethering_button_text"
-        android:summary="@string/usb_tethering_subtext"/>
+        android:summary="@string/usb_tethering_subtext" />
 
     <SwitchPreference
         android:key="enable_wifi_ap"
@@ -35,7 +41,7 @@
     <SwitchPreference
         android:key="enable_bluetooth_tethering"
         android:title="@string/bluetooth_tether_checkbox_text"
-        android:summary="@string/bluetooth_tethering_subtext"/>
+        android:summary="@string/bluetooth_tethering_subtext" />
 
     <Preference
         android:key="disabled_on_data_saver"
diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml
new file mode 100644
index 0000000..b83f2f3
--- /dev/null
+++ b/res/xml/wifi_tether_settings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <com.android.settings.widget.ValidatedEditTextPreference
+        android:key="wifi_tether_network_name"
+        android:title="@string/wifi_ssid"
+        android:summary="@string/summary_placeholder" />
+
+    <com.android.settings.widget.ValidatedEditTextPreference
+        android:key="wifi_tether_network_password"
+        android:title="@string/wifi_password" />
+
+    <ListPreference
+        android:key="wifi_tether_network_ap_band"
+        android:title="@string/wifi_ap_band_config"
+        android:summary="@string/summary_placeholder" />
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/ActivityPicker.java b/src/com/android/settings/ActivityPicker.java
index 2c3436f..ae61944 100644
--- a/src/com/android/settings/ActivityPicker.java
+++ b/src/com/android/settings/ActivityPicker.java
@@ -78,6 +78,10 @@
         Parcelable parcel = intent.getParcelableExtra(Intent.EXTRA_INTENT);
         if (parcel instanceof Intent) {
             mBaseIntent = (Intent) parcel;
+            mBaseIntent.setFlags(mBaseIntent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION
+                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                    | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                    | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION));
         } else {
             mBaseIntent = new Intent(Intent.ACTION_MAIN, null);
             mBaseIntent.addCategory(Intent.CATEGORY_DEFAULT);
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index 708e17f..664916a 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -43,6 +43,8 @@
 import com.android.settings.datausage.DataSaverBackend;
 import com.android.settings.wifi.WifiApDialog;
 import com.android.settings.wifi.WifiApEnabler;
+import com.android.settings.wifi.tether.WifiTetherPreferenceController;
+import com.android.settings.wifi.tether.WifiTetherSettings;
 import com.android.settingslib.TetherUtil;
 
 import java.lang.ref.WeakReference;
@@ -63,7 +65,6 @@
     private static final String USB_TETHER_SETTINGS = "usb_tether_settings";
     private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
     private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering";
-    private static final String TETHER_CHOICE = "TETHER_TYPE";
     private static final String DATA_SAVER_FOOTER = "disabled_on_data_saver";
 
     private static final int DIALOG_AP_SETTINGS = 1;
@@ -100,17 +101,14 @@
     private WifiConfiguration mWifiConfig = null;
     private ConnectivityManager mCm;
 
+    private WifiTetherPreferenceController mWifiTetherPreferenceController;
+
     private boolean mRestartWifiApAfterConfigChange;
 
     private boolean mUsbConnected;
     private boolean mMassStorageActive;
 
     private boolean mBluetoothEnableForTether;
-
-    /* Stores the package name and the class name of the provisioning app */
-    private String[] mProvisionApp;
-    private static final int PROVISION_REQUEST = 0;
-
     private boolean mUnavailable;
 
     private DataSaverBackend mDataSaverBackend;
@@ -127,6 +125,13 @@
     }
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mWifiTetherPreferenceController =
+                new WifiTetherPreferenceController(context, getLifecycle());
+    }
+
+    @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
@@ -154,6 +159,7 @@
 
         mEnableWifiAp =
                 (SwitchPreference) findPreference(ENABLE_WIFI_AP);
+
         Preference wifiApSettings = findPreference(WIFI_AP_SSID_AND_SECURITY);
         mUsbTether = (SwitchPreference) findPreference(USB_TETHER_SETTINGS);
         mBluetoothTether = (SwitchPreference) findPreference(ENABLE_BLUETOOTH_TETHERING);
@@ -175,12 +181,18 @@
             getPreferenceScreen().removePreference(mUsbTether);
         }
 
-        if (wifiAvailable && !Utils.isMonkeyRunning()) {
-            mWifiApEnabler = new WifiApEnabler(activity, mDataSaverBackend, mEnableWifiAp);
-            initWifiTethering();
+        mWifiTetherPreferenceController.displayPreference(getPreferenceScreen());
+        if (WifiTetherSettings.isTetherSettingPageEnabled()) {
+            removePreference(ENABLE_WIFI_AP);
+            removePreference(WIFI_AP_SSID_AND_SECURITY);
         } else {
-            getPreferenceScreen().removePreference(mEnableWifiAp);
-            getPreferenceScreen().removePreference(wifiApSettings);
+            if (wifiAvailable && !Utils.isMonkeyRunning()) {
+                mWifiApEnabler = new WifiApEnabler(activity, mDataSaverBackend, mEnableWifiAp);
+                initWifiTethering();
+            } else {
+                getPreferenceScreen().removePreference(mEnableWifiAp);
+                getPreferenceScreen().removePreference(wifiApSettings);
+            }
         }
 
         if (!bluetoothAvailable) {
diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java
index 03a620b..adb0659 100644
--- a/src/com/android/settings/accounts/AccountPreferenceController.java
+++ b/src/com/android/settings/accounts/AccountPreferenceController.java
@@ -16,6 +16,12 @@
 
 package com.android.settings.accounts;
 
+import static android.content.Intent.EXTRA_USER;
+import static android.os.UserManager.DISALLOW_MODIFY_ACCOUNTS;
+import static android.os.UserManager.DISALLOW_REMOVE_MANAGED_PROFILE;
+import static android.provider.Settings.ACTION_ADD_ACCOUNT;
+import static android.provider.Settings.EXTRA_AUTHORITIES;
+
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.content.BroadcastReceiver;
@@ -48,8 +54,8 @@
 import com.android.settings.core.PreferenceController;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.search.SearchIndexableRaw;
 import com.android.settings.search.SearchFeatureProviderImpl;
+import com.android.settings.search.SearchIndexableRaw;
 import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.accounts.AuthenticatorHelper;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -61,17 +67,11 @@
 import java.util.Comparator;
 import java.util.List;
 
-import static android.content.Intent.EXTRA_USER;
-import static android.os.UserManager.DISALLOW_MODIFY_ACCOUNTS;
-import static android.os.UserManager.DISALLOW_REMOVE_MANAGED_PROFILE;
-import static android.provider.Settings.EXTRA_AUTHORITIES;
-
 public class AccountPreferenceController extends PreferenceController
         implements AuthenticatorHelper.OnAccountsUpdateListener,
         OnPreferenceClickListener, LifecycleObserver, OnPause, OnResume {
 
     private static final String TAG = "AccountPrefController";
-    private static final String ADD_ACCOUNT_ACTION = "android.settings.ADD_ACCOUNT_SETTINGS";
 
     private static final int ORDER_ACCOUNT_PROFILES = 1;
     private static final int ORDER_LAST = 1002;
@@ -232,7 +232,7 @@
         for (int i = 0; i < count; i++) {
             ProfileData profileData = mProfiles.valueAt(i);
             if (preference == profileData.addAccountPreference) {
-                Intent intent = new Intent(ADD_ACCOUNT_ACTION);
+                Intent intent = new Intent(ACTION_ADD_ACCOUNT);
                 intent.putExtra(EXTRA_USER, profileData.userInfo.getUserHandle());
                 intent.putExtra(EXTRA_AUTHORITIES, mAuthorities);
                 mContext.startActivity(intent);
diff --git a/src/com/android/settings/accounts/UserAndAccountDashboardFragment.java b/src/com/android/settings/accounts/UserAndAccountDashboardFragment.java
index 792b77a..d8b19a1 100644
--- a/src/com/android/settings/accounts/UserAndAccountDashboardFragment.java
+++ b/src/com/android/settings/accounts/UserAndAccountDashboardFragment.java
@@ -81,15 +81,6 @@
         return controllers;
     }
 
-    @Override
-    protected boolean displayTile(Tile tile) {
-        final Bundle metadata = tile.metaData;
-        if (metadata != null) {
-            return metadata.getString(METADATA_IA_ACCOUNT) == null;
-        }
-        return true;
-    }
-
     private static class SummaryProvider implements SummaryLoader.SummaryProvider {
 
         private final Context mContext;
diff --git a/src/com/android/settings/applications/RecentAppsPreferenceController.java b/src/com/android/settings/applications/RecentAppsPreferenceController.java
index 7a99508..7eeda67 100644
--- a/src/com/android/settings/applications/RecentAppsPreferenceController.java
+++ b/src/com/android/settings/applications/RecentAppsPreferenceController.java
@@ -61,6 +61,8 @@
     private static final String TAG = "RecentAppsCtrl";
     private static final String KEY_PREF_CATEGORY = "recent_apps_category";
     @VisibleForTesting
+    static final String KEY_DIVIDER = "all_app_info_divider";
+    @VisibleForTesting
     static final String KEY_SEE_ALL = "all_app_info";
     private static final int SHOW_RECENT_APP_COUNT = 5;
     private static final Set<String> SKIP_SYSTEM_PACKAGES = new ArraySet<>();
@@ -77,6 +79,7 @@
 
     private PreferenceCategory mCategory;
     private Preference mSeeAllPref;
+    private Preference mDivider;
     private boolean mHasRecentApps;
 
     static {
@@ -121,12 +124,14 @@
         super.updateNonIndexableKeys(keys);
         // Don't index category name into search. It's not actionable.
         keys.add(KEY_PREF_CATEGORY);
+        keys.add(KEY_DIVIDER);
     }
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
         mCategory = (PreferenceCategory) screen.findPreference(getPreferenceKey());
         mSeeAllPref = screen.findPreference(KEY_SEE_ALL);
+        mDivider = screen.findPreference(KEY_DIVIDER);
         super.displayPreference(screen);
         refreshUi(mCategory.getContext());
     }
@@ -180,6 +185,7 @@
 
     private void displayOnlyAppInfo() {
         mCategory.setTitle(null);
+        mDivider.setVisible(false);
         mSeeAllPref.setTitle(R.string.applications_settings);
         mSeeAllPref.setIcon(null);
         int prefCount = mCategory.getPreferenceCount();
@@ -193,6 +199,7 @@
 
     private void displayRecentApps(Context prefContext, List<UsageStats> recentApps) {
         mCategory.setTitle(R.string.recent_app_category_title);
+        mDivider.setVisible(true);
         mSeeAllPref.setSummary(null);
         mSeeAllPref.setIcon(R.drawable.ic_chevron_right_24dp);
 
diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
index dddfb1b..7c8c8f1 100644
--- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
+++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
@@ -32,6 +32,7 @@
 import com.android.settings.network.NetworkDashboardFragment;
 import com.android.settings.notification.ConfigureNotificationSettings;
 import com.android.settings.notification.SoundSettings;
+import com.android.settings.security.LockscreenDashboardFragment;
 import com.android.settings.system.SystemDashboardFragment;
 import com.android.settingslib.drawer.CategoryKey;
 
@@ -75,7 +76,7 @@
         PARENT_TO_CATEGORY_KEY_MAP.put(SecuritySettings.class.getName(),
                 CategoryKey.CATEGORY_SECURITY);
         PARENT_TO_CATEGORY_KEY_MAP.put(AccountDetailDashboardFragment.class.getName(),
-                CategoryKey.CATEGORY_ACCOUNT);
+                CategoryKey.CATEGORY_ACCOUNT_DETAIL);
         PARENT_TO_CATEGORY_KEY_MAP.put(UserAndAccountDashboardFragment.class.getName(),
                 CategoryKey.CATEGORY_ACCOUNT);
         PARENT_TO_CATEGORY_KEY_MAP.put(
@@ -86,6 +87,8 @@
                 CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT);
         PARENT_TO_CATEGORY_KEY_MAP.put(ConfigureNotificationSettings.class.getName(),
                 CategoryKey.CATEGORY_NOTIFICATIONS);
+        PARENT_TO_CATEGORY_KEY_MAP.put(LockscreenDashboardFragment.class.getName(),
+                CategoryKey.CATEGORY_SECURITY_LOCKSCREEN);
 
         CATEGORY_KEY_TO_PARENT_MAP = new ArrayMap<>(PARENT_TO_CATEGORY_KEY_MAP.size());
 
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
index 26b3427..e668812 100644
--- a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
@@ -28,9 +28,9 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.SetupWizardUtils;
-import com.android.settings.password.ChooseLockGeneric;
 import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
 import com.android.settings.password.SetupChooseLockGeneric;
+import com.android.settings.password.SetupSkipDialog;
 
 public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntroduction {
 
@@ -98,9 +98,8 @@
             setResult(RESULT_SKIP);
             finish();
         } else {
-            SetupSkipDialog dialog = SetupSkipDialog.newInstance(
-                    getIntent().getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false));
-            dialog.show(getFragmentManager());
+            setResult(SetupSkipDialog.RESULT_SKIP);
+            finish();
         }
     }
 
diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java
index ef15d51..2d0030d 100644
--- a/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java
+++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java
@@ -101,9 +101,17 @@
         switch (anomalyAction.getActionType()) {
             case Anomaly.AnomalyActionType.FORCE_STOP:
                 return new AlertDialog.Builder(context)
-                        .setTitle(R.string.force_stop_dlg_title)
-                        .setMessage(R.string.force_stop_dlg_text)
-                        .setPositiveButton(R.string.dlg_ok, this)
+                        .setTitle(R.string.dialog_stop_title)
+                        .setMessage(getString(R.string.dialog_stop_message, mAnomaly.displayName))
+                        .setPositiveButton(R.string.dialog_stop_ok, this)
+                        .setNegativeButton(R.string.dlg_cancel, null)
+                        .create();
+            case Anomaly.AnomalyActionType.BACKGROUND_CHECK:
+                return new AlertDialog.Builder(context)
+                        .setTitle(R.string.dialog_background_check_title)
+                        .setMessage(getString(R.string.dialog_background_check_message,
+                                mAnomaly.displayName))
+                        .setPositiveButton(R.string.dialog_background_check_ok, this)
                         .setNegativeButton(R.string.dlg_cancel, null)
                         .create();
             default:
diff --git a/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java b/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java
index 4d4b136..3344eb6 100644
--- a/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java
+++ b/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java
@@ -24,11 +24,18 @@
 public interface AnomalyAction {
     /**
      * handle the action when user clicks positive button
-     * @param Anomaly about the app that we need to handle
+     * @param anomaly about the app that we need to handle
      * @param metricsKey key for the page that invokes the action
      *
      * @see com.android.internal.logging.nano.MetricsProto
      */
-    void handlePositiveAction(Anomaly Anomaly, int metricsKey);
+    void handlePositiveAction(Anomaly anomaly, int metricsKey);
+
+    /**
+     * Check whether the action is active for {@code anomaly}
+     * @param anomaly about the app that we need to handle
+     * @return {@code true} if action is active, otherwise return {@code false}
+     */
+    boolean isActionActive(Anomaly anomaly);
     int getActionType();
 }
diff --git a/src/com/android/settings/fuelgauge/anomaly/action/BackgroundCheckAction.java b/src/com/android/settings/fuelgauge/anomaly/action/BackgroundCheckAction.java
index 8c7e827..16fd0df 100644
--- a/src/com/android/settings/fuelgauge/anomaly/action/BackgroundCheckAction.java
+++ b/src/com/android/settings/fuelgauge/anomaly/action/BackgroundCheckAction.java
@@ -46,6 +46,14 @@
     }
 
     @Override
+    public boolean isActionActive(Anomaly anomaly) {
+        final int mode = mAppOpsManager
+                .checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, anomaly.uid,
+                        anomaly.packageName);
+        return mode != AppOpsManager.MODE_IGNORED && mode != AppOpsManager.MODE_ERRORED;
+    }
+
+    @Override
     public int getActionType() {
         return Anomaly.AnomalyActionType.BACKGROUND_CHECK;
     }
diff --git a/src/com/android/settings/fuelgauge/anomaly/action/ForceStopAction.java b/src/com/android/settings/fuelgauge/anomaly/action/ForceStopAction.java
index c124c9e..557b2a9 100644
--- a/src/com/android/settings/fuelgauge/anomaly/action/ForceStopAction.java
+++ b/src/com/android/settings/fuelgauge/anomaly/action/ForceStopAction.java
@@ -18,6 +18,9 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
 import android.util.Pair;
 
 import com.android.internal.logging.nano.MetricsProto;
@@ -25,20 +28,25 @@
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.overlay.FeatureFactory;
 
+import java.util.List;
+
 /**
  * Force stop action for anomaly app, which means to stop the app which causes anomaly
  */
 public class ForceStopAction implements AnomalyAction {
+    private static final String TAG = "ForceStopAction";
 
     private Context mContext;
     private MetricsFeatureProvider mMetricsFeatureProvider;
     private ActivityManager mActivityManager;
+    private PackageManager mPackageManager;
 
     public ForceStopAction(Context context) {
         mContext = context;
         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
         mActivityManager = (ActivityManager) context.getSystemService(
                 Context.ACTIVITY_SERVICE);
+        mPackageManager = context.getPackageManager();
     }
 
     @Override
@@ -53,6 +61,18 @@
     }
 
     @Override
+    public boolean isActionActive(Anomaly anomaly) {
+        try {
+            ApplicationInfo info = mPackageManager.getApplicationInfo(anomaly.packageName,
+                    PackageManager.GET_META_DATA);
+            return (info.flags & ApplicationInfo.FLAG_STOPPED) == 0;
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Cannot find info for app: " + anomaly.packageName);
+        }
+        return false;
+    }
+
+    @Override
     public int getActionType() {
         return Anomaly.AnomalyActionType.FORCE_STOP;
     }
diff --git a/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetector.java b/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetector.java
index 8568d37..74c43d0 100644
--- a/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetector.java
+++ b/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetector.java
@@ -17,14 +17,11 @@
 package com.android.settings.fuelgauge.anomaly.checker;
 
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.BatteryStats;
 import android.os.SystemClock;
 import android.support.annotation.VisibleForTesting;
-import android.text.format.DateUtils;
 import android.util.ArrayMap;
-import android.util.Log;
 
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
@@ -32,6 +29,8 @@
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
+import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
+import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -47,6 +46,8 @@
     BatteryUtils mBatteryUtils;
     @VisibleForTesting
     long mWakeLockThresholdMs;
+    @VisibleForTesting
+    AnomalyAction mAnomalyAction;
 
     public WakeLockAnomalyDetector(Context context) {
         this(context, new AnomalyDetectionPolicy(context));
@@ -57,6 +58,8 @@
         mContext = context;
         mPackageManager = context.getPackageManager();
         mBatteryUtils = BatteryUtils.getInstance(context);
+        mAnomalyAction = AnomalyUtils.getInstance(context).getAnomalyAction(
+                Anomaly.AnomalyType.WAKE_LOCK);
 
         mWakeLockThresholdMs = policy.wakeLockThreshold;
     }
@@ -111,7 +114,10 @@
                         .setDisplayName(displayName)
                         .setPackageName(packageName)
                         .build();
-                anomalies.add(anomaly);
+
+                if (mAnomalyAction.isActionActive(anomaly)) {
+                    anomalies.add(anomaly);
+                }
             }
 
         }
diff --git a/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java b/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java
index 83b8d9a..3502e73 100644
--- a/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java
+++ b/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java
@@ -17,9 +17,7 @@
 package com.android.settings.fuelgauge.anomaly.checker;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.BatteryStats;
-import android.os.SystemClock;
 import android.support.annotation.VisibleForTesting;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -30,6 +28,8 @@
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
+import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
+import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -41,6 +41,8 @@
     private static final String TAG = "WakeupAlarmAnomalyDetector";
     @VisibleForTesting
     BatteryUtils mBatteryUtils;
+    @VisibleForTesting
+    AnomalyAction mAnomalyAction;
     private long mWakeupAlarmThreshold;
     private Context mContext;
 
@@ -52,6 +54,8 @@
     WakeupAlarmAnomalyDetector(Context context, AnomalyDetectionPolicy policy) {
         mContext = context;
         mBatteryUtils = BatteryUtils.getInstance(context);
+        mAnomalyAction = AnomalyUtils.getInstance(context).getAnomalyAction(
+                Anomaly.AnomalyType.WAKEUP_ALARM);
         mWakeupAlarmThreshold = policy.wakeupAlarmThreshold;
     }
 
@@ -91,7 +95,10 @@
                             .setDisplayName(displayName)
                             .setPackageName(packageName)
                             .build();
-                    anomalies.add(anomaly);
+
+                    if (mAnomalyAction.isActionActive(anomaly)) {
+                        anomalies.add(anomaly);
+                    }
                 }
             }
         }
diff --git a/src/com/android/settings/gestures/AssistGestureFeatureProvider.java b/src/com/android/settings/gestures/AssistGestureFeatureProvider.java
index f06f6c1..8902393 100644
--- a/src/com/android/settings/gestures/AssistGestureFeatureProvider.java
+++ b/src/com/android/settings/gestures/AssistGestureFeatureProvider.java
@@ -18,10 +18,24 @@
 
 import android.content.Context;
 
+import com.android.settings.core.PreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.util.List;
+
 /** Feature provider for the assist gesture. */
 public interface AssistGestureFeatureProvider {
 
     /** Returns true if the assist gesture is supported. */
     boolean isSupported(Context context);
 
+    /** Returns true if the sensor is available. */
+    boolean isSensorAvailable(Context context);
+
+    /** Returns the resource */
+    int getPreferenceResourceId();
+
+    /** Returns a list of additional preference controllers */
+    List<PreferenceController> getControllers(Context context, Lifecycle lifecycle);
+
 }
diff --git a/src/com/android/settings/gestures/AssistGestureFeatureProviderImpl.java b/src/com/android/settings/gestures/AssistGestureFeatureProviderImpl.java
index b17d892..a2579ac 100644
--- a/src/com/android/settings/gestures/AssistGestureFeatureProviderImpl.java
+++ b/src/com/android/settings/gestures/AssistGestureFeatureProviderImpl.java
@@ -18,6 +18,13 @@
 
 import android.content.Context;
 
+import com.android.settings.R;
+import com.android.settings.core.PreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.util.ArrayList;
+import java.util.List;
+
 public class AssistGestureFeatureProviderImpl implements AssistGestureFeatureProvider {
 
     @Override
@@ -25,4 +32,19 @@
         return false;
     }
 
+    @Override
+    public boolean isSensorAvailable(Context context) {
+        return false;
+    }
+
+    @Override
+    public int getPreferenceResourceId() {
+        return R.xml.assist_gesture_settings;
+    }
+
+    @Override
+    public List<PreferenceController> getControllers(Context context, Lifecycle lifecycle) {
+        return new ArrayList<>();
+    }
+
 }
diff --git a/src/com/android/settings/gestures/AssistGestureSensitivityPreferenceController.java b/src/com/android/settings/gestures/AssistGestureSensitivityPreferenceController.java
deleted file mode 100644
index 0f42803..0000000
--- a/src/com/android/settings/gestures/AssistGestureSensitivityPreferenceController.java
+++ /dev/null
@@ -1,151 +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.gestures;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.provider.Settings;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-
-import com.android.settings.widget.SeekBarPreference;
-import com.android.settings.core.PreferenceController;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnPause;
-import com.android.settingslib.core.lifecycle.events.OnResume;
-
-public class AssistGestureSensitivityPreferenceController extends PreferenceController
-        implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnPause, OnResume {
-
-    private static final String PREF_KEY_ASSIST_GESTURE_SENSITIVITY = "gesture_assist_sensitivity";
-
-    private final AssistGestureFeatureProvider mFeatureProvider;
-    private final SettingObserver mSettingObserver;
-
-    private PreferenceScreen mScreen;
-    private SeekBarPreference mPreference;
-
-    public AssistGestureSensitivityPreferenceController(Context context, Lifecycle lifecycle) {
-        super(context);
-        mFeatureProvider = FeatureFactory.getFactory(context).getAssistGestureFeatureProvider();
-        mSettingObserver = new SettingObserver();
-
-        if (lifecycle != null) {
-            lifecycle.addObserver(this);
-        }
-    }
-
-    @Override
-    public void onResume() {
-        mSettingObserver.register(mContext.getContentResolver(), true /* register */);
-        updatePreference();
-    }
-
-    @Override
-    public void onPause() {
-        mSettingObserver.register(mContext.getContentResolver(), false /* register */);
-    }
-
-    @Override
-    public boolean isAvailable() {
-        // The sensitivity control is contingent on the assist gesture being supported and the
-        // gesture being enabled.
-        final int gestureEnabled = Settings.Secure.getInt(
-                mContext.getContentResolver(),
-                Settings.Secure.ASSIST_GESTURE_ENABLED,
-                1);
-        return (gestureEnabled == 1) && mFeatureProvider.isSupported(mContext);
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        mScreen = screen;
-        mPreference = (SeekBarPreference) screen.findPreference(getPreferenceKey());
-        // Call super last or AbstractPreferenceController might remove the preference from the
-        // screen (if !isAvailable()) before we can save a reference to it.
-        super.displayPreference(screen);
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        super.updateState(preference);
-        updatePreference();
-    }
-
-    private void updatePreference() {
-        if (mPreference == null) {
-            return;
-        }
-
-        if (isAvailable()) {
-            if (mScreen.findPreference(getPreferenceKey()) == null) {
-                mScreen.addPreference(mPreference);
-            }
-        } else {
-            mScreen.removePreference(mPreference);
-        }
-
-        final int sensitivity = Settings.Secure.getInt(
-                mContext.getContentResolver(),
-                Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
-                mPreference.getProgress());
-        mPreference.setProgress(sensitivity);
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final int sensitivity = (int) newValue;
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ASSIST_GESTURE_SENSITIVITY, sensitivity);
-        return true;
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return PREF_KEY_ASSIST_GESTURE_SENSITIVITY;
-    }
-
-    class SettingObserver extends ContentObserver {
-
-        private final Uri ASSIST_GESTURE_ENABLED_URI =
-                Settings.Secure.getUriFor(Settings.Secure.ASSIST_GESTURE_ENABLED);
-        private final Uri ASSIST_GESTURE_SENSITIVITY_URI =
-                Settings.Secure.getUriFor(Settings.Secure.ASSIST_GESTURE_SENSITIVITY);
-
-        public SettingObserver() {
-            super(null /* handler */);
-        }
-
-        public void register(ContentResolver cr, boolean register) {
-            if (register) {
-                cr.registerContentObserver(ASSIST_GESTURE_ENABLED_URI, false, this);
-                cr.registerContentObserver(ASSIST_GESTURE_SENSITIVITY_URI, false, this);
-            } else {
-                cr.unregisterContentObserver(this);
-            }
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            updatePreference();
-        }
-    }
-}
diff --git a/src/com/android/settings/gestures/AssistGestureSettings.java b/src/com/android/settings/gestures/AssistGestureSettings.java
index 64187a3..93d3a39 100644
--- a/src/com/android/settings/gestures/AssistGestureSettings.java
+++ b/src/com/android/settings/gestures/AssistGestureSettings.java
@@ -23,6 +23,7 @@
 import com.android.settings.R;
 import com.android.settings.core.PreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
@@ -48,7 +49,8 @@
 
     @Override
     protected int getPreferenceScreenResId() {
-        return R.xml.assist_gesture_settings;
+        return FeatureFactory.getFactory(getContext())
+                .getAssistGestureFeatureProvider().getPreferenceResourceId();
     }
 
     @Override
@@ -60,7 +62,9 @@
             Lifecycle lifecycle) {
         final List<PreferenceController> controllers = new ArrayList<>();
         controllers.add(new AssistGesturePreferenceController(context, lifecycle, KEY_ASSIST));
-        controllers.add(new AssistGestureSensitivityPreferenceController(context, lifecycle));
+        controllers.addAll(FeatureFactory.getFactory(context).getAssistGestureFeatureProvider()
+                .getControllers(context, lifecycle));
+
         return controllers;
     }
 
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 48ca207..78b763c 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -264,12 +264,9 @@
 
     protected void addAppLinkPref() {
         if (mAppRow.settingsIntent != null && mAppLink == null) {
-            mAppLink = new Preference(getPrefContext());
-            mAppLink.setKey(KEY_APP_LINK);
-            mAppLink.setOrder(500);
+            addPreferencesFromResource(R.xml.inapp_notification_settings);
+            mAppLink = (Preference) findPreference(KEY_APP_LINK);
             mAppLink.setIntent(mAppRow.settingsIntent);
-            mAppLink.setTitle(mContext.getString(R.string.app_settings_link));
-            getPreferenceScreen().addPreference(mAppLink);
         }
     }
 
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index d4bc076..b41a40f 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -201,7 +201,7 @@
         private String mFirstPin;
         private RecyclerView mPasswordRestrictionView;
         protected boolean mIsAlphaMode;
-        private Button mCancelButton;
+        protected Button mCancelButton;
         private Button mNextButton;
 
         private TextChangedHandler mTextChangedHandler;
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index 9a494cb..69498a9 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -44,6 +44,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.TextViewInputDisabler;
 import com.android.settings.R;
+import com.android.settings.widget.ImeAwareEditText;
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.settingslib.animation.DisappearAnimationUtils;
 
@@ -93,7 +94,7 @@
             CredentialCheckResultTracker.Listener {
         private static final long ERROR_MESSAGE_TIMEOUT = 3000;
         private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
-        private TextView mPasswordEntry;
+        private ImeAwareEditText mPasswordEntry;
         private TextViewInputDisabler mPasswordEntryInputDisabler;
         private AsyncTask<?, ?, ?> mPendingLockCheck;
         private CredentialCheckResultTracker mCredentialCheckResultTracker;
@@ -132,7 +133,7 @@
                     container,
                     false);
 
-            mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
+            mPasswordEntry = (ImeAwareEditText) view.findViewById(R.id.password_entry);
             mPasswordEntry.setOnEditorActionListener(this);
             // EditText inside ScrollView doesn't automatically get focus.
             mPasswordEntry.requestFocus();
@@ -310,7 +311,7 @@
             mPasswordEntry.setEnabled(true);
             mPasswordEntryInputDisabler.setInputEnabled(true);
             if (shouldAutoShowSoftKeyboard()) {
-                mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+                mPasswordEntry.scheduleShowSoftInput();
             }
         }
 
diff --git a/src/com/android/settings/password/SetupChooseLockGeneric.java b/src/com/android/settings/password/SetupChooseLockGeneric.java
index 4e73b87..179bd79 100644
--- a/src/com/android/settings/password/SetupChooseLockGeneric.java
+++ b/src/com/android/settings/password/SetupChooseLockGeneric.java
@@ -35,7 +35,6 @@
 import com.android.settings.SetupEncryptionInterstitial;
 import com.android.settings.SetupWizardUtils;
 import com.android.settings.fingerprint.SetupFingerprintEnrollFindSensor;
-import com.android.settings.fingerprint.SetupSkipDialog;
 import com.android.settings.utils.SettingsDividerItemDecoration;
 import com.android.setupwizardlib.GlifPreferenceLayout;
 
diff --git a/src/com/android/settings/password/SetupChooseLockPassword.java b/src/com/android/settings/password/SetupChooseLockPassword.java
index 0c62c7c..bd935a2 100644
--- a/src/com/android/settings/password/SetupChooseLockPassword.java
+++ b/src/com/android/settings/password/SetupChooseLockPassword.java
@@ -84,6 +84,9 @@
         @Override
         public void onViewCreated(View view, Bundle savedInstanceState) {
             super.onViewCreated(view, savedInstanceState);
+
+            mCancelButton.setText(R.string.skip_label);
+
             boolean showOptionsButton = getActivity().getIntent().getBooleanExtra(
                     ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, false);
             if (showOptionsButton) {
@@ -99,6 +102,12 @@
                 case R.id.screen_lock_options:
                     launchChooseLockGeneric();
                     break;
+                case R.id.cancel_button:
+                    SetupSkipDialog dialog = SetupSkipDialog.newInstance(
+                            getActivity().getIntent()
+                                    .getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false));
+                    dialog.show(getFragmentManager());
+                    break;
                 default:
                     super.onClick(v);
             }
diff --git a/src/com/android/settings/fingerprint/SetupSkipDialog.java b/src/com/android/settings/password/SetupSkipDialog.java
similarity index 94%
rename from src/com/android/settings/fingerprint/SetupSkipDialog.java
rename to src/com/android/settings/password/SetupSkipDialog.java
index 842e69c..36646b7 100644
--- a/src/com/android/settings/fingerprint/SetupSkipDialog.java
+++ b/src/com/android/settings/password/SetupSkipDialog.java
@@ -14,12 +14,11 @@
  * limitations under the License
  */
 
-package com.android.settings.fingerprint;
+package com.android.settings.password;
 
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.app.DialogFragment;
 import android.app.FragmentManager;
 import android.content.DialogInterface;
 import android.os.Bundle;
@@ -36,7 +35,7 @@
 
     private static final String ARG_FRP_SUPPORTED = "frp_supported";
     private static final String TAG_SKIP_DIALOG = "skip_dialog";
-    private static final int RESULT_SKIP = Activity.RESULT_FIRST_USER + 10;
+    public static final int RESULT_SKIP = Activity.RESULT_FIRST_USER + 10;
 
     public static SetupSkipDialog newInstance(boolean isFrpSupported) {
         SetupSkipDialog dialog = new SetupSkipDialog();
diff --git a/src/com/android/settings/search/AppSearchResult.java b/src/com/android/settings/search/AppSearchResult.java
index fcb83dc..b59e32e 100644
--- a/src/com/android/settings/search/AppSearchResult.java
+++ b/src/com/android/settings/search/AppSearchResult.java
@@ -18,6 +18,7 @@
 package com.android.settings.search;
 
 import android.content.pm.ApplicationInfo;
+import android.os.UserHandle;
 
 public class AppSearchResult extends SearchResult {
     /**
@@ -30,6 +31,10 @@
         info = builder.mInfo;
     }
 
+    public UserHandle getAppUserHandle() {
+        return new UserHandle(UserHandle.getUserId(info.uid));
+    }
+
     public static class Builder extends SearchResult.Builder {
         protected ApplicationInfo mInfo;
 
diff --git a/src/com/android/settings/search/InstalledAppResultLoader.java b/src/com/android/settings/search/InstalledAppResultLoader.java
index 81b96a1..70a39ee 100644
--- a/src/com/android/settings/search/InstalledAppResultLoader.java
+++ b/src/com/android/settings/search/InstalledAppResultLoader.java
@@ -40,6 +40,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -99,7 +100,7 @@
 
                 final AppSearchResult.Builder builder = new AppSearchResult.Builder();
                 builder.setAppInfo(info)
-                        .setStableId(info.packageName.hashCode())
+                        .setStableId(Objects.hash(info.packageName, user.id))
                         .setTitle(info.loadLabel(pm))
                         .setRank(getRank(wordDiff))
                         .addBreadcrumbs(getBreadCrumb())
diff --git a/src/com/android/settings/search/IntentSearchViewHolder.java b/src/com/android/settings/search/IntentSearchViewHolder.java
index 11adaef..2722d56 100644
--- a/src/com/android/settings/search/IntentSearchViewHolder.java
+++ b/src/com/android/settings/search/IntentSearchViewHolder.java
@@ -16,9 +16,11 @@
  */
 package com.android.settings.search;
 
+import android.content.Intent;
+import android.os.UserHandle;
 import android.view.View;
 
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto;
 
 /**
  * ViewHolder for intent based search results.
@@ -32,7 +34,7 @@
 
     @Override
     public int getClickActionMetricName() {
-        return MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_RESULT;
+        return MetricsProto.MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_RESULT;
     }
 
     @Override
@@ -41,7 +43,15 @@
 
         itemView.setOnClickListener(v -> {
            fragment.onSearchResultClicked(this, result);
-           fragment.startActivity(result.payload.getIntent());
+            final Intent intent = result.payload.getIntent();
+            // Use app user id to support work profile use case.
+            if (result instanceof AppSearchResult) {
+                AppSearchResult appResult = (AppSearchResult) result;
+                UserHandle userHandle = appResult.getAppUserHandle();
+                fragment.getActivity().startActivityAsUser(intent, userHandle);
+            } else {
+                fragment.startActivity(intent);
+            }
         });
     }
 }
diff --git a/src/com/android/settings/search/SearchViewHolder.java b/src/com/android/settings/search/SearchViewHolder.java
index 72fd02320..ed72940 100644
--- a/src/com/android/settings/search/SearchViewHolder.java
+++ b/src/com/android/settings/search/SearchViewHolder.java
@@ -18,8 +18,11 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
+import android.util.IconDrawableFactory;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -46,6 +49,7 @@
 
     protected final MetricsFeatureProvider mMetricsFeatureProvider;
     protected final SearchFeatureProvider mSearchFeatureProvider;
+    private final IconDrawableFactory mIconDrawableFactory;
 
     public SearchViewHolder(View view) {
         super(view);
@@ -59,6 +63,7 @@
         breadcrumbView = view.findViewById(R.id.breadcrumb);
 
         mPlaceholderSummary = view.getContext().getString(R.string.summary_placeholder);
+        mIconDrawableFactory = IconDrawableFactory.newInstance(view.getContext());
     }
 
     public abstract int getClickActionMetricName();
@@ -78,7 +83,12 @@
         if (result instanceof AppSearchResult) {
             AppSearchResult appResult = (AppSearchResult) result;
             PackageManager pm = fragment.getActivity().getPackageManager();
-            iconView.setImageDrawable(appResult.info.loadIcon(pm));
+            UserHandle userHandle = appResult.getAppUserHandle();
+            Drawable badgedIcon =
+                    mIconDrawableFactory.getBadgedIcon(appResult.info, userHandle.getIdentifier());
+            iconView.setImageDrawable(badgedIcon);
+            titleView.setContentDescription(
+                    pm.getUserBadgedLabel(appResult.info.loadLabel(pm), userHandle));
         } else {
             // Valid even when result.icon is null.
             iconView.setImageDrawable(result.icon);
diff --git a/src/com/android/settings/widget/ScrollToParentEditText.java b/src/com/android/settings/widget/ScrollToParentEditText.java
index 97602af..705e918 100644
--- a/src/com/android/settings/widget/ScrollToParentEditText.java
+++ b/src/com/android/settings/widget/ScrollToParentEditText.java
@@ -23,12 +23,14 @@
 import android.view.ViewParent;
 import android.widget.EditText;
 
+import com.android.settings.widget.ImeAwareEditText;
+
 /**
  * An EditText that, instead of scrolling to itself when focused, will request scrolling to its
  * parent. This is used in ChooseLockPassword to do make a best effort for not hiding the error
  * messages for why the password is invalid under the keyboard.
  */
-public class ScrollToParentEditText extends EditText {
+public class ScrollToParentEditText extends ImeAwareEditText {
 
     private Rect mRect = new Rect();
 
diff --git a/src/com/android/settings/widget/ValidatedEditTextPreference.java b/src/com/android/settings/widget/ValidatedEditTextPreference.java
new file mode 100644
index 0000000..53ff37a
--- /dev/null
+++ b/src/com/android/settings/widget/ValidatedEditTextPreference.java
@@ -0,0 +1,111 @@
+/*
+ * 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.widget;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.EditText;
+
+import com.android.settings.CustomEditTextPreference;
+
+/**
+ * {@code EditTextPreference} that supports input validation.
+ */
+public class ValidatedEditTextPreference extends CustomEditTextPreference {
+
+    public interface Validator {
+        boolean isTextValid(String value);
+    }
+
+    private final EditTextWatcher mTextWatcher = new EditTextWatcher();
+    private Validator mValidator;
+    private boolean mIsPassword;
+
+    public ValidatedEditTextPreference(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public ValidatedEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public ValidatedEditTextPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ValidatedEditTextPreference(Context context) {
+        super(context);
+    }
+
+    @Override
+    protected void onBindDialogView(View view) {
+        super.onBindDialogView(view);
+        if (mValidator != null) {
+            final EditText editText = view.findViewById(android.R.id.edit);
+            if (editText != null) {
+                editText.removeTextChangedListener(mTextWatcher);
+                if (mIsPassword) {
+                    editText.setInputType(
+                            InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+                    editText.setMaxLines(1);
+                }
+                editText.addTextChangedListener(mTextWatcher);
+            }
+        }
+    }
+
+    public void setIsPassword(boolean isPassword) {
+        mIsPassword = isPassword;
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+    public boolean isPassword() {
+        return mIsPassword;
+    }
+
+    public void setValidator(Validator validator) {
+        mValidator = validator;
+    }
+
+    private class EditTextWatcher implements TextWatcher {
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+        }
+
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int before, int count) {
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {
+            final EditText editText = getEditText();
+            if (mValidator != null && editText != null) {
+                final AlertDialog dialog = (AlertDialog) getDialog();
+                final boolean valid = mValidator.isTextValid(editText.getText().toString());
+                dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(valid);
+            }
+        }
+    }
+
+}
diff --git a/src/com/android/settings/wifi/WifiApEnabler.java b/src/com/android/settings/wifi/WifiApEnabler.java
index 5d725d8..675bf28 100644
--- a/src/com/android/settings/wifi/WifiApEnabler.java
+++ b/src/com/android/settings/wifi/WifiApEnabler.java
@@ -32,6 +32,10 @@
 
 import java.util.ArrayList;
 
+/**
+ * @deprecated in favor of WifiTetherPreferenceController and WifiTetherSettings
+ */
+@Deprecated
 public class WifiApEnabler {
     private final Context mContext;
     private final SwitchPreference mSwitch;
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 2a17dfc..6f87342 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -110,7 +110,6 @@
     public static final int WIFI_PEAP_PHASE2_AKA        = 4;
     public static final int WIFI_PEAP_PHASE2_AKA_PRIME  = 5;
 
-    private static final int SSID_ASCII_MAX_LENGTH = 32;
 
     /* Phase2 methods supported by PEAP are limited */
     private final ArrayAdapter<String> mPhase2PeapAdapter;
@@ -463,7 +462,7 @@
 
         if (mSsidView != null) {
             final String ssid = mSsidView.getText().toString();
-            if (ssid.length() > SSID_ASCII_MAX_LENGTH) {
+            if (WifiUtils.isSSIDTooLong(ssid)) {
                 mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.VISIBLE);
             }
         }
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 6d575d8..3cfb17d 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -43,7 +43,6 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceCategory;
-import android.support.v7.preference.PreferenceManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.ContextMenu;
@@ -321,7 +320,6 @@
 
         if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) {
             mOpenSsid = intent.getStringExtra(EXTRA_START_CONNECT_SSID);
-            updateAccessPointsDelayed();
         }
     }
 
@@ -354,13 +352,28 @@
         onWifiStateChanged(mWifiManager.getWifiState());
     }
 
-    private void forceUpdateAPs() {
+    /**
+     * Only update the AP list if there are not any APs currently shown.
+     *
+     * <p>Thus forceUpdate will only be called during cold start or when toggling between wifi on
+     * and off. In other use cases, the previous APs will remain until the next update is received
+     * from {@link WifiTracker}.
+     */
+    private void conditionallyForceUpdateAPs() {
+        if (mAccessPointsPreferenceCategory.getPreferenceCount() > 0
+                && mAccessPointsPreferenceCategory.getPreference(0) instanceof
+                        AccessPointPreference) {
+            // Make sure we don't update due to callbacks initiated by sticky broadcasts in
+            // WifiTracker.
+            Log.d(TAG, "Did not force update APs due to existing APs displayed");
+            getView().removeCallbacks(mUpdateAccessPointsRunnable);
+            return;
+        }
         setProgressBarVisible(true);
         mWifiTracker.forceUpdate();
         if (DEBUG) {
             Log.d(TAG, "WifiSettings force update APs: " + mWifiTracker.getAccessPoints());
         }
-
         getView().removeCallbacks(mUpdateAccessPointsRunnable);
         updateAccessPointPreferences();
     }
@@ -654,6 +667,7 @@
      */
     @Override
     public void onAccessPointsChanged() {
+        Log.d(TAG, "onAccessPointsChanged (WifiTracker) callback initiated");
         updateAccessPointsDelayed();
     }
 
@@ -679,7 +693,7 @@
         final int wifiState = mWifiManager.getWifiState();
         switch (wifiState) {
             case WifiManager.WIFI_STATE_ENABLED:
-                forceUpdateAPs();
+                conditionallyForceUpdateAPs();
                 break;
 
             case WifiManager.WIFI_STATE_ENABLING:
@@ -720,6 +734,9 @@
         }
         // AccessPoints are sorted by the WifiTracker
         final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
+        if (DEBUG) {
+            Log.d(TAG, "updateAccessPoints called for: " + accessPoints);
+        }
 
         boolean hasAvailableAccessPoints = false;
         mAccessPointsPreferenceCategory.removePreference(mStatusMessagePreference);
@@ -1015,6 +1032,7 @@
 
     @Override
     public void onAccessPointChanged(final AccessPoint accessPoint) {
+        Log.d(TAG, "onAccessPointChanged (singular) callback initiated");
         View view = getView();
         if (view != null) {
             view.post(new Runnable() {
diff --git a/src/com/android/settings/wifi/WifiUtils.java b/src/com/android/settings/wifi/WifiUtils.java
new file mode 100644
index 0000000..7bd69db
--- /dev/null
+++ b/src/com/android/settings/wifi/WifiUtils.java
@@ -0,0 +1,50 @@
+/*
+ * 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.wifi;
+
+import android.text.TextUtils;
+
+public class WifiUtils {
+
+    private static final int SSID_ASCII_MIN_LENGTH = 1;
+    private static final int SSID_ASCII_MAX_LENGTH = 32;
+    private static final int PASSWORD_MIN_LENGTH = 8;
+    private static final int PASSWORD_MAX_LENGTH = 63;
+
+
+    public static boolean isSSIDTooLong(String ssid) {
+        if (TextUtils.isEmpty(ssid)) {
+            return false;
+        }
+        return ssid.length() > SSID_ASCII_MAX_LENGTH;
+    }
+
+    public static boolean isSSIDTooShort(String ssid) {
+        if (TextUtils.isEmpty(ssid)) {
+            return true;
+        }
+        return ssid.length() < SSID_ASCII_MIN_LENGTH;
+    }
+
+    public static boolean isPasswordValid(String password) {
+        if (TextUtils.isEmpty(password)) {
+            return false;
+        }
+        final int length = password.length();
+        return length >= PASSWORD_MIN_LENGTH && length <= PASSWORD_MAX_LENGTH;
+    }
+}
diff --git a/src/com/android/settings/wifi/tether/NoOpOnStartTetheringCallback.java b/src/com/android/settings/wifi/tether/NoOpOnStartTetheringCallback.java
new file mode 100644
index 0000000..fc1719c
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/NoOpOnStartTetheringCallback.java
@@ -0,0 +1,23 @@
+/*
+ * 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.wifi.tether;
+
+import android.net.ConnectivityManager;
+
+class NoOpOnStartTetheringCallback extends ConnectivityManager.OnStartTetheringCallback {
+
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java
new file mode 100644
index 0000000..37da38e
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.tether;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+
+import static android.net.wifi.WifiConfiguration.AP_BAND_2GHZ;
+import static android.net.wifi.WifiConfiguration.AP_BAND_5GHZ;
+
+public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferenceController {
+
+    private static final String PREF_KEY = "wifi_tether_network_ap_band";
+    private static final String[] BAND_VALUES =
+            {String.valueOf(AP_BAND_2GHZ), String.valueOf(AP_BAND_5GHZ)};
+
+    private final String[] mBandEntries;
+    private int mBandIndex;
+
+    public WifiTetherApBandPreferenceController(Context context,
+            OnTetherConfigUpdateListener listener) {
+        super(context, listener);
+        mBandEntries = mContext.getResources().getStringArray(R.array.wifi_ap_band_config_full);
+        final WifiConfiguration config = mWifiManager.getWifiApConfiguration();
+        if (config != null) {
+            mBandIndex = config.apBand;
+        } else {
+            mBandIndex = 0;
+        }
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        ListPreference preference = (ListPreference) mPreference;
+        if (!is5GhzBandSupported()) {
+            preference.setEnabled(false);
+            preference.setSummary(R.string.wifi_ap_choose_2G);
+        } else {
+            preference.setEntries(mBandEntries);
+            preference.setEntryValues(BAND_VALUES);
+            preference.setSummary(mBandEntries[mBandIndex]);
+            preference.setValue(String.valueOf(mBandIndex));
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREF_KEY;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        mBandIndex = Integer.parseInt((String) newValue);
+        preference.setSummary(mBandEntries[mBandIndex]);
+        mListener.onTetherConfigUpdated();
+        return true;
+    }
+
+    private boolean is5GhzBandSupported() {
+        if (mBandIndex > 0) {
+            return true;
+        }
+        return mWifiManager.is5GHzBandSupported();
+    }
+
+    public int getBandIndex() {
+        return mBandIndex;
+    }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherBasePreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherBasePreferenceController.java
new file mode 100644
index 0000000..eb21175
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherBasePreferenceController.java
@@ -0,0 +1,60 @@
+/*
+ * 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.wifi.tether;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.core.PreferenceController;
+
+public abstract class WifiTetherBasePreferenceController extends PreferenceController
+        implements Preference.OnPreferenceChangeListener {
+
+    public interface OnTetherConfigUpdateListener {
+        void onTetherConfigUpdated();
+    }
+
+    protected final WifiManager mWifiManager;
+    protected final String[] mWifiRegexs;
+    protected final ConnectivityManager mCm;
+    protected final OnTetherConfigUpdateListener mListener;
+
+    protected Preference mPreference;
+
+    public WifiTetherBasePreferenceController(Context context,
+            OnTetherConfigUpdateListener listener) {
+        super(context);
+        mListener = listener;
+        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mWifiRegexs = mCm.getTetherableWifiRegexs();
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mWifiManager != null && mWifiRegexs != null && mWifiRegexs.length > 0;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(getPreferenceKey());
+    }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
new file mode 100644
index 0000000..a929b86
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
@@ -0,0 +1,72 @@
+/*
+ * 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.wifi.tether;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.widget.ValidatedEditTextPreference;
+import com.android.settings.wifi.WifiUtils;
+
+public class WifiTetherPasswordPreferenceController extends WifiTetherBasePreferenceController
+        implements ValidatedEditTextPreference.Validator {
+
+    private static final String PREF_KEY = "wifi_tether_network_password";
+
+    private String mPassword;
+
+    public WifiTetherPasswordPreferenceController(Context context,
+            OnTetherConfigUpdateListener listener) {
+        super(context, listener);
+        final WifiConfiguration config = mWifiManager.getWifiApConfiguration();
+        if (config != null) {
+            mPassword = config.preSharedKey;
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREF_KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        ((ValidatedEditTextPreference) mPreference).setText(mPassword);
+        ((ValidatedEditTextPreference) mPreference).setIsPassword(true);
+        ((ValidatedEditTextPreference) mPreference).setValidator(this);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        mPassword = (String) newValue;
+        ((ValidatedEditTextPreference) mPreference).setText(mPassword);
+        mListener.onTetherConfigUpdated();
+        return true;
+    }
+
+    public String getPassword() {
+        return mPassword;
+    }
+
+    @Override
+    public boolean isTextValid(String value) {
+        return WifiUtils.isPasswordValid(value);
+    }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
new file mode 100644
index 0000000..46fb7a9
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
@@ -0,0 +1,200 @@
+/*
+ * 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.wifi.tether;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.BidiFormatter;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.PreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+import java.util.List;
+
+public class WifiTetherPreferenceController extends PreferenceController
+        implements LifecycleObserver, OnResume, OnPause {
+
+    public static final IntentFilter WIFI_TETHER_INTENT_FILTER;
+    private static final String WIFI_TETHER_SETTINGS = "wifi_tether";
+
+    private final ConnectivityManager mConnectivityManager;
+    private final String[] mWifiRegexs;
+    private final WifiManager mWifiManager;
+    private Preference mPreference;
+
+    static {
+        WIFI_TETHER_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+        WIFI_TETHER_INTENT_FILTER.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+        WIFI_TETHER_INTENT_FILTER.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+    }
+
+    public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        mConnectivityManager =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+
+        mWifiRegexs = mConnectivityManager.getTetherableWifiRegexs();
+
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mWifiRegexs != null
+                && mWifiRegexs.length != 0
+                && WifiTetherSettings.isTetherSettingPageEnabled()
+                && !Utils.isMonkeyRunning();
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(WIFI_TETHER_SETTINGS);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return WIFI_TETHER_SETTINGS;
+    }
+
+    @Override
+    public void onResume() {
+        if (mPreference != null) {
+            mContext.registerReceiver(mReceiver, WIFI_TETHER_INTENT_FILTER);
+            clearSummaryForAirplaneMode();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        if (mPreference != null) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+    }
+
+    //
+    // Everything below is copied from WifiApEnabler
+    //
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) {
+                int state = intent.getIntExtra(
+                        WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
+                int reason = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON,
+                        WifiManager.SAP_START_FAILURE_GENERAL);
+                handleWifiApStateChanged(state, reason);
+            } else if (ConnectivityManager.ACTION_TETHER_STATE_CHANGED.equals(action)) {
+                List<String> active = intent.getStringArrayListExtra(
+                        ConnectivityManager.EXTRA_ACTIVE_TETHER);
+                List<String> errored = intent.getStringArrayListExtra(
+                        ConnectivityManager.EXTRA_ERRORED_TETHER);
+                updateTetherState(active.toArray(), errored.toArray());
+            } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
+                clearSummaryForAirplaneMode();
+            }
+        }
+    };
+
+    private void handleWifiApStateChanged(int state, int reason) {
+        switch (state) {
+            case WifiManager.WIFI_AP_STATE_ENABLED:
+                /**
+                 * Summary on enable is handled by tether
+                 * broadcast notice
+                 */
+                break;
+            case WifiManager.WIFI_AP_STATE_DISABLING:
+                mPreference.setSummary(R.string.wifi_tether_stopping);
+                break;
+            case WifiManager.WIFI_AP_STATE_DISABLED:
+                mPreference.setSummary(R.string.wifi_hotspot_off_subtext);
+                clearSummaryForAirplaneMode();
+                break;
+            default:
+                if (reason == WifiManager.SAP_START_FAILURE_NO_CHANNEL) {
+                    mPreference.setSummary(R.string.wifi_sap_no_channel_error);
+                } else {
+                    mPreference.setSummary(R.string.wifi_error);
+                }
+                clearSummaryForAirplaneMode();
+        }
+    }
+
+    private void updateTetherState(Object[] tethered, Object[] errored) {
+        boolean wifiTethered = matchRegex(tethered);
+        boolean wifiErrored = matchRegex(errored);
+
+        if (wifiTethered) {
+            WifiConfiguration wifiConfig = mWifiManager.getWifiApConfiguration();
+            updateConfigSummary(wifiConfig);
+        } else if (wifiErrored) {
+            mPreference.setSummary(R.string.wifi_error);
+        } else {
+            mPreference.setSummary(R.string.wifi_hotspot_off_subtext);
+        }
+    }
+
+    private boolean matchRegex(Object[] tethers) {
+        for (Object o : tethers) {
+            String s = (String) o;
+            for (String regex : mWifiRegexs) {
+                if (s.matches(regex)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void updateConfigSummary(WifiConfiguration wifiConfig) {
+        final String s = mContext.getString(
+                com.android.internal.R.string.wifi_tether_configure_ssid_default);
+
+        mPreference.setSummary(mContext.getString(R.string.wifi_tether_enabled_subtext,
+                BidiFormatter.getInstance().unicodeWrap(
+                        (wifiConfig == null) ? s : wifiConfig.SSID)));
+    }
+
+    private void clearSummaryForAirplaneMode() {
+        boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+        if (isAirplaneMode) {
+            mPreference.setSummary(R.string.summary_placeholder);
+        }
+    }
+    //
+    // Everything above is copied from WifiApEnabler
+    //
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
new file mode 100644
index 0000000..a4c6c67
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.tether;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.EditTextPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.widget.ValidatedEditTextPreference;
+import com.android.settings.wifi.WifiUtils;
+
+public class WifiTetherSSIDPreferenceController extends WifiTetherBasePreferenceController
+        implements ValidatedEditTextPreference.Validator {
+
+    private static final String PREF_KEY = "wifi_tether_network_name";
+    @VisibleForTesting
+    static final String DEFAULT_SSID = "AndroidAP";
+
+    private String mSSID;
+
+    public WifiTetherSSIDPreferenceController(Context context,
+            OnTetherConfigUpdateListener listener) {
+        super(context, listener);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREF_KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        final WifiConfiguration config = mWifiManager.getWifiApConfiguration();
+        if (config != null) {
+            mSSID = config.SSID;
+        } else {
+            mSSID = DEFAULT_SSID;
+        }
+        ((ValidatedEditTextPreference) mPreference).setValidator(this);
+        updateSsidDisplay((EditTextPreference) mPreference);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        mSSID = (String) newValue;
+        updateSsidDisplay((EditTextPreference) preference);
+        mListener.onTetherConfigUpdated();
+        return true;
+    }
+
+    @Override
+    public boolean isTextValid(String value) {
+        return !WifiUtils.isSSIDTooLong(value) && !WifiUtils.isSSIDTooShort(value);
+    }
+
+    public String getSSID() {
+        return mSSID;
+    }
+
+    private void updateSsidDisplay(EditTextPreference preference) {
+        preference.setText(mSSID);
+        preference.setSummary(mSSID);
+    }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
new file mode 100644
index 0000000..2584ed4
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
@@ -0,0 +1,192 @@
+/*
+ * 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.wifi.tether;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settings.widget.SwitchBar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_CHANGED_ACTION;
+
+public class WifiTetherSettings extends RestrictedDashboardFragment
+        implements WifiTetherBasePreferenceController.OnTetherConfigUpdateListener {
+
+    public static boolean isTetherSettingPageEnabled() {
+        return SystemProperties.getBoolean("settings.ui.wifi.tether.enabled", true);
+    }
+
+    private static final IntentFilter TETHER_STATE_CHANGE_FILTER;
+
+    private WifiTetherSwitchBarController mSwitchBarController;
+    private WifiTetherSSIDPreferenceController mSSIDPreferenceController;
+    private WifiTetherPasswordPreferenceController mPasswordPreferenceController;
+    private WifiTetherApBandPreferenceController mApBandPreferenceController;
+
+    private WifiManager mWifiManager;
+    private boolean mRestartWifiApAfterConfigChange;
+
+    @VisibleForTesting
+    TetherChangeReceiver mTetherChangeReceiver;
+
+    static {
+        TETHER_STATE_CHANGE_FILTER = new IntentFilter(ACTION_TETHER_STATE_CHANGED);
+        TETHER_STATE_CHANGE_FILTER.addAction(WIFI_AP_STATE_CHANGED_ACTION);
+    }
+
+    public WifiTetherSettings() {
+        super(UserManager.DISALLOW_CONFIG_TETHERING);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.WIFI_TETHER_SETTINGS;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return "WifiTetherSettings";
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        mTetherChangeReceiver = new TetherChangeReceiver();
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        // Assume we are in a SettingsActivity. This is only safe because we currently use
+        // SettingsActivity as base for all preference fragments.
+        final SettingsActivity activity = (SettingsActivity) getActivity();
+        final SwitchBar switchBar = activity.getSwitchBar();
+        mSwitchBarController = new WifiTetherSwitchBarController(activity, switchBar);
+        getLifecycle().addObserver(mSwitchBarController);
+        switchBar.show();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        final Context context = getContext();
+        if (context != null) {
+            context.registerReceiver(mTetherChangeReceiver, TETHER_STATE_CHANGE_FILTER);
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        final Context context = getContext();
+        if (context != null) {
+            context.unregisterReceiver(mTetherChangeReceiver);
+        }
+    }
+
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.wifi_tether_settings;
+    }
+
+    @Override
+    protected List<PreferenceController> getPreferenceControllers(Context context) {
+        final List<PreferenceController> controllers = new ArrayList<>();
+        mSSIDPreferenceController = new WifiTetherSSIDPreferenceController(context, this);
+        mPasswordPreferenceController = new WifiTetherPasswordPreferenceController(context, this);
+        mApBandPreferenceController = new WifiTetherApBandPreferenceController(context, this);
+
+        controllers.add(mSSIDPreferenceController);
+        controllers.add(mPasswordPreferenceController);
+        controllers.add(mApBandPreferenceController);
+        return controllers;
+    }
+
+    @Override
+    public void onTetherConfigUpdated() {
+        final WifiConfiguration config = buildNewConfig();
+        /**
+         * if soft AP is stopped, bring up
+         * else restart with new config
+         * TODO: update config on a running access point when framework support is added
+         */
+        if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
+            Log.d("TetheringSettings",
+                    "Wifi AP config changed while enabled, stop and restart");
+            mRestartWifiApAfterConfigChange = true;
+            mSwitchBarController.stopTether();
+        }
+        mWifiManager.setWifiApConfiguration(config);
+    }
+
+    private WifiConfiguration buildNewConfig() {
+        final WifiConfiguration config = new WifiConfiguration();
+
+        config.SSID = mSSIDPreferenceController.getSSID();
+        config.preSharedKey = mPasswordPreferenceController.getPassword();
+        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
+        config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
+        config.apBand = mApBandPreferenceController.getBandIndex();
+        return config;
+    }
+
+    @VisibleForTesting
+    class TetherChangeReceiver extends BroadcastReceiver {
+        private static final String TAG = "TetherChangeReceiver";
+
+        @Override
+        public void onReceive(Context content, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(ACTION_TETHER_STATE_CHANGED)) {
+                if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED
+                        && mRestartWifiApAfterConfigChange) {
+                    mRestartWifiApAfterConfigChange = false;
+                    Log.d(TAG, "Restarting WifiAp due to prior config change.");
+                    mSwitchBarController.startTether();
+                }
+            } else if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) {
+                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0);
+                if (state == WifiManager.WIFI_AP_STATE_DISABLED
+                        && mRestartWifiApAfterConfigChange) {
+                    mRestartWifiApAfterConfigChange = false;
+                    Log.d(TAG, "Restarting WifiAp due to prior config change.");
+                    mSwitchBarController.startTether();
+                }
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
new file mode 100644
index 0000000..e3c6098
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
@@ -0,0 +1,138 @@
+/*
+ * 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.wifi.tether;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.widget.Switch;
+
+import com.android.settings.datausage.DataSaverBackend;
+import com.android.settings.widget.SwitchBar;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+
+public class WifiTetherSwitchBarController implements SwitchBar.OnSwitchChangeListener,
+        LifecycleObserver, OnStart, OnStop {
+
+    private final Context mContext;
+    private final SwitchBar mSwitchBar;
+    private final ConnectivityManager mConnectivityManager;
+    private final DataSaverBackend mDataSaverBackend;
+    private final NoOpOnStartTetheringCallback mOnStartTetheringCallback;
+
+    WifiTetherSwitchBarController(Context context, SwitchBar switchBar) {
+        mContext = context;
+        mSwitchBar = switchBar;
+        mDataSaverBackend = new DataSaverBackend(context);
+        mOnStartTetheringCallback = new NoOpOnStartTetheringCallback();
+        mConnectivityManager =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mSwitchBar.addOnSwitchChangeListener(this);
+    }
+
+    @Override
+    public void onStart() {
+        mContext.registerReceiver(mReceiver,
+                WifiTetherPreferenceController.WIFI_TETHER_INTENT_FILTER);
+    }
+
+    @Override
+    public void onStop() {
+        mContext.unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    public void onSwitchChanged(Switch switchView, boolean isChecked) {
+        if (isChecked) {
+            startTether();
+        } else {
+            stopTether();
+        }
+    }
+
+    void stopTether() {
+        mSwitchBar.setEnabled(false);
+        mConnectivityManager.stopTethering(TETHERING_WIFI);
+    }
+
+    void startTether() {
+        mSwitchBar.setEnabled(false);
+        mConnectivityManager.startTethering(TETHERING_WIFI, true /* showProvisioningUi */,
+                mOnStartTetheringCallback, new Handler(Looper.getMainLooper()));
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) {
+                final int state = intent.getIntExtra(
+                        WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
+                handleWifiApStateChanged(state);
+            } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
+                enableWifiSwitch();
+            }
+        }
+    };
+
+    private void handleWifiApStateChanged(int state) {
+        switch (state) {
+            case WifiManager.WIFI_AP_STATE_ENABLING:
+                mSwitchBar.setEnabled(false);
+                break;
+            case WifiManager.WIFI_AP_STATE_ENABLED:
+                if (!mSwitchBar.isChecked()) {
+                    mSwitchBar.setChecked(true);
+                }
+                enableWifiSwitch();
+                break;
+            case WifiManager.WIFI_AP_STATE_DISABLING:
+                if (mSwitchBar.isChecked()) {
+                    mSwitchBar.setChecked(false);
+                }
+                mSwitchBar.setEnabled(false);
+                break;
+            case WifiManager.WIFI_AP_STATE_DISABLED:
+                mSwitchBar.setChecked(false);
+                enableWifiSwitch();
+                break;
+            default:
+                mSwitchBar.setChecked(false);
+                enableWifiSwitch();
+                break;
+        }
+    }
+
+    private void enableWifiSwitch() {
+        boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+        if (!isAirplaneMode) {
+            mSwitchBar.setEnabled(!mDataSaverBackend.isDataSaverEnabled());
+        } else {
+            mSwitchBar.setEnabled(false);
+        }
+    }
+}
diff --git a/tests/app/src/com/android/settings/password/SetupChooseLockPasswordAppTest.java b/tests/app/src/com/android/settings/password/SetupChooseLockPasswordAppTest.java
new file mode 100644
index 0000000..78acc3e
--- /dev/null
+++ b/tests/app/src/com/android/settings/password/SetupChooseLockPasswordAppTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.password;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.settings.R;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class SetupChooseLockPasswordAppTest {
+
+    @Rule
+    public ActivityTestRule<SetupChooseLockPassword> mActivityTestRule =
+            new ActivityTestRule<>(
+                    SetupChooseLockPassword.class,
+                    true /* enable touch at launch */,
+                    false /* don't launch at every test */);
+
+    @Test
+    public void testSkipDialogIsShown() throws Throwable {
+        SetupChooseLockPassword activity = mActivityTestRule.launchActivity(null);
+
+        onView(withId(R.id.cancel_button))
+                .check(matches(withText(R.string.skip_label)))
+                .check(matches(isDisplayed()))
+                .perform(click());
+        onView(withId(android.R.id.button1)).check(matches(isDisplayed())).perform(click());
+
+        assertThat(activity.isFinishing()).named("Is finishing").isTrue();
+    }
+}
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index b9e328c..d901a11 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -13,3 +13,4 @@
 com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionMicrophone
 com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages
 com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment
+com.android.settings.wifi.tether.WifiTetherSettings
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
index 81b563f..fb06917 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
@@ -74,16 +74,16 @@
     }
 
     @Test
-    public void testCategory_isAccount() {
+    public void testCategory_isAccountDetail() {
         assertThat(new AccountDetailDashboardFragment().getCategoryKey())
-                .isEqualTo(CategoryKey.CATEGORY_ACCOUNT);
+                .isEqualTo(CategoryKey.CATEGORY_ACCOUNT_DETAIL);
     }
 
     @Test
     public void refreshDashboardTiles_HasAccountType_shouldDisplay() {
         final Tile tile = new Tile();
         final Bundle metaData = new Bundle();
-        metaData.putString(METADATA_CATEGORY, CategoryKey.CATEGORY_ACCOUNT);
+        metaData.putString(METADATA_CATEGORY, CategoryKey.CATEGORY_ACCOUNT_DETAIL);
         metaData.putString(METADATA_ACCOUNT_TYPE, "com.abc");
         tile.metaData = metaData;
 
@@ -94,7 +94,7 @@
     public void refreshDashboardTiles_NoAccountType_shouldNotDisplay() {
         final Tile tile = new Tile();
         final Bundle metaData = new Bundle();
-        metaData.putString(METADATA_CATEGORY, CategoryKey.CATEGORY_ACCOUNT);
+        metaData.putString(METADATA_CATEGORY, CategoryKey.CATEGORY_ACCOUNT_DETAIL);
         tile.metaData = metaData;
 
         assertThat(mFragment.displayTile(tile)).isFalse();
@@ -104,7 +104,7 @@
     public void refreshDashboardTiles_OtherAccountType_shouldNotDisplay() {
         final Tile tile = new Tile();
         final Bundle metaData = new Bundle();
-        metaData.putString(METADATA_CATEGORY, CategoryKey.CATEGORY_ACCOUNT);
+        metaData.putString(METADATA_CATEGORY, CategoryKey.CATEGORY_ACCOUNT_DETAIL);
         metaData.putString(METADATA_ACCOUNT_TYPE, "com.other");
         tile.metaData = metaData;
 
diff --git a/tests/robotests/src/com/android/settings/accounts/UserAndAccountDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/UserAndAccountDashboardFragmentTest.java
index d4a7a9d..8c13aab 100644
--- a/tests/robotests/src/com/android/settings/accounts/UserAndAccountDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/UserAndAccountDashboardFragmentTest.java
@@ -67,27 +67,6 @@
     }
 
     @Test
-    public void refreshDashboardTiles_HasAccountType_shouldNotDisplay() {
-        final Tile tile = new Tile();
-        final Bundle metaData = new Bundle();
-        metaData.putString(METADATA_CATEGORY, CategoryKey.CATEGORY_ACCOUNT);
-        metaData.putString(METADATA_ACCOUNT_TYPE, "com.abc");
-        tile.metaData = metaData;
-
-        assertThat(mFragment.displayTile(tile)).isFalse();
-    }
-
-    @Test
-    public void refreshDashboardTiles_NoAccountType_shouldDisplay() {
-        final Tile tile = new Tile();
-        final Bundle metaData = new Bundle();
-        metaData.putString(METADATA_CATEGORY, CategoryKey.CATEGORY_ACCOUNT);
-        tile.metaData = metaData;
-
-        assertThat(mFragment.displayTile(tile)).isTrue();
-    }
-
-    @Test
     public void updateSummary_shouldDisplaySignedInUser() {
         final Activity activity = mock(Activity.class);
         final SummaryLoader loader = mock(SummaryLoader.class);
diff --git a/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java
index bfc5cd3..2b18e05 100644
--- a/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java
@@ -68,6 +68,8 @@
     private PreferenceCategory mCategory;
     @Mock
     private Preference mSeeAllPref;
+    @Mock
+    private PreferenceCategory mDivider;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mMockContext;
     @Mock
@@ -94,6 +96,8 @@
 
         when(mScreen.findPreference(RecentAppsPreferenceController.KEY_SEE_ALL))
                 .thenReturn(mSeeAllPref);
+        when(mScreen.findPreference(RecentAppsPreferenceController.KEY_DIVIDER))
+                .thenReturn(mDivider);
         when(mCategory.getContext()).thenReturn(mContext);
     }
 
@@ -108,7 +112,8 @@
 
         mController.updateNonIndexableKeys(nonIndexable);
 
-        assertThat(nonIndexable).containsExactly(mController.getPreferenceKey());
+        assertThat(nonIndexable).containsAllOf(mController.getPreferenceKey(),
+                RecentAppsPreferenceController.KEY_DIVIDER);
     }
 
     @Test
@@ -136,6 +141,7 @@
         verify(mCategory).setTitle(null);
         verify(mSeeAllPref).setTitle(R.string.applications_settings);
         verify(mSeeAllPref).setIcon(null);
+        verify(mDivider).setVisible(false);
     }
 
     @Test
@@ -180,6 +186,7 @@
 
         verify(mSeeAllPref).setSummary(null);
         verify(mSeeAllPref).setIcon(R.drawable.ic_chevron_right_24dp);
+        verify(mDivider).setVisible(true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspectionTest.java b/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspectionTest.java
index 23e1e2e..d05bee6 100644
--- a/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspectionTest.java
+++ b/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspectionTest.java
@@ -16,10 +16,10 @@
 
 package com.android.settings.core.codeinspection;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.core.instrumentation.InstrumentableFragmentCodeInspector;
 import com.android.settings.search.SearchIndexProviderCodeInspector;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java
new file mode 100644
index 0000000..9ee53ef
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.fingerprint;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.app.KeyguardManager;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.password.SetupSkipDialog;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowEventLogWriter;
+import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowActivity;
+import org.robolectric.shadows.ShadowKeyguardManager;
+import org.robolectric.util.ActivityController;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(
+        manifest = TestConfig.MANIFEST_PATH,
+        sdk = TestConfig.SDK_VERSION,
+        shadows = {
+                ShadowEventLogWriter.class,
+                ShadowLockPatternUtils.class,
+                ShadowUserManager.class
+        })
+public class SetupFingerprintEnrollIntroductionTest {
+
+    @Mock
+    private UserInfo mUserInfo;
+
+    private ActivityController<SetupFingerprintEnrollIntroduction> mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        final Intent intent = new Intent();
+        mController = Robolectric.buildActivity(SetupFingerprintEnrollIntroduction.class, intent);
+
+        ShadowUserManager.getShadow().setUserInfo(0, mUserInfo);
+    }
+
+    @Test
+    public void testKeyguardNotSecure_shouldFinishWithSetupSkipDialogResultSkip() {
+        getShadowKeyguardManager().setIsKeyguardSecure(false);
+
+        mController.create().resume();
+
+        final Button skipButton = mController.get().findViewById(R.id.fingerprint_cancel_button);
+        assertThat(skipButton.getVisibility()).named("Skip visible").isEqualTo(View.VISIBLE);
+        skipButton.performClick();
+
+        ShadowActivity shadowActivity = Shadows.shadowOf(mController.get());
+        assertThat(mController.get().isFinishing()).named("Is finishing").isTrue();
+        assertThat(shadowActivity.getResultCode()).named("Result code")
+                .isEqualTo(SetupSkipDialog.RESULT_SKIP);
+    }
+
+    @Test
+    public void testKeyguardSecure_shouldFinishWithFingerprintResultSkip() {
+        getShadowKeyguardManager().setIsKeyguardSecure(true);
+
+        mController.create().resume();
+
+        final Button skipButton = mController.get().findViewById(R.id.fingerprint_cancel_button);
+        assertThat(skipButton.getVisibility()).named("Skip visible").isEqualTo(View.VISIBLE);
+        skipButton.performClick();
+
+        ShadowActivity shadowActivity = Shadows.shadowOf(mController.get());
+        assertThat(mController.get().isFinishing()).named("Is finishing").isTrue();
+        assertThat(shadowActivity.getResultCode()).named("Result code")
+                .isEqualTo(FingerprintEnrollBase.RESULT_SKIP);
+    }
+
+    private ShadowKeyguardManager getShadowKeyguardManager() {
+        return Shadows.shadowOf(application.getSystemService(KeyguardManager.class));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragmentTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragmentTest.java
index 8f3d2db..365a14a 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragmentTest.java
@@ -40,46 +40,77 @@
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class AnomalyDialogFragmentTest {
-    @Anomaly.AnomalyType
-    private static final int ANOMALY_TYPE = Anomaly.AnomalyType.WAKE_LOCK;
     private static final String PACKAGE_NAME = "com.android.app";
+    private static final String DISPLAY_NAME = "app";
     private static final int UID = 111;
-    private Anomaly mAnomaly;
+    private Anomaly mWakeLockAnomaly;
+    private Anomaly mWakeupAlarmAnomaly;
     private AnomalyDialogFragment mAnomalyDialogFragment;
     private Context mContext;
 
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.application;
-        mAnomaly = new Anomaly.Builder()
-                .setType(ANOMALY_TYPE)
+        mWakeLockAnomaly = new Anomaly.Builder()
+                .setType(Anomaly.AnomalyType.WAKE_LOCK)
                 .setUid(UID)
                 .setPackageName(PACKAGE_NAME)
+                .setDisplayName(DISPLAY_NAME)
                 .build();
-
-        mAnomalyDialogFragment = AnomalyDialogFragment.newInstance(mAnomaly, 0 /* metricskey */);
+        mWakeupAlarmAnomaly = new Anomaly.Builder()
+                .setType(Anomaly.AnomalyType.WAKEUP_ALARM)
+                .setUid(UID)
+                .setPackageName(PACKAGE_NAME)
+                .setDisplayName(DISPLAY_NAME)
+                .build();
     }
 
     @Test
     public void testOnCreateDialog_hasCorrectData() {
+        mAnomalyDialogFragment = AnomalyDialogFragment.newInstance(mWakeLockAnomaly,
+                0 /* metricskey */);
         FragmentTestUtil.startFragment(mAnomalyDialogFragment);
 
-        assertThat(mAnomalyDialogFragment.mAnomaly).isEqualTo(mAnomaly);
+        assertThat(mAnomalyDialogFragment.mAnomaly).isEqualTo(mWakeLockAnomaly);
     }
 
     @Test
-    public void testOnCreateDialog_hasCorrectDialog() {
+    public void testOnCreateDialog_wakelockAnomaly_fireForceStopDialog() {
+        mAnomalyDialogFragment = AnomalyDialogFragment.newInstance(mWakeLockAnomaly,
+                0 /* metricskey */);
+
         FragmentTestUtil.startFragment(mAnomalyDialogFragment);
 
         final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
         ShadowAlertDialog shadowDialog = shadowOf(dialog);
 
         assertThat(shadowDialog.getMessage()).isEqualTo(
-                mContext.getString(R.string.force_stop_dlg_text));
+                mContext.getString(R.string.dialog_stop_message, mWakeLockAnomaly.displayName));
         assertThat(shadowDialog.getTitle()).isEqualTo(
-                mContext.getString(R.string.force_stop_dlg_title));
+                mContext.getString(R.string.dialog_stop_title));
         assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText()).isEqualTo(
-                mContext.getString(R.string.dlg_ok));
+                mContext.getString(R.string.dialog_stop_ok));
+        assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo(
+                mContext.getString(R.string.dlg_cancel));
+    }
+
+    @Test
+    public void testOnCreateDialog_wakeupAlarmAnomaly_fireBackgroundCheckDialog() {
+        mAnomalyDialogFragment = AnomalyDialogFragment.newInstance(mWakeupAlarmAnomaly,
+                0 /* metricskey */);
+
+        FragmentTestUtil.startFragment(mAnomalyDialogFragment);
+
+        final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
+        ShadowAlertDialog shadowDialog = shadowOf(dialog);
+
+        assertThat(shadowDialog.getMessage()).isEqualTo(
+                mContext.getString(R.string.dialog_background_check_message,
+                        mWakeLockAnomaly.displayName));
+        assertThat(shadowDialog.getTitle()).isEqualTo(
+                mContext.getString(R.string.dialog_background_check_title));
+        assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText()).isEqualTo(
+                mContext.getString(R.string.dialog_background_check_ok));
         assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo(
                 mContext.getString(R.string.dlg_cancel));
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/BackgroundCheckActionTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/BackgroundCheckActionTest.java
index 99f7c33..ae783ab 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/BackgroundCheckActionTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/BackgroundCheckActionTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.fuelgauge.anomaly.action;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
@@ -69,4 +71,20 @@
         verify(mAppOpsManagerr).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID, PACKAGE_NAME,
                 AppOpsManager.MODE_IGNORED);
     }
+
+    @Test
+    public void testIsActionActive_modeAllowed_returnTrue() {
+        doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManagerr).checkOpNoThrow(
+                AppOpsManager.OP_RUN_IN_BACKGROUND, UID, PACKAGE_NAME);
+
+        assertThat(mBackgroundCheckAction.isActionActive(mAnomaly)).isTrue();
+    }
+
+    @Test
+    public void testIsActionActive_modeIgnored_returnFalse() {
+        doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManagerr).checkOpNoThrow(
+                AppOpsManager.OP_RUN_IN_BACKGROUND, UID, PACKAGE_NAME);
+
+        assertThat(mBackgroundCheckAction.isActionActive(mAnomaly)).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/ForceStopActionTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/ForceStopActionTest.java
index 7bb6a04..89b1a16 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/ForceStopActionTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/ForceStopActionTest.java
@@ -16,11 +16,15 @@
 
 package com.android.settings.fuelgauge.anomaly.action;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
@@ -44,15 +48,22 @@
     private Context mContext;
     @Mock
     private ActivityManager mActivityManager;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
+    @Mock
+    private PackageManager mPackageManager;
     private Anomaly mAnomaly;
     private ForceStopAction mForceStopAction;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         FakeFeatureFactory.setupForTest(mContext);
         doReturn(mActivityManager).when(mContext).getSystemService(Context.ACTIVITY_SERVICE);
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(PACKAGE_NAME,
+                PackageManager.GET_META_DATA);
 
         mAnomaly = new Anomaly.Builder()
                 .setPackageName(PACKAGE_NAME)
@@ -66,4 +77,18 @@
 
         verify(mActivityManager).forceStopPackage(PACKAGE_NAME);
     }
+
+    @Test
+    public void testIsActionActive_appStopped_returnFalse() {
+        mApplicationInfo.flags = ApplicationInfo.FLAG_STOPPED;
+
+        assertThat(mForceStopAction.isActionActive(mAnomaly)).isFalse();
+    }
+
+    @Test
+    public void testIsActionActive_appRunning_returnTrue() {
+        mApplicationInfo.flags = 0;
+
+        assertThat(mForceStopAction.isActionActive(mAnomaly)).isTrue();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetectorTest.java
index 64e3840..386e162 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetectorTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.eq;
@@ -39,6 +40,7 @@
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
+import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -96,6 +98,8 @@
     private ApplicationInfo mApplicationInfo;
     @Mock
     private AnomalyDetectionPolicy mPolicy;
+    @Mock
+    private AnomalyAction mAnomalyAction;
 
     private ArrayMap<String, BatteryStats.Uid.Wakelock> mAnomalyWakelocks;
     private ArrayMap<String, BatteryStats.Uid.Wakelock> mNormalWakelocks;
@@ -115,6 +119,7 @@
         doReturn(mPackageManager).when(mContext).getPackageManager();
         doReturn(mApplicationInfo).when(mPackageManager)
                 .getApplicationInfo(nullable(String.class), anyInt());
+        doReturn(true).when(mAnomalyAction).isActionActive(any());
 
         mAnomalySipper.uidObj = mAnomalyUid;
         mAnomalyWakelocks = new ArrayMap<>();
@@ -145,6 +150,7 @@
 
         mWakelockAnomalyDetector = spy(new WakeLockAnomalyDetector(mContext, mPolicy));
         mWakelockAnomalyDetector.mBatteryUtils = mBatteryUtils;
+        mWakelockAnomalyDetector.mAnomalyAction = mAnomalyAction;
         doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector).getTotalDurationMs(
                 eq(mAnomalyTimer), anyLong());
         doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector).getTotalDurationMs(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetectorTest.java
index e6cdc4e..21b2e54 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetectorTest.java
@@ -38,6 +38,7 @@
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
+import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -85,6 +86,8 @@
     private BatteryStats.Counter mCounter;
     @Mock
     private AnomalyDetectionPolicy mPolicy;
+    @Mock
+    private AnomalyAction mAnomalyAction;
 
     private WakeupAlarmAnomalyDetector mWakeupAlarmAnomalyDetector;
     private Context mContext;
@@ -100,6 +103,7 @@
         doReturn(false).when(mBatteryUtils).shouldHideSipper(any());
         doReturn(RUNNING_TIME_MS).when(mBatteryUtils).calculateRunningTimeBasedOnStatsType(any(),
                 anyInt());
+        doReturn(true).when(mAnomalyAction).isActionActive(any());
 
         mAnomalySipper.uidObj = mAnomalyUid;
         doReturn(ANOMALY_UID).when(mAnomalyUid).getUid();
@@ -116,6 +120,7 @@
 
         mWakeupAlarmAnomalyDetector = spy(new WakeupAlarmAnomalyDetector(mContext, mPolicy));
         mWakeupAlarmAnomalyDetector.mBatteryUtils = mBatteryUtils;
+        mWakeupAlarmAnomalyDetector.mAnomalyAction = mAnomalyAction;
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/gestures/AssistGestureSensitivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/AssistGestureSensitivityPreferenceControllerTest.java
deleted file mode 100644
index 349ca4f..0000000
--- a/tests/robotests/src/com/android/settings/gestures/AssistGestureSensitivityPreferenceControllerTest.java
+++ /dev/null
@@ -1,91 +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.gestures;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.TestConfig;
-import com.android.settings.testutils.FakeFeatureFactory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class AssistGestureSensitivityPreferenceControllerTest {
-
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private Context mContext;
-    private FakeFeatureFactory mFactory;
-    private AssistGestureSensitivityPreferenceController mController;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        FakeFeatureFactory.setupForTest(mContext);
-        mFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
-        mController = new AssistGestureSensitivityPreferenceController(mContext, null);
-    }
-
-    @Test
-    public void isAvailable_whenSupportedAndEnabled_shouldReturnTrue() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ASSIST_GESTURE_ENABLED, 1);
-        when(mFactory.assistGestureFeatureProvider.isSupported(mContext)).thenReturn(true);
-
-        assertThat(mController.isAvailable()).isTrue();
-    }
-
-    @Test
-    public void isAvailable_whenSupportedAndDisabled_shouldReturnFalse() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ASSIST_GESTURE_ENABLED, 0);
-        when(mFactory.assistGestureFeatureProvider.isSupported(mContext)).thenReturn(true);
-
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void isAvailable_whenUnsupportedAndEnabled_shouldReturnFalse() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ASSIST_GESTURE_ENABLED, 1);
-        when(mFactory.assistGestureFeatureProvider.isSupported(mContext)).thenReturn(false);
-
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void isAvailable_whenUnsupportedAndDisabled_shouldReturnFalse() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ASSIST_GESTURE_ENABLED, 0);
-        when(mFactory.assistGestureFeatureProvider.isSupported(mContext)).thenReturn(false);
-
-        assertThat(mController.isAvailable()).isFalse();
-    }
-}
-
diff --git a/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java
index ba0a90a..17fa17b 100644
--- a/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java
@@ -20,6 +20,7 @@
 import android.provider.SearchIndexableResource;
 
 import com.android.settings.R;
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.core.PreferenceController;
@@ -35,22 +36,28 @@
 import java.util.List;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class AssistGestureSettingsTest {
     @Mock
     private Context mContext;
+    private FakeFeatureFactory mFakeFeatureFactory;
+    private AssistGestureFeatureProvider mFeatureProvider;
     private AssistGestureSettings mSettings;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mFakeFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+        mFeatureProvider = mFakeFeatureFactory.getAssistGestureFeatureProvider();
         mSettings = new AssistGestureSettings();
     }
 
     @Test
     public void testGetPreferenceScreenResId() {
+        when(mFeatureProvider.getPreferenceResourceId()).thenReturn(R.xml.assist_gesture_settings);
         assertThat(mSettings.getPreferenceScreenResId())
                 .isEqualTo(R.xml.assist_gesture_settings);
     }
@@ -69,6 +76,7 @@
                         ShadowApplication.getInstance().getApplicationContext(),
                         true /* enabled */);
 
+        when(mFeatureProvider.getPreferenceResourceId()).thenReturn(R.xml.assist_gesture_settings);
         assertThat(indexRes).isNotNull();
         assertThat(indexRes.get(0).xmlResId).isEqualTo(mSettings.getPreferenceScreenResId());
     }
diff --git a/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java
index 645b986..d0a200d 100644
--- a/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java
@@ -22,6 +22,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
+import android.os.UserHandle;
 import android.os.UserManager;
 
 import com.android.settings.R;
@@ -47,6 +48,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
@@ -387,4 +389,41 @@
 
         assertThat(mLoader.loadInBackground().size()).isEqualTo(0);
     }
+
+    @Test
+    public void query_appExistsInBothProfiles() {
+        final String query = "carrot";
+        final String packageName = "carrot";
+        final int user1 = 0;
+        final int user2 = 10;
+        final int uid = 67672;
+        List<UserInfo> infos = new ArrayList<>();
+        infos.add(new UserInfo(user1, "user 1", 0));
+        infos.add(new UserInfo(user2, "user 2", UserInfo.FLAG_MANAGED_PROFILE));
+
+        when(mUserManager.getProfiles(anyInt())).thenReturn(infos);
+
+        when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), eq(user1)))
+                .thenReturn(Arrays.asList(
+                        ApplicationTestUtils.buildInfo(UserHandle.getUid(user1, uid) /* uid */,
+                                packageName, 0 /* flags */,
+                                0 /* targetSdkVersion */)));
+        when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), eq(user2)))
+                .thenReturn(Arrays.asList(
+                        ApplicationTestUtils.buildInfo(UserHandle.getUid(user2, uid) /* uid */,
+                                packageName, 0 /* flags */,
+                                0 /* targetSdkVersion */)));
+
+        mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+                mSiteMapManager);
+
+        Set<AppSearchResult> searchResults = (Set<AppSearchResult>) mLoader.loadInBackground();
+        assertThat(searchResults).hasSize(2);
+
+        Set<Integer> uidResults = searchResults.stream().map(result -> result.info.uid).collect(
+                Collectors.toSet());
+        assertThat(uidResults).containsExactly(
+                UserHandle.getUid(user1, uid),
+                UserHandle.getUid(user2, uid));
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java b/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java
index 574e4f7..cee3c78 100644
--- a/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java
+++ b/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java
@@ -17,18 +17,30 @@
 
 package com.android.settings.search;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.search.SearchResult.Builder;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,25 +55,25 @@
 import java.util.List;
 import java.util.Objects;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.verify;
-
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class IntentSearchViewHolderTest {
 
     private static final String TITLE = "title";
     private static final String SUMMARY = "summary";
+    private static final int USER_ID = 10;
+    private static final String BADGED_LABEL = "work title";
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
-    @Mock
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private SearchFragment mFragment;
+    @Mock
+    private PackageManager mPackageManager;
     private FakeFeatureFactory mFeatureFactory;
     private IntentSearchViewHolder mHolder;
     private Drawable mIcon;
+    private Drawable mBadgedIcon;
 
     @Before
     public void setUp() {
@@ -74,6 +86,8 @@
         mHolder = new IntentSearchViewHolder(view);
 
         mIcon = context.getDrawable(R.drawable.ic_search_history);
+        mBadgedIcon = context.getDrawable(R.drawable.ic_add);
+        when(mFragment.getActivity().getPackageManager()).thenReturn(mPackageManager);
     }
 
     @Test
@@ -172,6 +186,27 @@
         assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.GONE);
     }
 
+    @Test
+    public void testBindViewElements_appSearchResult() {
+        when(mPackageManager.getUserBadgedLabel(any(CharSequence.class),
+                eq(new UserHandle(USER_ID)))).thenReturn(BADGED_LABEL);
+
+        SearchResult result = getAppSearchResult(
+                TITLE, SUMMARY, mIcon, getApplicationInfo(USER_ID, TITLE, mIcon));
+        mHolder.onBind(mFragment, result);
+        mHolder.itemView.performClick();
+
+        assertThat(mHolder.titleView.getText()).isEqualTo(TITLE);
+        assertThat(mHolder.summaryView.getText()).isEqualTo(SUMMARY);
+        assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mHolder.breadcrumbView.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mHolder.titleView.getContentDescription()).isEqualTo(BADGED_LABEL);
+
+        verify(mFragment).onSearchResultClicked(eq(mHolder), any(SearchResult.class));
+        verify(mFragment.getActivity()).startActivityAsUser(
+                any(Intent.class), eq(new UserHandle(USER_ID)));
+    }
+
     private SearchResult getSearchResult(String title, String summary, Drawable icon) {
         Builder builder = new Builder();
         builder.setStableId(Objects.hash(title, summary, icon))
@@ -186,4 +221,26 @@
 
         return builder.build();
     }
+
+    private SearchResult getAppSearchResult(
+            String title, String summary, Drawable icon, ApplicationInfo applicationInfo) {
+        AppSearchResult.Builder builder = new AppSearchResult.Builder();
+        builder.setTitle(title)
+                .setSummary(summary)
+                .setRank(1)
+                .setPayload(new ResultPayload(
+                        new Intent().setComponent(new ComponentName("pkg", "class"))))
+                .addBreadcrumbs(new ArrayList<>())
+                .setIcon(icon);
+        builder.setAppInfo(applicationInfo);
+        return builder.build();
+    }
+
+    private ApplicationInfo getApplicationInfo(int userId, CharSequence appLabel, Drawable icon) {
+        ApplicationInfo applicationInfo = spy(new ApplicationInfo());
+        applicationInfo.uid = UserHandle.getUid(userId, 12345);
+        doReturn(icon).when(applicationInfo).loadIcon(any(PackageManager.class));
+        doReturn(appLabel).when(applicationInfo).loadLabel(any(PackageManager.class));
+        return applicationInfo;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
index b1419ba..7c56dc6 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.testutils.shadow;
 
+import android.app.admin.DevicePolicyManager;
+
 import com.android.internal.widget.LockPatternUtils;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
@@ -27,4 +29,9 @@
     public boolean isSecure(int id) {
         return true;
     }
+
+    @Implementation
+    public int getActivePasswordQuality(int userId) {
+        return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
index c67ad36..61346bc 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
@@ -16,11 +16,19 @@
 
 package com.android.settings.testutils.shadow;
 
+import android.annotation.UserIdInt;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.os.UserManager;
+import android.util.SparseArray;
 
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
+import org.robolectric.internal.ShadowExtractor;
+
+import java.util.Collections;
+import java.util.List;
 
 /**
  * This class provides the API 24 implementation of UserManager.get(Context).
@@ -28,8 +36,34 @@
 @Implements(UserManager.class)
 public class ShadowUserManager {
 
+    private SparseArray<UserInfo> mUserInfos = new SparseArray<>();
+
+    public void setUserInfo(int userHandle, UserInfo userInfo) {
+        mUserInfos.put(userHandle, userInfo);
+    }
+
+    @Implementation
+    public UserInfo getUserInfo(int userHandle) {
+        return mUserInfos.get(userHandle);
+    }
+
+    @Implementation
+    public List<UserInfo> getProfiles(@UserIdInt int userHandle) {
+        return Collections.emptyList();
+    }
+
+    @Implementation
+    public int getCredentialOwnerProfile(@UserIdInt int userHandle) {
+        return userHandle;
+    }
+
     @Implementation
     public static UserManager get(Context context) {
         return (UserManager) context.getSystemService(Context.USER_SERVICE);
     }
+
+    public static ShadowUserManager getShadow() {
+        return (ShadowUserManager) ShadowExtractor.extract(
+                RuntimeEnvironment.application.getSystemService(UserManager.class));
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java
new file mode 100644
index 0000000..88a5147
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.widget;
+
+
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.EditText;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ValidatedEditTextPreferenceTest {
+
+    @Mock
+    private View mView;
+    @Mock
+    private ValidatedEditTextPreference.Validator mValidator;
+
+    private ValidatedEditTextPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mPreference = new ValidatedEditTextPreference(RuntimeEnvironment.application);
+    }
+
+    @Test
+    public void bindDialogView_noTextWatcher_shouldDoNothing() {
+        mPreference.onBindDialogView(mView);
+
+        verifyZeroInteractions(mView);
+    }
+
+    @Test
+    public void bindDialogView_hasValidator_shouldBindToEditText() {
+        final EditText editText = spy(new EditText(RuntimeEnvironment.application));
+        when(mView.findViewById(android.R.id.edit)).thenReturn(editText);
+
+        mPreference.setValidator(mValidator);
+        mPreference.onBindDialogView(mView);
+
+        verify(editText).addTextChangedListener(any(TextWatcher.class));
+    }
+
+    @Test
+    public void bindDialogView_isPassword_shouldSetInputType() {
+        final EditText editText = spy(new EditText(RuntimeEnvironment.application));
+        when(mView.findViewById(android.R.id.edit)).thenReturn(editText);
+
+        mPreference.setValidator(mValidator);
+        mPreference.setIsPassword(true);
+        mPreference.onBindDialogView(mView);
+
+        assertThat(editText.getInputType()
+                & (InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_CLASS_TEXT))
+                .isNotEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiUtilsTest.java b/tests/robotests/src/com/android/settings/wifi/WifiUtilsTest.java
new file mode 100644
index 0000000..1ccdb1f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/WifiUtilsTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.wifi;
+
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WifiUtilsTest {
+
+    @Test
+    public void testSSID() {
+        assertThat(WifiUtils.isSSIDTooLong("123")).isFalse();
+        assertThat(WifiUtils.isSSIDTooLong("☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎☎")).isTrue();
+
+        assertThat(WifiUtils.isSSIDTooShort("123")).isFalse();
+        assertThat(WifiUtils.isSSIDTooShort("")).isTrue();
+    }
+
+    @Test
+    public void testPassword() {
+        final String longPassword = "123456789012345678901234567890"
+                + "1234567890123456789012345678901234567890";
+        assertThat(WifiUtils.isPasswordValid("123")).isFalse();
+        assertThat(WifiUtils.isPasswordValid("12345678")).isTrue();
+        assertThat(WifiUtils.isPasswordValid("1234567890")).isTrue();
+        assertThat(WifiUtils.isPasswordValid(longPassword)).isFalse();
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java
new file mode 100644
index 0000000..a7e00ab
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.wifi.tether;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WifiTetherApBandPreferenceControllerTest {
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener;
+    @Mock
+    private PreferenceScreen mScreen;
+
+    private WifiTetherApBandPreferenceController mController;
+    private ListPreference mListPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mListPreference = new ListPreference(RuntimeEnvironment.application);
+        when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+                .thenReturn(mConnectivityManager);
+        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
+        when(mContext.getResources()).thenReturn(RuntimeEnvironment.application.getResources());
+        when(mScreen.findPreference(anyString())).thenReturn(mListPreference);
+
+        mController = new WifiTetherApBandPreferenceController(mContext, mListener);
+    }
+
+    @Test
+    public void display_5GhzSupported_shouldDisplayFullList() {
+        when(mWifiManager.is5GHzBandSupported()).thenReturn(true);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mListPreference.getEntries().length).isEqualTo(2);
+    }
+
+    @Test
+    public void display_5GhzNotSupported_shouldDisable() {
+        when(mWifiManager.is5GHzBandSupported()).thenReturn(false);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mListPreference.getEntries()).isNull();
+        assertThat(mListPreference.isEnabled()).isFalse();
+        assertThat(mListPreference.getSummary())
+                .isEqualTo(RuntimeEnvironment.application.getString(R.string.wifi_ap_choose_2G));
+    }
+
+    @Test
+    public void changePreference_shouldUpdateValue() {
+        when(mWifiManager.is5GHzBandSupported()).thenReturn(true);
+
+        mController.displayPreference(mScreen);
+        mController.onPreferenceChange(mListPreference, "1");
+        assertThat(mController.getBandIndex()).isEqualTo(1);
+
+        mController.onPreferenceChange(mListPreference, "0");
+        assertThat(mController.getBandIndex()).isEqualTo(0);
+
+        verify(mListener, times(2)).onTetherConfigUpdated();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java
new file mode 100644
index 0000000..7ea2ea9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.wifi.tether;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.widget.ValidatedEditTextPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WifiTetherPasswordPreferenceControllerTest {
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener;
+    @Mock
+    private PreferenceScreen mScreen;
+
+    private WifiTetherPasswordPreferenceController mController;
+    private ValidatedEditTextPreference mPreference;
+    private WifiConfiguration mConfig;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mPreference = new ValidatedEditTextPreference(RuntimeEnvironment.application);
+        mConfig = new WifiConfiguration();
+        mConfig.SSID = "test_1234";
+        mConfig.preSharedKey = "test_password";
+
+        when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+        when(mWifiManager.getWifiApConfiguration()).thenReturn(mConfig);
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+                .thenReturn(mConnectivityManager);
+        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
+        when(mContext.getResources()).thenReturn(RuntimeEnvironment.application.getResources());
+        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+
+        mController = new WifiTetherPasswordPreferenceController(mContext, mListener);
+    }
+
+    @Test
+    public void displayPreference_shouldStylePreference() {
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.getText()).isEqualTo(mConfig.preSharedKey);
+        assertThat(mPreference.isPassword()).isTrue();
+    }
+
+    @Test
+    public void changePreference_shouldUpdateValue() {
+        mController.displayPreference(mScreen);
+        mController.onPreferenceChange(mPreference, "1");
+        assertThat(mController.getPassword()).isEqualTo("1");
+
+        mController.onPreferenceChange(mPreference, "0");
+        assertThat(mController.getPassword()).isEqualTo("0");
+
+        verify(mListener, times(2)).onTetherConfigUpdated();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
new file mode 100644
index 0000000..c3bc1eb
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.wifi.tether;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowSettings;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+        shadows = {
+                WifiTetherPreferenceControllerTest.ShadowWifiTetherSettings.class
+        })
+public class WifiTetherPreferenceControllerTest {
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private PreferenceScreen mScreen;
+
+    private WifiTetherPreferenceController mController;
+    private Lifecycle mLifecycle;
+    private Preference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mLifecycle = new Lifecycle();
+        mPreference = new Preference(RuntimeEnvironment.application);
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+                .thenReturn(mConnectivityManager);
+        when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+
+        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
+        mController = new WifiTetherPreferenceController(mContext, mLifecycle);
+    }
+
+    @Test
+    public void isAvailable_noTetherRegex_shouldReturnFalse() {
+        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{});
+        mController = new WifiTetherPreferenceController(mContext, mLifecycle);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_hasTetherRegex_shouldReturnTrue() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void resumeAndPause_shouldRegisterUnregisterReceiver() {
+        final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
+
+        mController.displayPreference(mScreen);
+        mLifecycle.onResume();
+        mLifecycle.onPause();
+
+        verify(mContext).registerReceiver(eq(receiver), any(IntentFilter.class));
+        verify(mContext).unregisterReceiver(receiver);
+
+    }
+
+    @Test
+    public void testReceiver_apStateChangedToDisabled_shouldUpdatePreferenceSummary() {
+        mController.displayPreference(mScreen);
+        final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
+        final Intent broadcast = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+        broadcast.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_DISABLED);
+
+        receiver.onReceive(RuntimeEnvironment.application, broadcast);
+
+        assertThat(mPreference.getSummary().toString()).isEqualTo(
+                RuntimeEnvironment.application.getString(R.string.wifi_hotspot_off_subtext));
+    }
+
+    @Test
+    public void testReceiver_apStateChangedToDisabling_shouldUpdatePreferenceSummary() {
+        mController.displayPreference(mScreen);
+        final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
+        final Intent broadcast = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+        broadcast.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_DISABLING);
+
+        receiver.onReceive(RuntimeEnvironment.application, broadcast);
+
+        assertThat(mPreference.getSummary().toString()).isEqualTo(
+                RuntimeEnvironment.application.getString(R.string.wifi_tether_stopping));
+    }
+
+    @Test
+    public void testReceiver_goingToAirplaneMode_shouldClearPreferenceSummary() {
+        final ContentResolver cr = mock(ContentResolver.class);
+        when(mContext.getContentResolver()).thenReturn(cr);
+        ShadowSettings.ShadowGlobal.putInt(cr, Settings.Global.AIRPLANE_MODE_ON, 1);
+        mController.displayPreference(mScreen);
+        final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
+        final Intent broadcast = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+
+        receiver.onReceive(RuntimeEnvironment.application, broadcast);
+
+        assertThat(mPreference.getSummary().toString()).isEqualTo(
+                RuntimeEnvironment.application.getString(R.string.summary_placeholder));
+    }
+
+    @Test
+    public void testReceiver_tetherEnabled_shouldUpdatePreferenceSummary() {
+        mController.displayPreference(mScreen);
+        final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
+        final Intent broadcast = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+        final ArrayList<String> activeTethers = new ArrayList<>();
+        activeTethers.add("1");
+        broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, activeTethers);
+        broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
+                new ArrayList<>());
+        final WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = "test-ap";
+        when(mWifiManager.getWifiApConfiguration()).thenReturn(configuration);
+
+        receiver.onReceive(RuntimeEnvironment.application, broadcast);
+
+        verify(mContext).getString(eq(R.string.wifi_tether_enabled_subtext), any());
+    }
+
+    @Implements(WifiTetherSettings.class)
+    public static final class ShadowWifiTetherSettings {
+
+        @Implementation
+        public static boolean isTetherSettingPageEnabled() {
+            return true;
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
new file mode 100644
index 0000000..f43e3a7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.wifi.tether;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.widget.ValidatedEditTextPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WifiTetherSSIDPreferenceControllerTest {
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener;
+    @Mock
+    private PreferenceScreen mScreen;
+
+    private WifiTetherSSIDPreferenceController mController;
+    private ValidatedEditTextPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mPreference = new ValidatedEditTextPreference(RuntimeEnvironment.application);
+
+        when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+                .thenReturn(mConnectivityManager);
+        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
+        when(mContext.getResources()).thenReturn(RuntimeEnvironment.application.getResources());
+        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+
+        mController = new WifiTetherSSIDPreferenceController(mContext, mListener);
+    }
+
+    @Test
+    public void displayPreference_noWifiConfig_shouldDisplayDefaultSSID() {
+        when(mWifiManager.getWifiApConfiguration()).thenReturn(null);
+
+        mController.displayPreference(mScreen);
+        assertThat(mController.getSSID())
+                .isEqualTo(WifiTetherSSIDPreferenceController.DEFAULT_SSID);
+    }
+
+    @Test
+    public void displayPreference_hasCustomWifiConfig_shouldDisplayCustomSSID() {
+        final WifiConfiguration config = new WifiConfiguration();
+        config.SSID = "test_1234";
+        when(mWifiManager.getWifiApConfiguration()).thenReturn(config);
+
+        mController.displayPreference(mScreen);
+        assertThat(mController.getSSID()).isEqualTo(config.SSID);
+    }
+
+    @Test
+    public void changePreference_shouldUpdateValue() {
+        mController.displayPreference(mScreen);
+        mController.onPreferenceChange(mPreference, "1");
+        assertThat(mController.getSSID()).isEqualTo("1");
+
+        mController.onPreferenceChange(mPreference, "0");
+        assertThat(mController.getSSID()).isEqualTo("0");
+
+        verify(mListener, times(2)).onTetherConfigUpdated();
+    }
+}
diff --git a/tests/unit/Android.mk b/tests/unit/Android.mk
index f9c0489..060c3e1 100644
--- a/tests/unit/Android.mk
+++ b/tests/unit/Android.mk
@@ -9,10 +9,12 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
-    mockito-target-minus-junit4 \
     espresso-core \
+    legacy-android-test \
+    mockito-target-minus-junit4 \
     truth-prebuilt \
-    legacy-android-test
+    ub-uiautomator \
+
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/unit/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java b/tests/unit/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java
deleted file mode 100644
index 8afed18..0000000
--- a/tests/unit/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java
+++ /dev/null
@@ -1,109 +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.fingerprint;
-
-
-import static org.mockito.Mockito.doReturn;
-
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.test.ActivityUnitTestCase;
-import android.view.View;
-import android.widget.Button;
-
-import com.android.settings.R;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-public class SetupFingerprintEnrollIntroductionTest
-        extends ActivityUnitTestCase<SetupFingerprintEnrollIntroduction> {
-
-    private TestContext mContext;
-
-    @Mock
-    private KeyguardManager mKeyguardManager;
-
-    private SetupFingerprintEnrollIntroduction mActivity;
-
-    public SetupFingerprintEnrollIntroductionTest() {
-        super(SetupFingerprintEnrollIntroduction.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        MockitoAnnotations.initMocks(this);
-        mContext = new TestContext(getInstrumentation().getTargetContext());
-        setActivityContext(mContext);
-
-        getInstrumentation().runOnMainSync(() -> {
-            final Intent intent = new Intent();
-            mActivity = startActivity(intent,
-                    null /* savedInstanceState */, null /* lastNonConfigurationInstance */);
-        });
-    }
-
-    public void testKeyguardNotSecure_shouldShowSkipDialog() {
-        doReturn(false).when(mKeyguardManager).isKeyguardSecure();
-
-        getInstrumentation().runOnMainSync(() -> {
-            getInstrumentation().callActivityOnCreate(mActivity, null);
-            getInstrumentation().callActivityOnResume(mActivity);
-
-            final Button skipButton =
-                    (Button) mActivity.findViewById(R.id.fingerprint_cancel_button);
-            assertEquals(View.VISIBLE, skipButton.getVisibility());
-            skipButton.performClick();
-        });
-
-        assertFalse(isFinishCalled());
-    }
-
-    public void testKeyguardSecure_shouldNotShowSkipDialog() {
-        doReturn(true).when(mKeyguardManager).isKeyguardSecure();
-
-        getInstrumentation().runOnMainSync(() -> {
-            getInstrumentation().callActivityOnCreate(mActivity, null);
-            getInstrumentation().callActivityOnResume(mActivity);
-
-            final Button skipButton =
-                    (Button) mActivity.findViewById(R.id.fingerprint_cancel_button);
-            assertEquals(View.VISIBLE, skipButton.getVisibility());
-            skipButton.performClick();
-        });
-
-        assertTrue(isFinishCalled());
-    }
-
-    public class TestContext extends ContextWrapper {
-
-        public TestContext(Context base) {
-            super(base);
-        }
-
-        @Override
-        public Object getSystemService(String name) {
-            if (Context.KEYGUARD_SERVICE.equals(name)) {
-                return mKeyguardManager;
-            }
-            return super.getSystemService(name);
-        }
-    }
-}
diff --git a/tests/unit/src/com/android/settings/wifi/WifiSettingsUiTest.java b/tests/unit/src/com/android/settings/wifi/WifiSettingsUiTest.java
index c418d45..640c884 100644
--- a/tests/unit/src/com/android/settings/wifi/WifiSettingsUiTest.java
+++ b/tests/unit/src/com/android/settings/wifi/WifiSettingsUiTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.settings.wifi;
 
+import android.app.Activity;
 import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
@@ -45,6 +46,7 @@
 
 import java.util.List;
 
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
@@ -52,10 +54,14 @@
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.startsWith;
+
+import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -217,4 +223,23 @@
 
         onView(withText(CONNECTED)).check(matches(isDisplayed()));
     }
+
+    @Test
+    public void resumingAp_shouldNotForceUpdateWhenExistingAPsAreListed() {
+        setWifiState(WifiManager.WIFI_STATE_ENABLED);
+        setupConnectedAccessPoint();
+        when(mWifiTracker.isConnected()).thenReturn(true);
+
+        launchActivity();
+
+        onView(withText(CONNECTED)).check(matches(isDisplayed()));
+        verify(mWifiTracker).forceUpdate();
+
+        Activity activity = mActivityRule.getActivity();
+        activity.finish();
+        getInstrumentation().waitForIdleSync();
+
+        getInstrumentation().callActivityOnStart(activity);
+        verify(mWifiTracker, atMost(1)).forceUpdate();
+    }
 }
diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
new file mode 100644
index 0000000..26a711b
--- /dev/null
+++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.wifi.tether;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+import com.android.settings.Settings;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WifiTetherSettingsTest {
+
+    private static final long TIMEOUT = 2000L;
+
+    private Instrumentation mInstrumentation;
+    private Intent mTetherActivityIntent;
+    private UiDevice mDevice;
+
+    @Before
+    public void setUp() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mDevice = UiDevice.getInstance(mInstrumentation);
+        mTetherActivityIntent = new Intent()
+                .setClassName(mInstrumentation.getTargetContext().getPackageName(),
+                        Settings.TetherSettingsActivity.class.getName())
+                .setPackage(mInstrumentation.getTargetContext().getPackageName());
+    }
+
+    @After
+    public void tearDown() {
+        mDevice.pressHome();
+    }
+
+    @Test
+    public void launchTetherSettings_shouldHaveAllFields() {
+        launchWifiTetherActivity();
+        onView(withText("Network name")).check(matches(isDisplayed()));
+        onView(withText("Password")).check(matches(isDisplayed()));
+        onView(withText("Select AP Band")).check(matches(isDisplayed()));
+    }
+
+    private void launchWifiTetherActivity() {
+        mInstrumentation.startActivitySync(mTetherActivityIntent);
+        onView(withText("Portable Wi‑Fi hotspot")).perform();
+        UiObject2 item = mDevice.wait(Until.findObject(By.text("Portable Wi‑Fi hotspot")), TIMEOUT);
+        item.click();
+    }
+}