Merge "Update history flow" into rvc-dev
diff --git a/res/drawable-nodpi/nfc_detection_point.png b/res/drawable-nodpi/nfc_detection_point.png
new file mode 100644
index 0000000..1914264
--- /dev/null
+++ b/res/drawable-nodpi/nfc_detection_point.png
Binary files differ
diff --git a/res/layout/nfc_detection_point.xml b/res/layout/nfc_detection_point.xml
new file mode 100644
index 0000000..8f938d1
--- /dev/null
+++ b/res/layout/nfc_detection_point.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/nfc_detection_point_height"
+        android:scaleType="centerCrop"
+        android:cropToPadding="true"
+        android:src="@drawable/nfc_detection_point"
+        android:contentDescription="@null" />
+
+</FrameLayout>
diff --git a/res/values/config.xml b/res/values/config.xml
index 64d9ab7..5bddc5e 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -442,4 +442,7 @@
 
     <!-- Package name of dialer supports RTT setting-->
     <string name="config_rtt_setting_package_name" translatable="false"></string>
+
+    <!-- Whether nfc detection point preview image is available or not. -->
+    <bool name="config_nfc_detection_point">false</bool>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index aadd62d..c2539fa 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -67,6 +67,8 @@
     <!-- Weight of the right pane in a multi-pane preference layout. So the split is 40:60 -->
     <integer name="preferences_right_pane_weight">6</integer>
 
+    <dimen name="nfc_detection_point_height">300dp</dimen>
+
     <dimen name="notification_app_icon_size">64dp</dimen>
     <dimen name="notification_app_icon_badge_size">20dp</dimen>
     <dimen name="notification_app_icon_badge_margin">4dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3c4f304..8a642ac 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3817,9 +3817,9 @@
     <string name="bluetooth_untether_blank"><xliff:g id="device_name">%1$s</xliff:g> will be untethered.</string>
 
     <!-- Ethernet Tethering settings-->
-    <!-- Label for ethernet tether checkbox [CHAR LIMIT=25]-->
+    <!-- Label for ethernet tether checkbox [CHAR LIMIT=NONE]-->
     <string name="ethernet_tether_checkbox_text">Ethernet tethering</string>
-    <!-- Ethernet Tethering subtext [CHAR LIMIT=70]-->
+    <!-- Ethernet Tethering subtext [CHAR LIMIT=NONE]-->
     <string name="ethernet_tethering_subtext" product="default">Share phone\u2019s internet connection via USB Ethernet</string>
 
     <!-- Tethering footer info [CHAR LIMIT=NONE]-->
@@ -7185,6 +7185,8 @@
     <string name="help_url_autoclick" translatable="false"></string>
     <!-- Help URL, Accessibility caption preferences [DO NOT TRANSLATE] -->
     <string name="help_url_caption" translatable="false"></string>
+    <!-- Help URL, Accessibility Timeout [DO NOT TRANSLATE] -->
+    <string name="help_url_timeout" translatable="false"></string>
     <string name="help_url_system_dashboard" translatable="false"></string>
     <string name="help_url_double_tap_screen" translatable="false"></string>
     <string name="help_url_account_detail" translatable="false"></string>
@@ -11843,6 +11845,8 @@
     <string name="dsu_loader_description" translatable="false">Load a Dynamic System Update Image</string>
     <!-- DSU Loader Loading. Do not translate. -->
     <string name="dsu_loader_loading" translatable="false">Loading...</string>
+    <!-- DSU Loader. Do not translate. -->
+    <string name="dsu_is_running" translatable="false">DSU is running</string>
 
     <!-- Name of dev option called "Bug report handler" [CHAR LIMIT=NONE] -->
     <string name="bug_report_handler_title">Bug report handler</string>
diff --git a/res/xml/nfc_and_payment_settings.xml b/res/xml/nfc_and_payment_settings.xml
index f240388..8ff4983 100644
--- a/res/xml/nfc_and_payment_settings.xml
+++ b/res/xml/nfc_and_payment_settings.xml
@@ -19,6 +19,12 @@
     xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:title="@string/nfc_quick_toggle_title">
 
+    <com.android.settingslib.widget.LayoutPreference
+        android:key="nfc_detection_point"
+        android:selectable="false"
+        android:layout="@layout/nfc_detection_point"
+        settings:controller="com.android.settings.nfc.NfcDetectionPointController"/>
+
     <SwitchPreference
         android:key="toggle_nfc"
         android:title="@string/nfc_quick_toggle_title"
@@ -37,4 +43,4 @@
         android:fragment="com.android.settings.nfc.PaymentSettings"
         settings:keywords="@string/keywords_default_payment_app"
         settings:controller="com.android.settings.applications.specialaccess.DefaultPaymentSettingsPreferenceController"/>
-</PreferenceScreen>
\ No newline at end of file
+</PreferenceScreen>
diff --git a/src/com/android/settings/accessibility/AccessibilityControlTimeoutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityControlTimeoutPreferenceFragment.java
index b37fdfb..0550908 100644
--- a/src/com/android/settings/accessibility/AccessibilityControlTimeoutPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityControlTimeoutPreferenceFragment.java
@@ -19,7 +19,6 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.res.Resources;
-import android.provider.SearchIndexableResource;
 
 import androidx.preference.Preference;
 
@@ -31,7 +30,6 @@
 import com.android.settingslib.search.SearchIndexable;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 @SearchIndexable
@@ -88,6 +86,11 @@
         return buildPreferenceControllers(context, getSettingsLifecycle());
     }
 
+    @Override
+    public int getHelpResource() {
+        return R.string.help_url_timeout;
+    }
+
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
             Lifecycle lifecycle) {
         if (sControllers.size() == 0) {
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index c55c9a2..9bf4cf8 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -83,6 +83,8 @@
     protected CharSequence mPackageName;
     protected Uri mImageUri;
     protected CharSequence mHtmlDescription;
+    // Used to restore the edit dialog status.
+    protected int mUserShortcutTypeCache = UserShortcutType.EMPTY;
     private static final String DRAWABLE_FOLDER = "drawable";
     protected static final String KEY_USE_SERVICE_PREFERENCE = "use_service";
     protected static final String KEY_GENERAL_CATEGORY = "general_categories";
@@ -91,8 +93,6 @@
     private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
     private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
     private int mUserShortcutType = UserShortcutType.EMPTY;
-    // Used to restore the edit dialog status.
-    private int mUserShortcutTypeCache = UserShortcutType.EMPTY;
     private CheckBox mSoftwareTypeCheckBox;
     private CheckBox mHardwareTypeCheckBox;
 
@@ -473,8 +473,10 @@
     }
 
     private void updateAlertDialogCheckState() {
-        updateCheckStatus(mSoftwareTypeCheckBox, UserShortcutType.SOFTWARE);
-        updateCheckStatus(mHardwareTypeCheckBox, UserShortcutType.HARDWARE);
+        if (mUserShortcutTypeCache != UserShortcutType.EMPTY) {
+            updateCheckStatus(mSoftwareTypeCheckBox, UserShortcutType.SOFTWARE);
+            updateCheckStatus(mHardwareTypeCheckBox, UserShortcutType.HARDWARE);
+        }
     }
 
     private void updateCheckStatus(CheckBox checkBox, @UserShortcutType int type) {
@@ -658,7 +660,10 @@
 
     @Override
     public void onSettingsClicked(ShortcutPreference preference) {
-        mUserShortcutTypeCache = getUserShortcutType(getPrefContext(), UserShortcutType.SOFTWARE);
+        // Do not restore shortcut in shortcut chooser dialog when shortcutPreference is turned off.
+        mUserShortcutTypeCache = mShortcutPreference.isChecked()
+                ? getUserShortcutType(getPrefContext(), UserShortcutType.SOFTWARE)
+                : UserShortcutType.EMPTY;
     }
 
     private void createFooterPreference(CharSequence title) {
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index 5573746..a9667bb 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -71,8 +71,6 @@
     private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
     private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
     private int mUserShortcutType = UserShortcutType.EMPTY;
-    // Used to restore the edit dialog status.
-    private int mUserShortcutTypeCache = UserShortcutType.EMPTY;
     private CheckBox mSoftwareTypeCheckBox;
     private CheckBox mHardwareTypeCheckBox;
     private CheckBox mTripleTapTypeCheckBox;
@@ -283,9 +281,11 @@
     }
 
     private void updateAlertDialogCheckState() {
-        updateCheckStatus(mSoftwareTypeCheckBox, UserShortcutType.SOFTWARE);
-        updateCheckStatus(mHardwareTypeCheckBox, UserShortcutType.HARDWARE);
-        updateCheckStatus(mTripleTapTypeCheckBox, UserShortcutType.TRIPLETAP);
+        if (mUserShortcutTypeCache != UserShortcutType.EMPTY) {
+            updateCheckStatus(mSoftwareTypeCheckBox, UserShortcutType.SOFTWARE);
+            updateCheckStatus(mHardwareTypeCheckBox, UserShortcutType.HARDWARE);
+            updateCheckStatus(mTripleTapTypeCheckBox, UserShortcutType.TRIPLETAP);
+        }
     }
 
     private void updateCheckStatus(CheckBox checkBox, @UserShortcutType int type) {
@@ -457,7 +457,10 @@
 
     @Override
     public void onSettingsClicked(ShortcutPreference preference) {
-        mUserShortcutTypeCache = getUserShortcutType(getPrefContext(), UserShortcutType.SOFTWARE);
+        // Do not restore shortcut in shortcut chooser dialog when shortcutPreference is turned off.
+        mUserShortcutTypeCache = mShortcutPreference.isChecked()
+                ? getUserShortcutType(getPrefContext(), UserShortcutType.SOFTWARE)
+                : UserShortcutType.EMPTY;
         showDialog(DialogEnums.MAGNIFICATION_EDIT_SHORTCUT);
     }
 
diff --git a/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java b/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java
index 12393ad..cf0be20 100644
--- a/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java
@@ -19,6 +19,7 @@
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.icu.text.ListFormatter;
 import android.util.Log;
@@ -28,15 +29,24 @@
 
 import com.android.settings.R;
 import com.android.settingslib.applications.PermissionsSummaryHelper;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
 
 import java.util.ArrayList;
 import java.util.List;
 
-public class AppPermissionPreferenceController extends AppInfoPreferenceControllerBase {
+/**
+ * A PreferenceController handling the logic for permissions of apps.
+ */
+public class AppPermissionPreferenceController extends AppInfoPreferenceControllerBase implements
+        LifecycleObserver, OnStart, OnStop {
 
     private static final String TAG = "PermissionPrefControl";
     private static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
 
+    private final PackageManager mPackageManager;
+
     private String mPackageName;
 
     @VisibleForTesting
@@ -73,8 +83,22 @@
         }
     };
 
+    private final PackageManager.OnPermissionsChangedListener mOnPermissionsChangedListener =
+            uid -> updateState(mPreference);
+
     public AppPermissionPreferenceController(Context context, String key) {
         super(context, key);
+        mPackageManager = context.getPackageManager();
+    }
+
+    @Override
+    public void onStart() {
+        mPackageManager.addOnPermissionsChangeListener(mOnPermissionsChangedListener);
+    }
+
+    @Override
+    public void onStop() {
+        mPackageManager.removeOnPermissionsChangeListener(mOnPermissionsChangedListener);
     }
 
     @Override
diff --git a/src/com/android/settings/development/DSULoader.java b/src/com/android/settings/development/DSULoader.java
index 36ce785..4727369 100644
--- a/src/com/android/settings/development/DSULoader.java
+++ b/src/com/android/settings/development/DSULoader.java
@@ -22,6 +22,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -40,8 +41,11 @@
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 
 import javax.net.ssl.HttpsURLConnection;
@@ -62,6 +66,7 @@
     private static final String PROPERTY_KEY_OS = "ro.system.build.version.release";
     private static final String PROPERTY_KEY_VNDK = "ro.vndk.version";
     private static final String PROPERTY_KEY_LIST = "ro.vendor.dsu.list";
+    private static final String PROPERTY_KEY_SPL = "ro.build.version.security_patch";
     private static final String DSU_LIST =
             "https://dl.google.com/developers/android/gsi/gsi-src.json";
 
@@ -121,7 +126,8 @@
             mDsuList = dsuList;
         }
 
-        private void fetch(URL url) throws IOException, JSONException, MalformedURLException {
+        private void fetch(URL url)
+                throws IOException, JSONException, MalformedURLException, ParseException {
             String content = readAll(url);
             JSONObject jsn = new JSONObject(content);
             // The include primitive is like below
@@ -195,6 +201,8 @@
         private static final String OS_VERSION = "os_version";
         private static final String VNDK = "vndk";
         private static final String PUBKEY = "pubkey";
+        private static final String SPL = "spl";
+        private static final String SPL_FORMAT = "yyyy-MM-dd";
         private static final String TOS = "tos";
 
         String mName = null;
@@ -203,10 +211,11 @@
         int mOsVersion = -1;
         int[] mVndk = null;
         String mPubKey = "";
+        Date mSPL = null;
         URL mTosUrl = null;
         URL mUri;
 
-        DSUPackage(JSONObject jsn) throws JSONException, MalformedURLException {
+        DSUPackage(JSONObject jsn) throws JSONException, MalformedURLException, ParseException {
             Slog.i(TAG, "DSUPackage: " + jsn.toString());
             mName = jsn.getString(NAME);
             mDetails = jsn.getString(DETAILS);
@@ -228,6 +237,9 @@
             if (jsn.has(TOS)) {
                 mTosUrl = new URL(jsn.getString(TOS));
             }
+            if (jsn.has(SPL)) {
+                mSPL = new SimpleDateFormat(SPL_FORMAT).parse(jsn.getString(SPL));
+            }
         }
 
         int dessertNumber(String s, int base) {
@@ -265,6 +277,18 @@
             return cpu;
         }
 
+        Date getDeviceSPL() {
+            String spl = SystemProperties.get(PROPERTY_KEY_SPL);
+            if (TextUtils.isEmpty(spl)) {
+                return null;
+            }
+            try {
+                return new SimpleDateFormat(SPL_FORMAT).parse(spl);
+            } catch (ParseException e) {
+                return null;
+            }
+        }
+
         boolean isSupported() {
             boolean supported = true;
             String cpu = getDeviceCpu();
@@ -301,6 +325,16 @@
                     }
                 }
             }
+            if (mSPL != null) {
+                Date spl = getDeviceSPL();
+                if (spl == null) {
+                    Slog.i(TAG, "Failed to getDeviceSPL");
+                    supported = false;
+                } else if (spl.getTime() > mSPL.getTime()) {
+                    Slog.i(TAG, "Device SPL:" + spl.toString() + " > " + mSPL.toString());
+                    supported = false;
+                }
+            }
             Slog.i(TAG, mName + " isSupported " + supported);
             return supported;
         }
diff --git a/src/com/android/settings/development/SelectDSUPreferenceController.java b/src/com/android/settings/development/SelectDSUPreferenceController.java
index 72e8447..6498ae7 100644
--- a/src/com/android/settings/development/SelectDSUPreferenceController.java
+++ b/src/com/android/settings/development/SelectDSUPreferenceController.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.os.SystemProperties;
 
 import androidx.preference.Preference;
 
@@ -37,9 +38,16 @@
         return DSU_LOADER_KEY;
     }
 
+    private boolean isDSURunning() {
+        return SystemProperties.getBoolean("ro.gsid.image_running", false);
+    }
+
     @Override
     public boolean handlePreferenceTreeClick(Preference preference) {
         if (DSU_LOADER_KEY.equals(preference.getKey())) {
+            if (isDSURunning()) {
+                return true;
+            }
             final Intent intent = new Intent(mContext, DSULoader.class);
             mContext.startActivity(intent);
             return true;
@@ -49,6 +57,7 @@
 
     @Override
     public void updateState(Preference preference) {
-        preference.setSummary(mContext.getResources().getString(R.string.dsu_loader_description));
+        int key = isDSURunning() ? R.string.dsu_is_running : R.string.dsu_loader_description;
+        preference.setSummary(mContext.getResources().getString(key));
     }
 }
diff --git a/src/com/android/settings/media/MediaOutputSlice.java b/src/com/android/settings/media/MediaOutputSlice.java
index 9dbf948..27960e1 100644
--- a/src/com/android/settings/media/MediaOutputSlice.java
+++ b/src/com/android/settings/media/MediaOutputSlice.java
@@ -197,6 +197,8 @@
                 }
                 listBuilder.addInputRange(builder);
             } else {
+                Log.d(TAG, "addRow device = " + device.getName() + " MaxVolume = "
+                        + device.getMaxVolume());
                 final ListBuilder.RowBuilder builder = getMediaDeviceRow(device);
                 // Check end item visibility
                 if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE
@@ -266,16 +268,24 @@
 
         if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
                 && !device.isConnected()) {
-            // Append status to title only for the disconnected Bluetooth device.
-            final SpannableString spannableTitle = new SpannableString(
-                    mContext.getString(R.string.media_output_disconnected_status, deviceName));
-            spannableTitle.setSpan(new ForegroundColorSpan(
-                    Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorSecondary)),
-                    deviceName.length(),
-                    spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
-            rowBuilder.setTitle(spannableTitle);
-            rowBuilder.setPrimaryAction(SliceAction.create(broadcastAction, deviceIcon,
-                    ListBuilder.ICON_IMAGE, spannableTitle));
+            if (device.getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
+                rowBuilder.setTitle(deviceName);
+                rowBuilder.setPrimaryAction(SliceAction.create(broadcastAction, deviceIcon,
+                        ListBuilder.ICON_IMAGE, deviceName));
+                rowBuilder.setSubtitle(mContext.getText(R.string.media_output_switching));
+            } else {
+                // Append status to title only for the disconnected Bluetooth device.
+                final SpannableString spannableTitle = new SpannableString(
+                        mContext.getString(R.string.media_output_disconnected_status, deviceName));
+                spannableTitle.setSpan(new ForegroundColorSpan(
+                                Utils.getColorAttrDefaultColor(mContext,
+                                        android.R.attr.textColorSecondary)),
+                        deviceName.length(),
+                        spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
+                rowBuilder.setTitle(spannableTitle);
+                rowBuilder.setPrimaryAction(SliceAction.create(broadcastAction, deviceIcon,
+                        ListBuilder.ICON_IMAGE, spannableTitle));
+            }
         } else {
             rowBuilder.setTitle(deviceName);
             rowBuilder.setPrimaryAction(SliceAction.create(broadcastAction, deviceIcon,
diff --git a/src/com/android/settings/nfc/NfcDetectionPointController.java b/src/com/android/settings/nfc/NfcDetectionPointController.java
new file mode 100644
index 0000000..b2e8f6a
--- /dev/null
+++ b/src/com/android/settings/nfc/NfcDetectionPointController.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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.nfc;
+
+import android.content.Context;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+/**
+ * Controller that used to show nfc detection point guidance
+ */
+public class NfcDetectionPointController extends BasePreferenceController {
+    private boolean mEnabled;
+
+    public NfcDetectionPointController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mEnabled = mContext.getResources().getBoolean(R.bool.config_nfc_detection_point);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        if (!mEnabled) {
+            return UNSUPPORTED_ON_DEVICE;
+        }
+        return AVAILABLE;
+    }
+
+    @VisibleForTesting
+    public void setConfig(boolean value) {
+        mEnabled = value;
+    }
+}
diff --git a/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
index 6ad4c94..1b54e1a 100644
--- a/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
+++ b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
@@ -235,6 +235,13 @@
     public void refreshPreferences() {
         final PreferenceScreen screen = getPreferenceScreen();
         for (AbstractPreferenceController controller : mControllers) {
+            // WifiDetailPreferenceController2 gets the callback WifiEntryCallback#onUpdated,
+            // it can control the visibility change by itself.
+            // And WifiDetailPreferenceController2#updatePreference renew mEntityHeaderController
+            // instance which will cause icon reset.
+            if (controller instanceof WifiDetailPreferenceController2) {
+                continue;
+            }
             controller.displayPreference(screen);
         }
     }
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java
index 022de97..f00471d 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
@@ -43,6 +44,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 
@@ -57,6 +59,8 @@
     private PreferenceScreen mScreen;
     @Mock
     private Preference mPreference;
+    @Mock
+    private PackageManager mPackageManager;
 
     private Context mContext;
     private AppPermissionPreferenceController mController;
@@ -68,6 +72,7 @@
         mController = new AppPermissionPreferenceController(mContext, "permission_settings");
         mController.setPackageName("package1");
         mController.setParentFragment(mFragment);
+        ReflectionHelpers.setField(mController, "mPackageManager", mPackageManager);
 
         when(mScreen.findPreference(any())).thenReturn(mPreference);
         final String key = mController.getPreferenceKey();
@@ -76,9 +81,25 @@
     }
 
     @Test
+    public void onStart_shouldAddPermissionsChangeListener() {
+        mController.onStart();
+
+        verify(mPackageManager).addOnPermissionsChangeListener(
+                any(PackageManager.OnPermissionsChangedListener.class));
+    }
+
+    @Test
+    public void onStop_shouldRemovePermissionsChangeListener() {
+        mController.onStop();
+
+        verify(mPackageManager).removeOnPermissionsChangeListener(
+                any(PackageManager.OnPermissionsChangedListener.class));
+    }
+
+    @Test
     public void getAvailabilityStatus_isAlwaysAvailable() {
         assertThat(mController.getAvailabilityStatus())
-            .isEqualTo(AppPermissionPreferenceController.AVAILABLE);
+                .isEqualTo(AppPermissionPreferenceController.AVAILABLE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
index 305d1ab..d4590b5 100644
--- a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
+++ b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
@@ -474,6 +474,43 @@
     }
 
     @Test
+    public void getSlice_disconnectedBtOnTransferring_containTransferringSubtitle() {
+        final List<MediaDevice> mSelectedDevices = new ArrayList<>();
+        final List<MediaDevice> mSelectableDevices = new ArrayList<>();
+        mDevices.clear();
+        final MediaDevice device = mock(MediaDevice.class);
+        when(device.getName()).thenReturn(TEST_DEVICE_1_NAME);
+        when(device.getIcon()).thenReturn(mTestDrawable);
+        when(device.getMaxVolume()).thenReturn(100);
+        when(device.isConnected()).thenReturn(true);
+        when(device.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
+        when(device.getId()).thenReturn(TEST_DEVICE_1_ID);
+        final MediaDevice device2 = mock(MediaDevice.class);
+        when(device2.getName()).thenReturn(TEST_DEVICE_2_NAME);
+        when(device2.getIcon()).thenReturn(mTestDrawable);
+        when(device2.getMaxVolume()).thenReturn(100);
+        when(device2.isConnected()).thenReturn(false);
+        when(device2.getState()).thenReturn(LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+        when(device2.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
+        when(device2.getId()).thenReturn(TEST_DEVICE_2_ID);
+        mSelectedDevices.add(device);
+        mSelectableDevices.add(device2);
+        when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(device);
+        mDevices.add(device);
+        mDevices.add(device2);
+        when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mSelectedDevices);
+        when(mLocalMediaManager.getSelectableMediaDevice()).thenReturn(mSelectableDevices);
+        mMediaDeviceUpdateWorker.onDeviceListUpdate(mDevices);
+
+        final Slice mediaSlice = mMediaOutputSlice.getSlice();
+        final String sliceInfo = SliceQuery.findAll(mediaSlice, FORMAT_SLICE, HINT_LIST_ITEM,
+                null).toString();
+
+        assertThat(TextUtils.indexOf(sliceInfo, mContext.getText(R.string.media_output_switching)))
+                .isNotEqualTo(-1);
+    }
+
+    @Test
     public void onNotifyChange_foundMediaDevice_connect() {
         mDevices.clear();
         final MediaDevice device = mock(MediaDevice.class);
diff --git a/tests/robotests/src/com/android/settings/nfc/NfcDetectionPointControllerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcDetectionPointControllerTest.java
new file mode 100644
index 0000000..31ac7d6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/nfc/NfcDetectionPointControllerTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class NfcDetectionPointControllerTest {
+
+    private NfcDetectionPointController mController;
+
+    @Before
+    public void setUp() {
+        mController = new NfcDetectionPointController(RuntimeEnvironment.application, "fakeKey");
+    }
+
+    @Test
+    public void getAvailabilityStatus_withConfigIsTrue_returnAvailable() {
+        mController.setConfig(true);
+        assertThat(mController.getAvailabilityStatus())
+            .isEqualTo(NfcDetectionPointController.AVAILABLE);
+    }
+
+    @Test
+    public void getAvailabilityStatus_withConfigIsFalse_returnUnavailable() {
+        mController.setConfig(false);
+        assertThat(mController.getAvailabilityStatus())
+            .isNotEqualTo(NfcDetectionPointController.AVAILABLE);
+    }
+}