Merge "Set ClearDefaultsPreference as unselectable for talkback"
diff --git a/res/layout/adb_qrcode_scanner_fragment.xml b/res/layout/adb_qrcode_scanner_fragment.xml
new file mode 100644
index 0000000..975256d
--- /dev/null
+++ b/res/layout/adb_qrcode_scanner_fragment.xml
@@ -0,0 +1,110 @@
+<?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.
+-->
+
+<com.google.android.setupdesign.GlifLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:theme="@style/GlifV3Theme.Light"
+    android:icon="@drawable/ic_scan_32dp">
+
+    <LinearLayout
+        style="@style/SudContentFrame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:gravity="center_horizontal">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            android:gravity="center_horizontal"
+            android:id="@+id/camera_layout">
+
+            <TextView
+                android:id="@android:id/summary"
+                style="@style/TextAppearance.SudGlifBody"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="?attr/sudMarginSides"
+                android:layout_marginEnd="?attr/sudMarginSides"
+                android:textAlignment="center"
+                android:accessibilityLiveRegion="polite"/>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:orientation="vertical">
+
+                <FrameLayout
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:clipChildren="true">
+                    <TextureView
+                        android:id="@+id/preview_view"
+                        android:layout_width="match_parent"
+                        android:layout_height="@dimen/qrcode_preview_size"/>
+                    <com.android.settings.wifi.qrcode.QrDecorateView
+                        android:id="@+id/decorate_view"
+                        android:layout_width="match_parent"
+                        android:layout_height="@dimen/qrcode_preview_size"/>
+                </FrameLayout>
+
+                <TextView
+                    android:id="@+id/error_message"
+                    style="@style/TextAppearance.ErrorText"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:layout_marginStart="?attr/sudMarginSides"
+                    android:layout_marginEnd="?attr/sudMarginSides"
+                    android:textAlignment="center"
+                    android:visibility="invisible"/>
+
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <!--
+             The spinner indicating that the device is waiting for pairing
+             after getting valid QR code
+        -->
+        <LinearLayout
+            android:id="@+id/verifying_layout"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:visibility="gone">
+
+            <ProgressBar
+                android:id="@+id/verifying_progress"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                style="@style/adb_wireless_item_progress_text"
+                android:text="@string/adb_wireless_verifying_qrcode_text"/>
+
+        </LinearLayout>
+
+    </LinearLayout>
+</com.google.android.setupdesign.GlifLayout>
diff --git a/res/layout/adb_wireless_dialog.xml b/res/layout/adb_wireless_dialog.xml
new file mode 100644
index 0000000..31f94c9
--- /dev/null
+++ b/res/layout/adb_wireless_dialog.xml
@@ -0,0 +1,112 @@
+<?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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/dialog_scrollview"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:fadeScrollbars="false"
+        android:scrollIndicators="top|bottom">
+
+    <LinearLayout
+            android:id="@+id/l_adbwirelessdialog"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:paddingBottom="8dip">
+
+        <LinearLayout android:id="@+id/l_pairing_six_digit"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                style="@style/adb_wireless_section"
+                android:visibility="gone">
+
+            <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    style="@style/adb_wireless_item" >
+                <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        style="@style/adb_wireless_item_label"
+                        android:text="@string/adb_pairing_device_dialog_pairing_code_label"
+                        android:textDirection="locale" />
+
+                <TextView
+                    android:id="@+id/pairing_code"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    style="@style/adb_wireless_item_content" />
+
+                <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        style="@style/adb_wireless_item_label"
+                        android:text="@string/adb_wireless_ip_addr_preference_title"
+                        android:textDirection="locale"
+                        android:paddingTop="8dip"/>
+
+                <TextView
+                        android:id="@+id/ip_addr"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        style="@style/adb_wireless_item_label"
+                        android:text="@string/summary_placeholder"
+                        android:textDirection="locale" />
+            </LinearLayout>
+        </LinearLayout>
+
+        <LinearLayout android:id="@+id/l_pairing_failed"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                style="@style/adb_wireless_section"
+                android:visibility="gone">
+
+            <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    style="@style/adb_wireless_item" >
+                <TextView
+                        android:id="@+id/pairing_failed_label"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        style="@style/adb_wireless_item_label"
+                        android:textDirection="locale" />
+            </LinearLayout>
+        </LinearLayout>
+
+        <LinearLayout android:id="@+id/l_qrcode_pairing_failed"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                style="@style/adb_wireless_section"
+                android:visibility="gone">
+
+            <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    style="@style/adb_wireless_item" >
+                <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        style="@style/adb_wireless_item_label"
+                        android:textDirection="locale"
+                        android:text="@string/adb_qrcode_pairing_device_failed_msg"/>
+            </LinearLayout>
+        </LinearLayout>
+
+    </LinearLayout>
+</ScrollView>
+
diff --git a/res/values/config.xml b/res/values/config.xml
index 2373b25..64d9ab7 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -201,6 +201,9 @@
     <!-- Whether or not TopLevelSettings should force rounded icon for injected tiles -->
     <bool name="config_force_rounded_icon_TopLevelSettings">true</bool>
 
+    <!-- Whether dismissal timestamp should be kept before deletion -->
+    <bool name="config_keep_contextual_card_dismissal_timestamp">false</bool>
+
     <!-- Settings intelligence package name -->
     <string name="config_settingsintelligence_package_name" translatable="false">
         com.android.settings.intelligence
diff --git a/res/values/strings.xml b/res/values/strings.xml
index aa37f06..47b30ea 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5086,29 +5086,19 @@
     <string name="daltonizer_mode_tritanomaly_summary">Tritanomaly</string>
 
     <!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
-    <plurals name="accessibilty_autoclick_preference_subtitle_extremely_short_delay">
-        <item quantity="one">Extremely short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
-        <item quantity="other">Extremely short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
-    </plurals>
-    <!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
-    <plurals name="accessibilty_autoclick_preference_subtitle_very_short_delay">
-        <item quantity="one">Very short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
-        <item quantity="other">Very short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
-    </plurals>
-    <!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
     <plurals name="accessibilty_autoclick_preference_subtitle_short_delay">
-        <item quantity="one">Short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
-        <item quantity="other">Short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
+        <item quantity="one">Short (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
+        <item quantity="other">Short (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
+    </plurals>
+    <!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
+    <plurals name="accessibilty_autoclick_preference_subtitle_medium_delay">
+        <item quantity="one">Medium (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
+        <item quantity="other">Medium (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
     </plurals>
     <!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
     <plurals name="accessibilty_autoclick_preference_subtitle_long_delay">
-        <item quantity="one">Long delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
-        <item quantity="other">Long delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
-    </plurals>
-    <!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
-    <plurals name="accessibilty_autoclick_preference_subtitle_very_long_delay">
-        <item quantity="one">Very long delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
-        <item quantity="other">Very long delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
+        <item quantity="one">Long (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
+        <item quantity="other">Long (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
     </plurals>
 
     <!-- Summary for autoclick seekbar settings preference when user selected custom item. [CHAR LIMIT=35] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c42fab2..85b25e4 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -153,6 +153,44 @@
         <item name="android:orientation">vertical</item>
     </style>
 
+    <style name="adb_wireless_item">
+        <item name="android:layout_marginTop">8dp</item>
+        <item name="android:layout_marginStart">8dp</item>
+        <item name="android:layout_marginEnd">8dp</item>
+        <item name="android:paddingStart">8dp</item>
+        <item name="android:paddingEnd">8dp</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:gravity">start</item>
+    </style>
+
+    <style name="adb_wireless_item_label">
+        <item name="android:paddingStart">8dp</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textAlignment">viewStart</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.Material.Body1</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+    </style>
+
+    <style name="adb_wireless_item_content">
+        <item name="android:paddingStart">8dp</item>
+        <item name="android:textSize">24sp</item>
+        <item name="android:textAlignment">viewStart</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.Material.Body1</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="adb_wireless_item_progress_text">
+        <item name="android:paddingTop">16dp</item>
+        <item name="android:textSize">18sp</item>
+        <item name="android:textAlignment">viewStart</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.Material.Body1</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="adb_wireless_section">
+        <item name="android:orientation">vertical</item>
+    </style>
+
     <style name="ConfirmDeviceCredentialsAnimationStyle"
            parent="@*android:style/Animation.Material.Activity">
         <item name="android:activityOpenEnterAnimation">@anim/confirm_credential_open_enter</item>
diff --git a/res/xml/adb_device_details_fragment.xml b/res/xml/adb_device_details_fragment.xml
new file mode 100644
index 0000000..2e149ed
--- /dev/null
+++ b/res/xml/adb_device_details_fragment.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:title="@string/device_details_title"
+    settings:initialExpandedChildrenCount="3">
+
+    <com.android.settingslib.widget.LayoutPreference
+        android:key="adb_device_header"
+        android:layout="@layout/settings_entity_header"
+        android:selectable="false" />
+
+    <!-- Buttons -->
+    <com.android.settingslib.widget.ActionButtonsPreference
+        android:key="buttons"
+        android:selectable="false" />
+
+    <!-- Device Fingerprint Details -->
+    <PreferenceCategory
+            android:key="fingerprint_category"
+            android:layout="@layout/preference_category_no_label">
+    </PreferenceCategory>
+</PreferenceScreen>
+
diff --git a/res/xml/adb_wireless_settings.xml b/res/xml/adb_wireless_settings.xml
new file mode 100644
index 0000000..7f60c69
--- /dev/null
+++ b/res/xml/adb_wireless_settings.xml
@@ -0,0 +1,68 @@
+<?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.
+-->
+
+<PreferenceScreen
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:settings="http://schemas.android.com/apk/res-auto"
+        android:title="@string/adb_wireless_settings">
+
+    <PreferenceCategory
+        android:layout="@layout/preference_category_no_label">
+        <!-- ADB device name -->
+        <Preference
+            android:key="adb_device_name_pref"
+            android:title="@string/my_device_info_device_name_preference_title"
+            android:summary="@string/summary_placeholder"
+            android:selectable="false"
+            settings:controller="com.android.settings.development.AdbDeviceNamePreferenceController"
+            settings:enableCopying="true"/>
+
+        <!-- IP address & port -->
+        <Preference
+            android:key="adb_ip_addr_pref"
+            android:title="@string/adb_wireless_ip_addr_preference_title"
+            android:summary="@string/summary_placeholder"
+            android:selectable="false"/>
+    </PreferenceCategory>
+
+    <!-- Pairing methods category -->
+    <PreferenceCategory
+        android:key="adb_pairing_methods_category"
+        android:layout="@layout/preference_category_no_label">
+        <!-- qrcode scanner -->
+        <Preference
+            android:key="adb_pair_method_qrcode_pref"
+            android:icon="@drawable/ic_scan_24dp"
+            android:title="@string/adb_pair_method_qrcode_title"
+            android:summary="@string/adb_pair_method_qrcode_summary"/>
+        <Preference
+            android:key="adb_pair_method_code_pref"
+            android:title="@string/adb_pair_method_code_title"
+            android:summary="@string/adb_pair_method_code_summary"/>
+    </PreferenceCategory>
+
+    <!-- Paired devices list -->
+    <PreferenceCategory
+        android:key="adb_paired_devices_category"
+        android:title="@string/adb_paired_devices_title"/>
+
+    <!-- Off message: Shown only in the off state -->
+    <PreferenceCategory
+        android:key="adb_wireless_footer_category"
+        android:layout="@layout/preference_category_no_label"
+        settings:allowDividerAbove="false"/>
+
+</PreferenceScreen>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index c5920f0..834fb48 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -141,6 +141,13 @@
         <Preference android:key="clear_adb_keys"
                     android:title="@string/clear_adb_keys" />
 
+        <com.android.settings.widget.MasterSwitchPreference
+            android:fragment="com.android.settings.development.WirelessDebuggingFragment"
+            android:key="toggle_adb_wireless"
+            android:title="@string/enable_adb_wireless"
+            android:summary="@string/enable_adb_wireless_summary"
+            settings:keywords="@string/keywords_adb_wireless" />
+
         <SwitchPreference
             android:key="enable_terminal"
             android:title="@string/enable_terminal_title"
@@ -269,6 +276,11 @@
             android:summary="@string/mobile_data_always_on_summary" />
 
         <SwitchPreference
+            android:key="enhanced_connectivity"
+            android:title="@string/enhanced_connectivity"
+            android:summary="@string/enhanced_connectivity_summary" />
+
+        <SwitchPreference
             android:key="tethering_hardware_offload"
             android:title="@string/tethering_hardware_offload"
             android:summary="@string/tethering_hardware_offload_summary" />
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index de08df2..3883eba 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -30,6 +30,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources.Theme;
+import android.graphics.drawable.Icon;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -491,7 +492,7 @@
 
     @Override
     public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
-        taskDescription.setIcon(R.drawable.ic_launcher_settings);
+        taskDescription.setIcon(Icon.createWithResource(this, R.drawable.ic_launcher_settings));
         super.setTaskDescription(taskDescription);
     }
 
diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java
index 8da6fbb..76fb3ff 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtil.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtil.java
@@ -21,9 +21,11 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.Build;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.TypedValue;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.IntDef;
@@ -350,4 +352,32 @@
                         "Unsupported userShortcutType " + shortcutType);
         }
     }
+
+    /**
+     * Gets the width of the screen.
+     *
+     * @param context the current context.
+     * @return the width of the screen in terms of pixels.
+     */
+    public static int getScreenWidthPixels(Context context) {
+        final Resources resources = context.getResources();
+        final int screenWidthDp = resources.getConfiguration().screenWidthDp;
+
+        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp,
+                resources.getDisplayMetrics()));
+    }
+
+    /**
+     * Gets the height of the screen.
+     *
+     * @param context the current context.
+     * @return the height of the screen in terms of pixels.
+     */
+    public static int getScreenHeightPixels(Context context) {
+        final Resources resources = context.getResources();
+        final int screenHeightDp = resources.getConfiguration().screenHeightDp;
+
+        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
+                resources.getDisplayMetrics()));
+    }
 }
diff --git a/src/com/android/settings/accessibility/AnimatedImagePreference.java b/src/com/android/settings/accessibility/AnimatedImagePreference.java
index 1609a3c..61e439f 100644
--- a/src/com/android/settings/accessibility/AnimatedImagePreference.java
+++ b/src/com/android/settings/accessibility/AnimatedImagePreference.java
@@ -34,6 +34,7 @@
 public class AnimatedImagePreference extends Preference {
 
     private Uri mImageUri;
+    private int mMaxHeight = -1;
 
     AnimatedImagePreference(Context context) {
         super(context);
@@ -45,20 +46,26 @@
         super.onBindViewHolder(holder);
 
         final ImageView imageView = holder.itemView.findViewById(R.id.animated_img);
-        if (imageView != null && mImageUri != null) {
+        if (imageView == null) {
+            return;
+        }
+
+        if (mImageUri != null) {
             imageView.setImageURI(mImageUri);
 
             final Drawable drawable = imageView.getDrawable();
-            if (drawable != null) {
-                if (drawable instanceof AnimatedImageDrawable) {
-                    ((AnimatedImageDrawable) drawable).start();
-                }
+            if (drawable instanceof AnimatedImageDrawable) {
+                ((AnimatedImageDrawable) drawable).start();
             }
         }
+
+        if (mMaxHeight > -1) {
+            imageView.setMaxWidth(mMaxHeight);
+        }
     }
 
     /**
-     * Set image uri to display image in {@link ImageView}
+     * Sets image uri to display image in {@link ImageView}
      *
      * @param imageUri the Uri of an image
      */
@@ -68,4 +75,16 @@
             notifyChanged();
         }
     }
+
+    /**
+     * Sets the maximum height of the view.
+     *
+     * @param maxHeight the maximum height of ImageView in terms of pixels.
+     */
+    public void setMaxHeight(int maxHeight) {
+        if (maxHeight != mMaxHeight) {
+            mMaxHeight = maxHeight;
+            notifyChanged();
+        }
+    }
 }
diff --git a/src/com/android/settings/accessibility/HtmlTextPreference.java b/src/com/android/settings/accessibility/HtmlTextPreference.java
index fcf4725..4c528a3 100644
--- a/src/com/android/settings/accessibility/HtmlTextPreference.java
+++ b/src/com/android/settings/accessibility/HtmlTextPreference.java
@@ -23,9 +23,6 @@
 
 import androidx.preference.PreferenceViewHolder;
 
-import java.util.List;
-import java.util.regex.Pattern;
-
 /**
  * A custom {@link android.widget.TextView} preference that shows html text with a custom tag
  * filter.
@@ -35,7 +32,6 @@
     private int mFlag = Html.FROM_HTML_MODE_COMPACT;
     private Html.ImageGetter mImageGetter;
     private Html.TagHandler mTagHandler;
-    private List<String> mUnsupportedTagList;
 
     HtmlTextPreference(Context context) {
         super(context);
@@ -47,8 +43,8 @@
 
         final TextView summaryView = holder.itemView.findViewById(android.R.id.summary);
         if (summaryView != null && !TextUtils.isEmpty(getSummary())) {
-            final String filteredText = getFilteredText(getSummary().toString());
-            summaryView.setText(Html.fromHtml(filteredText, mFlag, mImageGetter, mTagHandler));
+            summaryView.setText(
+                    Html.fromHtml(getSummary().toString(), mFlag, mImageGetter, mTagHandler));
         }
     }
 
@@ -87,39 +83,4 @@
             notifyChanged();
         }
     }
-
-    /**
-     * Sets unsupported tag list, the text will be filtered though this list in advanced.
-     *
-     * @param unsupportedTagList the list of unsupported tags
-     */
-    public void setUnsupportedTagList(List<String> unsupportedTagList) {
-        if (unsupportedTagList != null && !unsupportedTagList.equals(mUnsupportedTagList)) {
-            mUnsupportedTagList = unsupportedTagList;
-            notifyChanged();
-        }
-    }
-
-    private String getFilteredText(String text) {
-        if (mUnsupportedTagList == null) {
-            return text;
-        }
-
-        int i = 1;
-        for (String tag : mUnsupportedTagList) {
-            if (!TextUtils.isEmpty(text)) {
-                final String index = String.valueOf(i++);
-                final String targetStart1 = "(?i)<" + tag + " ";
-                final String targetStart2 = "(?i)<" + tag + ">";
-                final String replacementStart1 = "<unsupportedtag" + index + " ";
-                final String replacementStart2 = "<unsupportedtag" + index + ">";
-                final String targetEnd = "(?i)</" + tag + ">";
-                final String replacementEnd = "</unsupportedtag" + index + ">";
-                text = Pattern.compile(targetStart1).matcher(text).replaceAll(replacementStart1);
-                text = Pattern.compile(targetStart2).matcher(text).replaceAll(replacementStart2);
-                text = Pattern.compile(targetEnd).matcher(text).replaceAll(replacementEnd);
-            }
-        }
-        return text;
-    }
 }
diff --git a/src/com/android/settings/accessibility/ToggleAutoclickPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAutoclickPreferenceFragment.java
index 5e09b2a..6624457 100644
--- a/src/com/android/settings/accessibility/ToggleAutoclickPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAutoclickPreferenceFragment.java
@@ -64,12 +64,10 @@
      * Resource ids from which autoclick preference summaries should be derived. The strings have
      * placeholder for integer delay value.
      */
-    private static final int[] mAutoclickPreferenceSummaries = {
-            R.plurals.accessibilty_autoclick_preference_subtitle_extremely_short_delay,
-            R.plurals.accessibilty_autoclick_preference_subtitle_very_short_delay,
+    private static final int[] AUTOCLICK_PREFERENCE_SUMMARIES = {
             R.plurals.accessibilty_autoclick_preference_subtitle_short_delay,
-            R.plurals.accessibilty_autoclick_preference_subtitle_long_delay,
-            R.plurals.accessibilty_autoclick_preference_subtitle_very_long_delay
+            R.plurals.accessibilty_autoclick_preference_subtitle_medium_delay,
+            R.plurals.accessibilty_autoclick_preference_subtitle_long_delay
     };
 
     /**
@@ -86,7 +84,7 @@
         // Only show integer when delay time is 1.
         final String decimalFormat = (delaySecond == 1) ? "%.0f" : "%.1f";
 
-        return resources.getQuantityString(mAutoclickPreferenceSummaries[summaryIndex],
+        return resources.getQuantityString(AUTOCLICK_PREFERENCE_SUMMARIES[summaryIndex],
                 quantity, String.format(decimalFormat, delaySecond));
     }
 
@@ -98,10 +96,10 @@
             return 0;
         }
         if (delay >= MAX_AUTOCLICK_DELAY_MS) {
-            return mAutoclickPreferenceSummaries.length - 1;
+            return AUTOCLICK_PREFERENCE_SUMMARIES.length - 1;
         }
         int delayRange = MAX_AUTOCLICK_DELAY_MS - MIN_AUTOCLICK_DELAY_MS;
-        int rangeSize = (delayRange) / (mAutoclickPreferenceSummaries.length - 1);
+        int rangeSize = (delayRange) / (AUTOCLICK_PREFERENCE_SUMMARIES.length - 1);
         return (delay - MIN_AUTOCLICK_DELAY_MS) / rangeSize;
     }
 
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 19a0f2b..838a758 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.accessibility;
 
+import static com.android.settings.accessibility.AccessibilityUtil.getScreenHeightPixels;
+import static com.android.settings.accessibility.AccessibilityUtil.getScreenWidthPixels;
+
 import android.app.Dialog;
 import android.app.settings.SettingsEnums;
 import android.content.ComponentName;
@@ -80,7 +83,6 @@
     protected CharSequence mPackageName;
     protected Uri mImageUri;
     protected CharSequence mHtmlDescription;
-    private static final String ANCHOR_TAG = "a";
     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";
@@ -94,9 +96,8 @@
     private CheckBox mSoftwareTypeCheckBox;
     private CheckBox mHardwareTypeCheckBox;
 
-    // For html description of accessibility service, third party developer must follow the rule,
-    // such as <img src="R.drawable.fileName"/>, a11y settings will get third party resources
-    // by this.
+    // For html description of accessibility service, must follow the rule, such as
+    // <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
     private static final String IMG_PREFIX = "R.drawable.";
 
     private ImageView mImageGetterCacheView;
@@ -147,10 +148,12 @@
 
         PreferenceScreen preferenceScreen = getPreferenceScreen();
         if (mImageUri != null) {
+            final int screenHalfHeight = getScreenHeightPixels(getPrefContext()) / /* half */ 2;
             final AnimatedImagePreference animatedImagePreference = new AnimatedImagePreference(
                     getPrefContext());
             animatedImagePreference.setImageUri(mImageUri);
             animatedImagePreference.setSelectable(false);
+            animatedImagePreference.setMaxHeight(screenHalfHeight);
             preferenceScreen.addPreference(animatedImagePreference);
         }
 
@@ -195,14 +198,9 @@
             introductionCategory.setTitle(title);
             preferenceScreen.addPreference(introductionCategory);
 
-            // For accessibility service, avoid malicious links made by third party developer.
-            final List<String> unsupportedTagList = new ArrayList<>();
-            unsupportedTagList.add(ANCHOR_TAG);
-
             final HtmlTextPreference htmlTextPreference = new HtmlTextPreference(getPrefContext());
             htmlTextPreference.setSummary(mHtmlDescription);
             htmlTextPreference.setImageGetter(mImageGetter);
-            htmlTextPreference.setUnsupportedTagList(unsupportedTagList);
             htmlTextPreference.setSelectable(false);
             introductionCategory.addPreference(htmlTextPreference);
         }
@@ -383,14 +381,24 @@
         mImageGetterCacheView.setAdjustViewBounds(true);
         mImageGetterCacheView.setImageURI(imageUri);
 
-        final Drawable drawable = mImageGetterCacheView.getDrawable().mutate();
-        if (drawable != null) {
-            drawable.setBounds(/* left= */0, /* top= */0, drawable.getIntrinsicWidth(),
-                    drawable.getIntrinsicHeight());
+        if (mImageGetterCacheView.getDrawable() == null) {
+            return null;
         }
 
+        final Drawable drawable =
+                mImageGetterCacheView.getDrawable().mutate().getConstantState().newDrawable();
         mImageGetterCacheView.setImageURI(null);
-        mImageGetterCacheView.setImageDrawable(null);
+        final int imageWidth = drawable.getIntrinsicWidth();
+        final int imageHeight = drawable.getIntrinsicHeight();
+        final int screenHalfHeight = getScreenHeightPixels(getPrefContext()) / /* half */ 2;
+        if ((imageWidth > getScreenWidthPixels(getPrefContext()))
+                || (imageHeight > screenHalfHeight)) {
+            return null;
+        }
+
+        drawable.setBounds(/* left= */0, /* top= */0, drawable.getIntrinsicWidth(),
+                drawable.getIntrinsicHeight());
+
         return drawable;
     }
 
diff --git a/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java
index cbb805f..34c67f1 100644
--- a/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java
@@ -38,7 +38,8 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return AppUtils.isInstant(mParent.getPackageInfo().applicationInfo)
+        return mParent.getPackageInfo() != null
+                && AppUtils.isInstant(mParent.getPackageInfo().applicationInfo)
                 ? AVAILABLE : DISABLED_FOR_USER;
     }
 
diff --git a/src/com/android/settings/development/AdbDeviceDetailsActionController.java b/src/com/android/settings/development/AdbDeviceDetailsActionController.java
new file mode 100644
index 0000000..da4430d
--- /dev/null
+++ b/src/com/android/settings/development/AdbDeviceDetailsActionController.java
@@ -0,0 +1,92 @@
+/*
+ * 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.development;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.debug.PairDevice;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.ActionButtonsPreference;
+
+/**
+ * Controller for logic pertaining to displaying adb device information for the
+ * {@link AdbDeviceDetailsFragment}.
+ */
+public class AdbDeviceDetailsActionController extends AbstractPreferenceController {
+    private static final String TAG = "AdbDeviceDetailsAction";
+
+    @VisibleForTesting
+    static final String KEY_BUTTONS_PREF = "buttons";
+
+    private PairDevice mPairedDevice;
+    private final Fragment mFragment;
+
+    private ActionButtonsPreference mButtonsPref;
+
+    public AdbDeviceDetailsActionController(
+            PairDevice pairedDevice,
+            Context context,
+            Fragment fragment) {
+        super(context);
+
+        mPairedDevice = pairedDevice;
+        mFragment = fragment;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BUTTONS_PREF;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mButtonsPref = ((ActionButtonsPreference) screen.findPreference(getPreferenceKey()))
+                .setButton1Visible(false)
+                .setButton2Icon(R.drawable.ic_settings_delete)
+                .setButton2Text(R.string.adb_device_forget)
+                .setButton2OnClickListener(view -> forgetDevice());
+    }
+
+    /**
+     * Forgets the device.
+     */
+    private void forgetDevice() {
+        Intent intent = new Intent();
+        intent.putExtra(
+                WirelessDebuggingFragment.PAIRED_DEVICE_REQUEST_TYPE,
+                WirelessDebuggingFragment.FORGET_ACTION);
+        intent.putExtra(
+                WirelessDebuggingFragment.PAIRED_DEVICE_EXTRA,
+                mPairedDevice);
+        mFragment.getActivity().setResult(Activity.RESULT_OK, intent);
+        mFragment.getActivity().finish();
+    }
+}
+
diff --git a/src/com/android/settings/development/AdbDeviceDetailsFingerprintController.java b/src/com/android/settings/development/AdbDeviceDetailsFingerprintController.java
new file mode 100644
index 0000000..b9c2d43
--- /dev/null
+++ b/src/com/android/settings/development/AdbDeviceDetailsFingerprintController.java
@@ -0,0 +1,80 @@
+/*
+ * 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.development;
+
+import android.content.Context;
+import android.debug.PairDevice;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.FooterPreference;
+
+/**
+ * Controller for logic pertaining to displaying adb device information for the
+ * {@link AdbDeviceDetailsFragment}.
+ */
+public class AdbDeviceDetailsFingerprintController extends AbstractPreferenceController {
+
+    private static final String TAG = "AdbDeviceDetailsFinger";
+
+    @VisibleForTesting
+    static final String KEY_FINGERPRINT_CATEGORY = "fingerprint_category";
+
+    private PairDevice mPairedDevice;
+    private final Fragment mFragment;
+
+    private PreferenceCategory mFingerprintCategory;
+    private FooterPreference mFingerprintPref;
+
+    public AdbDeviceDetailsFingerprintController(
+            PairDevice pairedDevice,
+            Context context,
+            Fragment fragment) {
+        super(context);
+
+        mPairedDevice = pairedDevice;
+        mFragment = fragment;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_FINGERPRINT_CATEGORY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mFingerprintCategory = (PreferenceCategory) screen.findPreference(getPreferenceKey());
+        mFingerprintPref = new FooterPreference(mFingerprintCategory.getContext());
+        final CharSequence titleFormat = mContext.getText(
+                R.string.adb_device_fingerprint_title_format);
+        mFingerprintPref.setTitle(String.format(
+                titleFormat.toString(), mPairedDevice.getGuid()));
+        mFingerprintCategory.addPreference(mFingerprintPref);
+    }
+}
+
diff --git a/src/com/android/settings/development/AdbDeviceDetailsFragment.java b/src/com/android/settings/development/AdbDeviceDetailsFragment.java
new file mode 100644
index 0000000..9861345
--- /dev/null
+++ b/src/com/android/settings/development/AdbDeviceDetailsFragment.java
@@ -0,0 +1,78 @@
+/*
+ * 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.development;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.debug.PairDevice;
+import android.os.Bundle;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment shown when clicking on a paired device in the Wireless
+ * Debugging fragment.
+ */
+public class AdbDeviceDetailsFragment extends DashboardFragment {
+    private static final String TAG = "AdbDeviceDetailsFrag";
+    private PairDevice mPairedDevice;
+
+    public AdbDeviceDetailsFragment() {
+        super();
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        // Get the paired device stored in the extras
+        Bundle bundle = getArguments();
+        if (bundle.containsKey(AdbPairedDevicePreference.PAIRED_DEVICE_EXTRA)) {
+            mPairedDevice = (PairDevice) bundle.getParcelable(
+                    AdbPairedDevicePreference.PAIRED_DEVICE_EXTRA);
+        }
+        super.onAttach(context);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.ADB_WIRELESS_DEVICE_DETAILS;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.adb_device_details_fragment;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new AdbDeviceDetailsHeaderController(mPairedDevice, context, this));
+        controllers.add(new AdbDeviceDetailsActionController(mPairedDevice, context, this));
+        controllers.add(new AdbDeviceDetailsFingerprintController(mPairedDevice, context, this));
+
+        return controllers;
+    }
+}
diff --git a/src/com/android/settings/development/AdbDeviceDetailsHeaderController.java b/src/com/android/settings/development/AdbDeviceDetailsHeaderController.java
new file mode 100644
index 0000000..a4e79ea
--- /dev/null
+++ b/src/com/android/settings/development/AdbDeviceDetailsHeaderController.java
@@ -0,0 +1,88 @@
+/*
+ * 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.development;
+
+import android.content.Context;
+import android.debug.PairDevice;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.widget.LayoutPreference;
+
+/**
+ * Controller for logic pertaining to displaying adb device information for the
+ * {@link AdbDeviceDetailsFragment}.
+ */
+public class AdbDeviceDetailsHeaderController extends AbstractPreferenceController
+        implements PreferenceControllerMixin, LifecycleObserver {
+
+    private static final String TAG = "AdbDeviceDetailsHeader";
+
+    @VisibleForTesting
+    static final String KEY_HEADER = "adb_device_header";
+
+    private PairDevice mPairedDevice;
+    private final Fragment mFragment;
+    private EntityHeaderController mEntityHeaderController;
+
+    public AdbDeviceDetailsHeaderController(
+            PairDevice pairedDevice,
+            Context context,
+            Fragment fragment) {
+        super(context);
+
+        mPairedDevice = pairedDevice;
+        mFragment = fragment;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_HEADER;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        setupEntityHeader(screen);
+    }
+
+    private void setupEntityHeader(PreferenceScreen screen) {
+        LayoutPreference headerPref = (LayoutPreference) screen.findPreference(KEY_HEADER);
+        mEntityHeaderController =
+                EntityHeaderController.newInstance(
+                        mFragment.getActivity(), mFragment,
+                        headerPref.findViewById(R.id.entity_header));
+
+        mEntityHeaderController
+                .setIcon(mContext.getDrawable(com.android.internal.R.drawable.ic_bt_laptop))
+                .setLabel(mPairedDevice.getDeviceName())
+                .done(mFragment.getActivity(), true);
+    }
+}
+
diff --git a/src/com/android/settings/development/AdbDeviceNamePreferenceController.java b/src/com/android/settings/development/AdbDeviceNamePreferenceController.java
new file mode 100644
index 0000000..7706c3c
--- /dev/null
+++ b/src/com/android/settings/development/AdbDeviceNamePreferenceController.java
@@ -0,0 +1,78 @@
+/*
+ * 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.development;
+
+import static android.content.Context.CLIPBOARD_SERVICE;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Build;
+import android.provider.Settings;
+import android.widget.Toast;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Controller for the device name preference in the Wireless debugging
+ * fragment.
+ */
+public class AdbDeviceNamePreferenceController extends BasePreferenceController {
+    private static final String TAG = "AdbDeviceNamePrefCtrl";
+
+    private String mDeviceName;
+
+    public AdbDeviceNamePreferenceController(Context context, String key) {
+        super(context, key);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        // Keep device name in sync with Settings > About phone > Device name
+        mDeviceName = Settings.Global.getString(mContext.getContentResolver(),
+                Settings.Global.DEVICE_NAME);
+        if (mDeviceName == null) {
+            mDeviceName = Build.MODEL;
+        }
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        return mDeviceName;
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public void copy() {
+        final ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(
+                CLIPBOARD_SERVICE);
+        clipboard.setPrimaryClip(ClipData.newPlainText("text", mDeviceName));
+
+        final String toast = mContext.getString(R.string.copyable_slice_toast,
+                mDeviceName);
+        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
+    }
+}
diff --git a/src/com/android/settings/development/AdbIpAddressPreferenceController.java b/src/com/android/settings/development/AdbIpAddressPreferenceController.java
new file mode 100644
index 0000000..dbc329e
--- /dev/null
+++ b/src/com/android/settings/development/AdbIpAddressPreferenceController.java
@@ -0,0 +1,156 @@
+/*
+ * 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.development;
+
+import android.content.Context;
+import android.debug.IAdbManager;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.wifi.WifiManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.deviceinfo.AbstractConnectivityPreferenceController;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.Iterator;
+
+/**
+ * Controller for the ip address preference in the Wireless debugging
+ * fragment.
+ */
+public class AdbIpAddressPreferenceController extends AbstractConnectivityPreferenceController {
+    private static final String TAG = "AdbIpAddrPrefCtrl";
+
+    private static final String[] CONNECTIVITY_INTENTS = {
+            ConnectivityManager.CONNECTIVITY_ACTION,
+            WifiManager.ACTION_LINK_CONFIGURATION_CHANGED,
+            WifiManager.NETWORK_STATE_CHANGED_ACTION,
+    };
+
+    private static final String PREF_KEY = "adb_ip_addr_pref";
+    private Preference mAdbIpAddrPref;
+    private int mPort;
+    private final ConnectivityManager mCM;
+    private IAdbManager mAdbManager;
+
+    public AdbIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, lifecycle);
+        mCM = context.getSystemService(ConnectivityManager.class);
+        mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(
+                Context.ADB_SERVICE));
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREF_KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mAdbIpAddrPref = screen.findPreference(PREF_KEY);
+        updateConnectivity();
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        updateConnectivity();
+    }
+
+    @Override
+    protected String[] getConnectivityIntents() {
+        return CONNECTIVITY_INTENTS;
+    }
+
+    protected int getPort() {
+        try {
+            return mAdbManager.getAdbWirelessPort();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get the adbwifi port");
+        }
+        return 0;
+    }
+
+    public String getIpv4Address() {
+        return getDefaultIpAddresses(mCM);
+    }
+
+    @Override
+    protected void updateConnectivity() {
+        String ipAddress = getDefaultIpAddresses(mCM);
+        if (ipAddress != null) {
+            int port = getPort();
+            if (port <= 0) {
+                mAdbIpAddrPref.setSummary(R.string.status_unavailable);
+            } else {
+                ipAddress += ":" + port;
+            }
+            mAdbIpAddrPref.setSummary(ipAddress);
+        } else {
+            mAdbIpAddrPref.setSummary(R.string.status_unavailable);
+        }
+    }
+
+    /**
+     * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
+     * addresses.
+     * @param cm ConnectivityManager
+     * @return the formatted and newline-separated IP addresses, or null if none.
+     */
+    private static String getDefaultIpAddresses(ConnectivityManager cm) {
+        LinkProperties prop = cm.getActiveLinkProperties();
+        return formatIpAddresses(prop);
+    }
+
+    private static String formatIpAddresses(LinkProperties prop) {
+        if (prop == null) {
+            return null;
+        }
+
+        Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
+        // If there are no entries, return null
+        if (!iter.hasNext()) {
+            return null;
+        }
+
+        // Concatenate all available addresses, newline separated
+        StringBuilder addresses = new StringBuilder();
+        while (iter.hasNext()) {
+            InetAddress addr = iter.next();
+            if (addr instanceof Inet4Address) {
+                // adb only supports ipv4 at the moment
+                addresses.append(addr.getHostAddress());
+                break;
+            }
+        }
+        return addresses.toString();
+    }
+}
diff --git a/src/com/android/settings/development/AdbPairedDevicePreference.java b/src/com/android/settings/development/AdbPairedDevicePreference.java
new file mode 100644
index 0000000..a82a949
--- /dev/null
+++ b/src/com/android/settings/development/AdbPairedDevicePreference.java
@@ -0,0 +1,95 @@
+/*
+ * 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.development;
+
+import android.content.Context;
+import android.debug.PairDevice;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+
+/**
+ * An AP preference for the currently connected AP
+ */
+public class AdbPairedDevicePreference extends Preference {
+    private static final String TAG = "AdbPairedDevicePref";
+
+    private PairDevice mPairedDevice;
+
+    // Extract using getSerializable(PAIRED_DEVICE_EXTRA)
+    public static final String PAIRED_DEVICE_EXTRA = "paired_device";
+
+    public AdbPairedDevicePreference(PairDevice pairedDevice, Context context) {
+        super(context);
+
+        mPairedDevice = pairedDevice;
+        setWidgetLayoutResource(getWidgetLayoutResourceId());
+        refresh();
+    }
+
+    protected int getWidgetLayoutResourceId() {
+        return R.layout.preference_widget_gear_optional_background;
+    }
+
+    /**
+     * Refreshes the preference bound to the paired device previously passed in.
+     */
+    public void refresh() {
+        setTitle(this, mPairedDevice);
+    }
+
+    public void setPairedDevice(PairDevice pairedDevice) {
+        mPairedDevice = pairedDevice;
+    }
+
+    public PairDevice getPairedDevice() {
+        return mPairedDevice;
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        final View gear = holder.findViewById(R.id.settings_button);
+        final View gearNoBg = holder.findViewById(R.id.settings_button_no_background);
+
+        gear.setVisibility(View.INVISIBLE);
+        gearNoBg.setVisibility(View.VISIBLE);
+    }
+
+    static void setTitle(AdbPairedDevicePreference preference,
+                         PairDevice pairedDevice) {
+        preference.setTitle(pairedDevice.getDeviceName());
+        preference.setSummary(pairedDevice.isConnected()
+                ? preference.getContext().getText(R.string.adb_wireless_device_connected_summary)
+                : "");
+    }
+
+    /**
+     * Writes the paired devices bound to this preference to the bundle.
+     *
+     * @param bundle the bundle to write the paired device to
+     */
+    public void savePairedDeviceToExtras(Bundle bundle) {
+        bundle.putParcelable(PAIRED_DEVICE_EXTRA, mPairedDevice);
+    }
+}
+
diff --git a/src/com/android/settings/development/AdbQrcodeScannerFragment.java b/src/com/android/settings/development/AdbQrcodeScannerFragment.java
new file mode 100644
index 0000000..4f8b113
--- /dev/null
+++ b/src/com/android/settings/development/AdbQrcodeScannerFragment.java
@@ -0,0 +1,326 @@
+/*
+ * 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.development;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.debug.AdbManager;
+import android.debug.IAdbManager;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.Size;
+import android.view.LayoutInflater;
+import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.TextView;
+
+import androidx.annotation.StringRes;
+
+import com.android.settings.R;
+import com.android.settings.wifi.dpp.AdbQrCode;
+import com.android.settings.wifi.dpp.WifiDppQrCodeBaseFragment;
+import com.android.settings.wifi.dpp.WifiNetworkConfig;
+import com.android.settings.wifi.qrcode.QrCamera;
+import com.android.settings.wifi.qrcode.QrDecorateView;
+
+/**
+ * Fragment shown when clicking on the "Pair by QR code" preference in
+ * the Wireless Debugging fragment.
+ */
+public class AdbQrcodeScannerFragment extends WifiDppQrCodeBaseFragment implements
+        SurfaceTextureListener,
+        QrCamera.ScannerCallback {
+    private static final String TAG = "AdbQrcodeScannerFrag";
+
+    /** Message sent to hide error message */
+    private static final int MESSAGE_HIDE_ERROR_MESSAGE = 1;
+
+    /** Message sent to show error message */
+    private static final int MESSAGE_SHOW_ERROR_MESSAGE = 2;
+
+    private static final long SHOW_ERROR_MESSAGE_INTERVAL = 10000;
+    private static final long SHOW_SUCCESS_SQUARE_INTERVAL = 1000;
+
+    private QrCamera mCamera;
+    private TextureView mTextureView;
+    private QrDecorateView mDecorateView;
+    private View mQrCameraView;
+    private View mVerifyingView;
+    private TextView mErrorMessage;
+
+    /** QR code data scanned by camera */
+    private AdbQrCode mAdbQrCode;
+    private WifiNetworkConfig mAdbConfig;
+
+    private IAdbManager mAdbManager;
+
+    private IntentFilter mIntentFilter;
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
+                Integer res = intent.getIntExtra(
+                        AdbManager.WIRELESS_STATUS_EXTRA,
+                        AdbManager.WIRELESS_STATUS_FAIL);
+                if (res.equals(AdbManager.WIRELESS_STATUS_SUCCESS)) {
+                    Intent i = new Intent();
+                    i.putExtra(
+                            WirelessDebuggingFragment.PAIRING_DEVICE_REQUEST_TYPE,
+                            WirelessDebuggingFragment.SUCCESS_ACTION);
+                    getActivity().setResult(Activity.RESULT_OK, i);
+                    getActivity().finish();
+                } else if (res.equals(AdbManager.WIRELESS_STATUS_FAIL)) {
+                    Intent i = new Intent();
+                    i.putExtra(
+                            WirelessDebuggingFragment.PAIRING_DEVICE_REQUEST_TYPE,
+                            WirelessDebuggingFragment.FAIL_ACTION);
+                    getActivity().setResult(Activity.RESULT_OK, i);
+                    getActivity().finish();
+                } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
+                    int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
+                    Log.i(TAG, "Got Qr pairing code port=" + port);
+                }
+            }
+        }
+    };
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_HIDE_ERROR_MESSAGE:
+                    mErrorMessage.setVisibility(View.INVISIBLE);
+                    break;
+
+                case MESSAGE_SHOW_ERROR_MESSAGE:
+                    final String errorMessage = (String) msg.obj;
+
+                    mErrorMessage.setVisibility(View.VISIBLE);
+                    mErrorMessage.setText(errorMessage);
+                    mErrorMessage.sendAccessibilityEvent(
+                            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+
+                    // Cancel any pending messages to hide error view and requeue the message so
+                    // user has time to see error
+                    removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
+                    sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,
+                            SHOW_ERROR_MESSAGE_INTERVAL);
+                    break;
+
+                default:
+                    return;
+            }
+        }
+    };
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mIntentFilter = new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+    }
+
+    @Override
+    public final View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.adb_qrcode_scanner_fragment, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        mTextureView = (TextureView) view.findViewById(R.id.preview_view);
+        mTextureView.setSurfaceTextureListener(this);
+
+        mDecorateView = view.findViewById(R.id.decorate_view);
+        setProgressBarShown(false);
+
+        setHeaderIconImageResource(R.drawable.ic_scan_24dp);
+
+        mQrCameraView = view.findViewById(R.id.camera_layout);
+        mVerifyingView = view.findViewById(R.id.verifying_layout);
+
+        setHeaderTitle(R.string.wifi_dpp_scan_qr_code);
+        mSummary.setText(R.string.adb_wireless_qrcode_pairing_description);
+
+        mErrorMessage = view.findViewById(R.id.error_message);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(Context.ADB_SERVICE));
+        getActivity().registerReceiver(mReceiver, mIntentFilter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+
+        getActivity().unregisterReceiver(mReceiver);
+        try {
+            mAdbManager.disablePairing();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to cancel pairing");
+        }
+        getActivity().finish();
+    }
+
+    @Override
+    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+        // Do nothing
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return 0;
+    }
+
+    @Override
+    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+        initCamera(surface);
+    }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+        // Do nothing
+    }
+
+    @Override
+    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+        destroyCamera();
+        return true;
+    }
+
+    @Override
+    public Size getViewSize() {
+        return new Size(mTextureView.getWidth(), mTextureView.getHeight());
+    }
+
+    @Override
+    public void setTransform(Matrix transform) {
+        mTextureView.setTransform(transform);
+    }
+
+    @Override
+    public Rect getFramePosition(Size previewSize, int cameraOrientation) {
+        return new Rect(0, 0, previewSize.getHeight(), previewSize.getHeight());
+    }
+
+    @Override
+    public boolean isValid(String qrCode) {
+        try {
+            // WIFI:T:ADB;S:myname;P:mypass;;
+            mAdbQrCode = new AdbQrCode(qrCode);
+        } catch (IllegalArgumentException e) {
+            showErrorMessage(R.string.wifi_dpp_qr_code_is_not_valid_format);
+            return false;
+        }
+
+        mAdbConfig = mAdbQrCode.getAdbNetworkConfig();
+
+        return true;
+    }
+
+    @Override
+    public void handleSuccessfulResult(String qrCode) {
+        destroyCamera();
+        mDecorateView.setFocused(true);
+        mQrCameraView.setVisibility(View.GONE);
+        mVerifyingView.setVisibility(View.VISIBLE);
+        try {
+            mAdbManager.enablePairingByQrCode(mAdbConfig.getSsid(),
+                    mAdbConfig.getPreSharedKey());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to enable QR code pairing");
+            getActivity().finish();
+        }
+    }
+
+    @Override
+    public void handleCameraFailure() {
+        destroyCamera();
+    }
+
+    private void initCamera(SurfaceTexture surface) {
+        // Check if the camera has alread been created.
+        if (mCamera == null) {
+            mCamera = new QrCamera(getContext(), this);
+            mCamera.start(surface);
+        }
+    }
+
+    /**
+     * To resume camera decoding task after handshake fail or Wi-Fi connection fail.
+     */
+    private void restartCamera() {
+        if (mCamera == null) {
+            Log.d(TAG, "mCamera is not available for restarting camera");
+            return;
+        }
+
+        if (mCamera.isDecodeTaskAlive()) {
+            mCamera.stop();
+        }
+
+        final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
+        if (surfaceTexture == null) {
+            throw new IllegalStateException("SurfaceTexture is not ready for restarting camera");
+        }
+
+        mCamera.start(surfaceTexture);
+    }
+
+    private void destroyCamera() {
+        if (mCamera != null) {
+            mCamera.stop();
+            mCamera = null;
+        }
+    }
+
+    private void showErrorMessage(@StringRes int messageResId) {
+        final Message message = mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE,
+                getString(messageResId));
+        message.sendToTarget();
+    }
+
+    @Override
+    protected boolean isFooterAvailable() {
+        return false;
+    }
+}
diff --git a/src/com/android/settings/development/AdbWirelessDialog.java b/src/com/android/settings/development/AdbWirelessDialog.java
new file mode 100644
index 0000000..48884b9
--- /dev/null
+++ b/src/com/android/settings/development/AdbWirelessDialog.java
@@ -0,0 +1,157 @@
+/*
+ * 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.development;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+
+/**
+ * Class to show a variety of dialogs for the Wireless debugging
+ * fragment.
+ */
+public class AdbWirelessDialog extends AlertDialog implements
+        AdbWirelessDialogUiBase,
+        DialogInterface.OnClickListener {
+
+    /**
+     * Interface for subscribers to implement in order to listen
+     * to AdbWirelessDialog events.
+     */
+    public interface AdbWirelessDialogListener {
+        /**
+         * Called when the dialog was closed by clicking a negative button.
+         */
+        default void onCancel() {
+        }
+
+        /**
+         * Called when the dialog was closed by clicking a positive button.
+         *
+         * @param dialog the dialog that was closed.
+         */
+        default void onSubmit(AdbWirelessDialog dialog) {
+        }
+
+        /**
+         * Called when the dialog was dismissed.
+         */
+        default void onDismiss() {
+        }
+    }
+
+    private static final String TAG = "AdbWirelessDialog";
+
+    private static final int BUTTON_CANCEL = DialogInterface.BUTTON_NEGATIVE;
+    private static final int BUTTON_SUBMIT = DialogInterface.BUTTON_POSITIVE;
+
+    private final AdbWirelessDialogListener mListener;
+    private final int mMode;
+
+    private View mView;
+    private AdbWirelessDialogController mController;
+
+    /**
+     * Creates a AdbWirelessDialog with no additional style. It displays as a dialog above the
+     * current view.
+     */
+    public static AdbWirelessDialog createModal(
+            Context context,
+            AdbWirelessDialogListener listener,
+            int mode) {
+        return new AdbWirelessDialog(context, listener, mode);
+    }
+
+    AdbWirelessDialog(Context context, AdbWirelessDialogListener listener, int mode) {
+        super(context);
+        mListener = listener;
+        mMode = mode;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        mView = getLayoutInflater().inflate(R.layout.adb_wireless_dialog, null);
+        setView(mView);
+        mController = new AdbWirelessDialogController(this, mView, mMode);
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        dismiss();
+        if (mListener != null) {
+            mListener.onDismiss();
+        }
+    }
+
+    @Override
+    public void onClick(DialogInterface dialogInterface, int id) {
+        if (mListener != null) {
+            switch (id) {
+                case BUTTON_CANCEL:
+                    mListener.onCancel();
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public AdbWirelessDialogController getController() {
+        return mController;
+    }
+
+    @Override
+    public void dispatchSubmit() {
+        if (mListener != null) {
+            mListener.onSubmit(this);
+        }
+        dismiss();
+    }
+
+    @Override
+    public int getMode() {
+        return mMode;
+    }
+
+    @Override
+    public Button getSubmitButton() {
+        return getButton(BUTTON_SUBMIT);
+    }
+
+    @Override
+    public Button getCancelButton() {
+        return getButton(BUTTON_NEGATIVE);
+    }
+
+    @Override
+    public void setSubmitButton(CharSequence text) {
+        setButton(BUTTON_SUBMIT, text, this);
+    }
+
+    @Override
+    public void setCancelButton(CharSequence text) {
+        setButton(BUTTON_NEGATIVE, text, this);
+    }
+}
diff --git a/src/com/android/settings/development/AdbWirelessDialogController.java b/src/com/android/settings/development/AdbWirelessDialogController.java
new file mode 100644
index 0000000..136c7b3
--- /dev/null
+++ b/src/com/android/settings/development/AdbWirelessDialogController.java
@@ -0,0 +1,104 @@
+/*
+ * 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.development;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+/**
+ * The class for allowing UIs like {@link AdbWirelessDialog} and {@link AdbWirelessDialogUiBase} to
+ * share the logic for controlling buttons, text fields, etc.
+ */
+public class AdbWirelessDialogController {
+    private static final String TAG = "AdbWirelessDialogCtrl";
+
+    private final AdbWirelessDialogUiBase mUi;
+    private final View mView;
+
+    private int mMode;
+
+    // The dialog for showing the six-digit code
+    private TextView mPairingCodeTitle;
+    private TextView mSixDigitCode;
+    private TextView mIpAddr;
+
+    // The dialog for showing pairing failed message
+    private TextView mFailedMsg;
+
+    private Context mContext;
+
+    public AdbWirelessDialogController(AdbWirelessDialogUiBase parent, View view,
+            int mode) {
+        mUi = parent;
+        mView = view;
+        mMode = mode;
+
+        mContext = mUi.getContext();
+        final Resources res = mContext.getResources();
+
+        mSixDigitCode = mView.findViewById(R.id.pairing_code);
+        mIpAddr = mView.findViewById(R.id.ip_addr);
+
+        switch (mMode) {
+            case AdbWirelessDialogUiBase.MODE_PAIRING:
+                String title = res.getString(R.string.adb_pairing_device_dialog_title);
+                mUi.setTitle(title);
+                mView.findViewById(R.id.l_pairing_six_digit).setVisibility(View.VISIBLE);
+                mUi.setCancelButton(res.getString(R.string.cancel));
+                mUi.setCanceledOnTouchOutside(false);
+                break;
+            case AdbWirelessDialogUiBase.MODE_PAIRING_FAILED:
+                String msg = res.getString(R.string.adb_pairing_device_dialog_failed_msg);
+                mUi.setTitle(R.string.adb_pairing_device_dialog_failed_title);
+                mView.findViewById(R.id.l_pairing_failed).setVisibility(View.VISIBLE);
+                mFailedMsg = (TextView) mView.findViewById(R.id.pairing_failed_label);
+                mFailedMsg.setText(msg);
+                mUi.setSubmitButton(res.getString(R.string.okay));
+                break;
+            case AdbWirelessDialogUiBase.MODE_QRCODE_FAILED:
+                mUi.setTitle(R.string.adb_pairing_device_dialog_failed_title);
+                mView.findViewById(R.id.l_qrcode_pairing_failed).setVisibility(View.VISIBLE);
+                mUi.setSubmitButton(res.getString(R.string.okay));
+                break;
+        }
+
+        // After done view show and hide, request focus from parent view
+        mView.findViewById(R.id.l_adbwirelessdialog).requestFocus();
+    }
+
+    /**
+     * Set the pairing code UI text field to code.
+     *
+     * @param code the pairing code string
+     */
+    public void setPairingCode(String code) {
+        mSixDigitCode.setText(code);
+    }
+
+    /**
+     * Set the Ip address UI text field to ipAddr.
+     *
+     * @param ipAddr the ip address string
+     */
+    public void setIpAddr(String ipAddr) {
+        mIpAddr.setText(ipAddr);
+    }
+}
diff --git a/src/com/android/settings/development/AdbWirelessDialogUiBase.java b/src/com/android/settings/development/AdbWirelessDialogUiBase.java
new file mode 100644
index 0000000..b2fc01c
--- /dev/null
+++ b/src/com/android/settings/development/AdbWirelessDialogUiBase.java
@@ -0,0 +1,126 @@
+/*
+ * 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.development;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.widget.Button;
+
+/**
+ * Foundation interface glues between Activities and UIs like {@link AdbWirelessDialog}.
+ */
+public interface AdbWirelessDialogUiBase {
+    /**
+     * Dialog shown when pairing a device via six-digit code.
+     */
+    int MODE_PAIRING = 0;
+    /**
+     *  Dialog shown when connecting to a paired device failed.
+     */
+    int MODE_CONNECTION_FAILED = 1;
+    /**
+     * Dialog shown when pairing failed.
+     */
+    int MODE_PAIRING_FAILED = 2;
+
+    /**
+     * Dialog shown when QR code pairing failed.
+     */
+    int MODE_QRCODE_FAILED = 3;
+
+    /**
+     * Gets the context for the dialog.
+     *
+     * @return the context for the dialog
+     */
+    Context getContext();
+
+    /**
+     * Gets the controller for the dialog.
+     *
+     * @return the controller for the dialog.
+     */
+    AdbWirelessDialogController getController();
+
+    /**
+     * Gets the layout for the dialog.
+     *
+     * @return the {@link LayoutInflater} for the dialog
+     */
+    LayoutInflater getLayoutInflater();
+
+    /**
+     * Gets the dialog mode/ID.
+     *
+     * @return the mode of the dialog
+     */
+    int getMode();
+
+    /**
+     * Sends a submit command to the dialog.
+     */
+    void dispatchSubmit();
+
+    /**
+     * Enables if user can cancel a dialog by clicking outside of the dialog.
+     *
+     * @param cancel The flag indicating if can cancel by clicking outside
+     */
+    void setCanceledOnTouchOutside(boolean cancel);
+
+    /**
+     * Sets the title of the dialog.
+     *
+     * @param id the string id
+     */
+    void setTitle(int id);
+
+    /**
+     * Sets the title of the dialog.
+     *
+     * @param title the title string
+     */
+    void setTitle(CharSequence title);
+
+    /**
+     * Sets the text for the submit button.
+     *
+     * @param text the submit text
+     */
+    void setSubmitButton(CharSequence text);
+
+    /**
+     * Sets the text for the cancel button.
+     *
+     * @param text the cancel text
+     */
+    void setCancelButton(CharSequence text);
+
+    /**
+     * Gets the button widget for the submit button.
+     *
+     * @return the submit {@link Button} widget
+     */
+    Button getSubmitButton();
+
+    /**
+     * Gets the button widget for the cancel button.
+     *
+     * @return the cancel {@link Button} widget
+     */
+    Button getCancelButton();
+}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 07e2ecd..f3f2a3a 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -443,6 +443,7 @@
         controllers.add(new SelectDSUPreferenceController(context));
         controllers.add(new AdbPreferenceController(context, fragment));
         controllers.add(new ClearAdbKeysPreferenceController(context, fragment));
+        controllers.add(new WirelessDebuggingPreferenceController(context, lifecycle));
         controllers.add(new LocalTerminalPreferenceController(context));
         controllers.add(new BugReportInPowerPreferenceController(context));
         controllers.add(new AutomaticSystemServerHeapDumpPreferenceController(context));
@@ -469,6 +470,7 @@
         controllers.add(new BluetoothMapVersionPreferenceController(context));
         controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
         controllers.add(new BluetoothMaxConnectedAudioDevicesPreferenceController(context));
+        controllers.add(new EnhancedConnectivityPreferenceController(context));
         controllers.add(new ShowTapsPreferenceController(context));
         controllers.add(new PointerLocationPreferenceController(context));
         controllers.add(new ShowSurfaceUpdatesPreferenceController(context));
diff --git a/src/com/android/settings/development/EnhancedConnectivityPreferenceController.java b/src/com/android/settings/development/EnhancedConnectivityPreferenceController.java
new file mode 100644
index 0000000..2dbd7d1
--- /dev/null
+++ b/src/com/android/settings/development/EnhancedConnectivityPreferenceController.java
@@ -0,0 +1,81 @@
+/*
+ * 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.development;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * Preference controller for Enhanced Connectivity feature
+ */
+public class EnhancedConnectivityPreferenceController extends
+        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
+        PreferenceControllerMixin {
+
+    private static final String ENHANCED_CONNECTIVITY_KEY = "enhanced_connectivity";
+
+    @VisibleForTesting
+    static final int ENHANCED_CONNECTIVITY_ON = 1;
+    // default is enhanced connectivity disabled.
+    @VisibleForTesting
+    static final int ENHANCED_CONNECTIVITY_OFF = 0;
+
+    public EnhancedConnectivityPreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object o) {
+        final boolean isEnabled = (Boolean) o;
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
+                isEnabled
+                        ? ENHANCED_CONNECTIVITY_ON
+                        : ENHANCED_CONNECTIVITY_OFF);
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return ENHANCED_CONNECTIVITY_KEY;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        final int enhancedConnectivityEnabled = Settings.Global.getInt(
+                mContext.getContentResolver(), Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
+                ENHANCED_CONNECTIVITY_OFF);
+        ((SwitchPreference) mPreference).setChecked(
+                enhancedConnectivityEnabled == ENHANCED_CONNECTIVITY_ON);
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        super.onDeveloperOptionsSwitchDisabled();
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
+                ENHANCED_CONNECTIVITY_OFF);
+        ((SwitchPreference) mPreference).setChecked(false);
+    }
+}
diff --git a/src/com/android/settings/development/WirelessDebuggingEnabler.java b/src/com/android/settings/development/WirelessDebuggingEnabler.java
new file mode 100644
index 0000000..51b81f6
--- /dev/null
+++ b/src/com/android/settings/development/WirelessDebuggingEnabler.java
@@ -0,0 +1,140 @@
+/*
+ * 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.development;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.settings.widget.SwitchWidgetController;
+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;
+
+/**
+ * Class to control the switch bar in the wireless debugging fragment.
+ */
+public class WirelessDebuggingEnabler implements SwitchWidgetController.OnSwitchChangeListener,
+        LifecycleObserver, OnResume, OnPause {
+    private static final String TAG = "WirelessDebuggingEnabler";
+
+    private final SwitchWidgetController mSwitchWidget;
+    private Context mContext;
+    private boolean mListeningToOnSwitchChange = false;
+    private OnEnabledListener mListener;
+    private final ContentResolver mContentResolver;
+    private final ContentObserver mSettingsObserver;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    public WirelessDebuggingEnabler(Context context, SwitchWidgetController switchWidget,
+            OnEnabledListener listener, Lifecycle lifecycle) {
+        mContext = context;
+        mSwitchWidget = switchWidget;
+        mSwitchWidget.setListener(this);
+        mSwitchWidget.setupView();
+
+        mListener = listener;
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+
+        mContentResolver = context.getContentResolver();
+        mSettingsObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                Log.i(TAG, "ADB_WIFI_ENABLED=" + isAdbWifiEnabled());
+                onWirelessDebuggingEnabled(isAdbWifiEnabled());
+            }
+        };
+    }
+
+    private boolean isAdbWifiEnabled() {
+        return Settings.Global.getInt(mContentResolver, Settings.Global.ADB_WIFI_ENABLED,
+                AdbPreferenceController.ADB_SETTING_OFF)
+                != AdbPreferenceController.ADB_SETTING_OFF;
+    }
+
+    /**
+     * Tears down the switch controller for the wireless debugging switch.
+     */
+    public void teardownSwitchController() {
+        if (mListeningToOnSwitchChange) {
+            mSwitchWidget.stopListening();
+            mListeningToOnSwitchChange = false;
+        }
+        mSwitchWidget.teardownView();
+    }
+
+    @Override
+    public void onResume() {
+        if (!mListeningToOnSwitchChange) {
+            mSwitchWidget.startListening();
+            mListeningToOnSwitchChange = true;
+        }
+        onWirelessDebuggingEnabled(isAdbWifiEnabled());
+        mContentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED), false,
+                mSettingsObserver);
+    }
+
+    @Override
+    public void onPause() {
+        if (mListeningToOnSwitchChange) {
+            mSwitchWidget.stopListening();
+            mListeningToOnSwitchChange = false;
+        }
+        mContentResolver.unregisterContentObserver(mSettingsObserver);
+    }
+
+    private void onWirelessDebuggingEnabled(boolean enabled) {
+        mSwitchWidget.setChecked(enabled);
+        if (mListener != null) {
+            mListener.onEnabled(enabled);
+        }
+    }
+
+    protected void writeAdbWifiSetting(boolean enabled) {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_WIFI_ENABLED, enabled ? AdbPreferenceController.ADB_SETTING_ON
+                : AdbPreferenceController.ADB_SETTING_OFF);
+    }
+
+    @Override
+    public boolean onSwitchToggled(boolean isChecked) {
+        writeAdbWifiSetting(isChecked);
+        return true;
+    }
+
+    /**
+     * Interface for subscribers to implement in order to listen for
+     * wireless debugging state changes.
+     */
+    public interface OnEnabledListener {
+        /**
+         * Called when wireless debugging state changes.
+         *
+         * @param enabled the state of wireless debugging
+         */
+        void onEnabled(boolean enabled);
+    }
+}
diff --git a/src/com/android/settings/development/WirelessDebuggingFragment.java b/src/com/android/settings/development/WirelessDebuggingFragment.java
new file mode 100644
index 0000000..2edadcc
--- /dev/null
+++ b/src/com/android/settings/development/WirelessDebuggingFragment.java
@@ -0,0 +1,495 @@
+/*
+ * 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.development;
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.debug.AdbManager;
+import android.debug.IAdbManager;
+import android.debug.PairDevice;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.widget.SwitchBarController;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.development.DevelopmentSettingsEnabler;
+import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.widget.FooterPreference;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Fragment shown when clicking in the "Wireless Debugging" preference in
+ * the developer options.
+ */
+@SearchIndexable
+public class WirelessDebuggingFragment extends DashboardFragment
+        implements WirelessDebuggingEnabler.OnEnabledListener {
+
+    private static final String TAG = "WirelessDebuggingFrag";
+
+    // Activity result from clicking on a paired device.
+    private static final int PAIRED_DEVICE_REQUEST = 0;
+    public static final String PAIRED_DEVICE_REQUEST_TYPE = "request_type";
+    public static final int FORGET_ACTION = 0;
+
+    // Activity result from pairing a device.
+    private static final int PAIRING_DEVICE_REQUEST = 1;
+    public static final String PAIRING_DEVICE_REQUEST_TYPE = "request_type_pairing";
+    public static final int SUCCESS_ACTION = 0;
+    public static final int FAIL_ACTION = 1;
+
+    public static final String PAIRED_DEVICE_EXTRA = "paired_device";
+    public static final String DEVICE_NAME_EXTRA = "device_name";
+    public static final String IP_ADDR_EXTRA = "ip_addr";
+
+    private WirelessDebuggingEnabler mWifiDebuggingEnabler;
+
+    private static AdbIpAddressPreferenceController sAdbIpAddressPreferenceController;
+    // UI components
+    private static final String PREF_KEY_ADB_DEVICE_NAME = "adb_device_name_pref";
+    private static final String PREF_KEY_ADB_IP_ADDR = "adb_ip_addr_pref";
+    private static final String PREF_KEY_PAIRING_METHODS_CATEGORY = "adb_pairing_methods_category";
+    private static final String PREF_KEY_ADB_QRCODE_PAIRING = "adb_pair_method_qrcode_pref";
+    private static final String PREF_KEY_ADB_CODE_PAIRING = "adb_pair_method_code_pref";
+    private static final String PREF_KEY_PAIRED_DEVICES_CATEGORY = "adb_paired_devices_category";
+    private static final String PREF_KEY_FOOTER_CATEGORY = "adb_wireless_footer_category";
+
+    private Preference mDeviceNamePreference;
+    private Preference mIpAddrPreference;
+
+    private PreferenceCategory mPairingMethodsCategory;
+    private Preference mQrcodePairingPreference;
+    private Preference mCodePairingPreference;
+
+    private PreferenceCategory mPairedDevicesCategory;
+
+    private PreferenceCategory mFooterCategory;
+    private FooterPreference mOffMessagePreference;
+
+    // Map of paired devices, with the device GUID is the key
+    private Map<String, AdbPairedDevicePreference> mPairedDevicePreferences;
+
+    private IAdbManager mAdbManager;
+    private int mConnectionPort;
+
+    class PairingCodeDialogListener implements AdbWirelessDialog.AdbWirelessDialogListener {
+        @Override
+        public void onDismiss() {
+            Log.i(TAG, "onDismiss");
+            mPairingCodeDialog = null;
+            try {
+                mAdbManager.disablePairing();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to cancel pairing");
+            }
+        }
+    }
+    final PairingCodeDialogListener mPairingCodeDialogListener = new PairingCodeDialogListener();
+    AdbWirelessDialog mPairingCodeDialog;
+
+    private IntentFilter mIntentFilter;
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION.equals(action)) {
+                Map<String, PairDevice> newPairedDevicesList =
+                        (HashMap<String, PairDevice>) intent.getSerializableExtra(
+                            AdbManager.WIRELESS_DEVICES_EXTRA);
+                updatePairedDevicePreferences(newPairedDevicesList);
+            } else if (AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION.equals(action)) {
+                int status = intent.getIntExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                        AdbManager.WIRELESS_STATUS_DISCONNECTED);
+                if (status == AdbManager.WIRELESS_STATUS_CONNECTED) {
+                    int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
+                    Log.i(TAG, "Got adbwifi port=" + port);
+                } else {
+                    Log.i(TAG, "adbwifi server disconnected");
+                }
+            } else if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
+                Integer res = intent.getIntExtra(
+                        AdbManager.WIRELESS_STATUS_EXTRA,
+                        AdbManager.WIRELESS_STATUS_FAIL);
+
+                if (res.equals(AdbManager.WIRELESS_STATUS_PAIRING_CODE)) {
+                    String pairingCode = intent.getStringExtra(
+                                AdbManager.WIRELESS_PAIRING_CODE_EXTRA);
+                    if (mPairingCodeDialog != null) {
+                        mPairingCodeDialog.getController().setPairingCode(pairingCode);
+                    }
+                } else if (res.equals(AdbManager.WIRELESS_STATUS_SUCCESS)) {
+                    removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
+                    mPairingCodeDialog = null;
+                } else if (res.equals(AdbManager.WIRELESS_STATUS_FAIL)) {
+                    removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
+                    mPairingCodeDialog = null;
+                    showDialog(AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
+                } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
+                    int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
+                    Log.i(TAG, "Got pairing code port=" + port);
+                    String ipAddr = sAdbIpAddressPreferenceController.getIpv4Address() + ":" + port;
+                    if (mPairingCodeDialog != null) {
+                        mPairingCodeDialog.getController().setIpAddr(ipAddr);
+                    }
+                }
+            }
+        }
+    };
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        final SettingsActivity activity = (SettingsActivity) getActivity();
+        mWifiDebuggingEnabler =  new WirelessDebuggingEnabler(activity,
+                new SwitchBarController(activity.getSwitchBar()), this,
+                getSettingsLifecycle());
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        addPreferences();
+        mIntentFilter = new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+        mIntentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+        mIntentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+    }
+
+    private void addPreferences() {
+        mDeviceNamePreference =
+            (Preference) findPreference(PREF_KEY_ADB_DEVICE_NAME);
+        mIpAddrPreference =
+            (Preference) findPreference(PREF_KEY_ADB_IP_ADDR);
+        mPairingMethodsCategory =
+                (PreferenceCategory) findPreference(PREF_KEY_PAIRING_METHODS_CATEGORY);
+        mCodePairingPreference =
+                (Preference) findPreference(PREF_KEY_ADB_CODE_PAIRING);
+        mCodePairingPreference.setOnPreferenceClickListener(preference -> {
+            showDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
+            return true;
+        });
+        mQrcodePairingPreference =
+                (Preference) findPreference(PREF_KEY_ADB_QRCODE_PAIRING);
+        mQrcodePairingPreference.setOnPreferenceClickListener(preference -> {
+            launchQrcodeScannerFragment();
+            return true;
+        });
+
+        mPairedDevicesCategory =
+                (PreferenceCategory) findPreference(PREF_KEY_PAIRED_DEVICES_CATEGORY);
+        mFooterCategory =
+                (PreferenceCategory) findPreference(PREF_KEY_FOOTER_CATEGORY);
+
+        mOffMessagePreference =
+                new FooterPreference(mFooterCategory.getContext());
+        final CharSequence title = getText(R.string.adb_wireless_list_empty_off);
+        mOffMessagePreference.setTitle(title);
+        mFooterCategory.addPreference(mOffMessagePreference);
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+
+        mWifiDebuggingEnabler.teardownSwitchController();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        getActivity().registerReceiver(mReceiver, mIntentFilter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+
+        removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
+        getActivity().unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (requestCode == PAIRED_DEVICE_REQUEST) {
+            handlePairedDeviceRequest(resultCode, data);
+        } else if (requestCode == PAIRING_DEVICE_REQUEST) {
+            handlePairingDeviceRequest(resultCode, data);
+        }
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.SETTINGS_ADB_WIRELESS;
+    }
+
+    @Override
+    public int getDialogMetricsCategory(int dialogId) {
+        return SettingsEnums.ADB_WIRELESS_DEVICE_PAIRING_DIALOG;
+    }
+
+    @Override
+    public Dialog onCreateDialog(int dialogId) {
+        Dialog d = AdbWirelessDialog.createModal(getActivity(),
+                dialogId == AdbWirelessDialogUiBase.MODE_PAIRING
+                    ? mPairingCodeDialogListener : null, dialogId);
+        if (dialogId == AdbWirelessDialogUiBase.MODE_PAIRING) {
+            mPairingCodeDialog = (AdbWirelessDialog) d;
+            try {
+                mAdbManager.enablePairingByPairingCode();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to enable pairing");
+                mPairingCodeDialog = null;
+                d = AdbWirelessDialog.createModal(getActivity(), null,
+                        AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
+            }
+        }
+        if (d != null) {
+            return d;
+        }
+        return super.onCreateDialog(dialogId);
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.adb_wireless_settings;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+        return buildPreferenceControllers(context, getActivity(), this /* fragment */,
+                getSettingsLifecycle());
+    }
+
+    private static List<AbstractPreferenceController> buildPreferenceControllers(
+            Context context, Activity activity, WirelessDebuggingFragment fragment,
+            Lifecycle lifecycle) {
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        sAdbIpAddressPreferenceController =
+                new AdbIpAddressPreferenceController(context, lifecycle);
+        controllers.add(sAdbIpAddressPreferenceController);
+
+        return controllers;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    public void onEnabled(boolean enabled) {
+        if (enabled) {
+            showDebuggingPreferences();
+            mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(
+                    Context.ADB_SERVICE));
+            try {
+                Map<String, PairDevice> newList = mAdbManager.getPairedDevices();
+                updatePairedDevicePreferences(newList);
+                mConnectionPort = mAdbManager.getAdbWirelessPort();
+                if (mConnectionPort > 0) {
+                    Log.i(TAG, "onEnabled(): connect_port=" + mConnectionPort);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to request the paired list for Adb wireless");
+            }
+            sAdbIpAddressPreferenceController.updateState(mIpAddrPreference);
+        } else {
+            showOffMessage();
+        }
+    }
+
+    private void showOffMessage() {
+        mDeviceNamePreference.setVisible(false);
+        mIpAddrPreference.setVisible(false);
+        mPairingMethodsCategory.setVisible(false);
+        mPairedDevicesCategory.setVisible(false);
+        mFooterCategory.setVisible(true);
+    }
+
+    private void showDebuggingPreferences() {
+        mDeviceNamePreference.setVisible(true);
+        mIpAddrPreference.setVisible(true);
+        mPairingMethodsCategory.setVisible(true);
+        mPairedDevicesCategory.setVisible(true);
+        mFooterCategory.setVisible(false);
+    }
+
+    private void updatePairedDevicePreferences(Map<String, PairDevice> newList) {
+        // TODO(joshuaduong): Move the non-UI stuff into another thread
+        // as the processing could take some time.
+        if (newList == null) {
+            mPairedDevicesCategory.removeAll();
+            return;
+        }
+        if (mPairedDevicePreferences == null) {
+            mPairedDevicePreferences = new HashMap<String, AdbPairedDevicePreference>();
+        }
+        if (mPairedDevicePreferences.isEmpty()) {
+            for (Map.Entry<String, PairDevice> entry : newList.entrySet()) {
+                AdbPairedDevicePreference p =
+                        new AdbPairedDevicePreference(entry.getValue(),
+                            mPairedDevicesCategory.getContext());
+                mPairedDevicePreferences.put(
+                        entry.getKey(),
+                        p);
+                p.setOnPreferenceClickListener(preference -> {
+                    AdbPairedDevicePreference pref =
+                            (AdbPairedDevicePreference) preference;
+                    launchPairedDeviceDetailsFragment(pref);
+                    return true;
+                });
+                mPairedDevicesCategory.addPreference(p);
+            }
+        } else {
+            // Remove any devices no longer on the newList
+            mPairedDevicePreferences.entrySet().removeIf(entry -> {
+                if (newList.get(entry.getKey()) == null) {
+                    mPairedDevicesCategory.removePreference(entry.getValue());
+                    return true;
+                } else {
+                    // It is in the newList. Just update the PairDevice value
+                    AdbPairedDevicePreference p =
+                            entry.getValue();
+                    p.setPairedDevice(newList.get(entry.getKey()));
+                    p.refresh();
+                    return false;
+                }
+            });
+            // Add new devices if any.
+            for (Map.Entry<String, PairDevice> entry :
+                    newList.entrySet()) {
+                if (mPairedDevicePreferences.get(entry.getKey()) == null) {
+                    AdbPairedDevicePreference p =
+                            new AdbPairedDevicePreference(entry.getValue(),
+                                mPairedDevicesCategory.getContext());
+                    mPairedDevicePreferences.put(
+                            entry.getKey(),
+                            p);
+                    p.setOnPreferenceClickListener(preference -> {
+                        AdbPairedDevicePreference pref =
+                                (AdbPairedDevicePreference) preference;
+                        launchPairedDeviceDetailsFragment(pref);
+                        return true;
+                    });
+                    mPairedDevicesCategory.addPreference(p);
+                }
+            }
+        }
+    }
+
+    private void launchPairedDeviceDetailsFragment(AdbPairedDevicePreference p) {
+        // For sending to the device details fragment.
+        p.savePairedDeviceToExtras(p.getExtras());
+        new SubSettingLauncher(getContext())
+                .setTitleRes(R.string.adb_wireless_device_details_title)
+                .setDestination(AdbDeviceDetailsFragment.class.getName())
+                .setArguments(p.getExtras())
+                .setSourceMetricsCategory(getMetricsCategory())
+                .setResultListener(this, PAIRED_DEVICE_REQUEST)
+                .launch();
+    }
+
+    void handlePairedDeviceRequest(int result, Intent data) {
+        if (result != Activity.RESULT_OK) {
+            return;
+        }
+
+        Log.i(TAG, "Processing paired device request");
+        int requestType = data.getIntExtra(PAIRED_DEVICE_REQUEST_TYPE, -1);
+
+        PairDevice p;
+
+        switch (requestType) {
+            case FORGET_ACTION:
+                try {
+                    p = (PairDevice) data.getParcelableExtra(PAIRED_DEVICE_EXTRA);
+                    mAdbManager.unpairDevice(p.getGuid());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to forget the device");
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    void handlePairingDeviceRequest(int result, Intent data) {
+        if (result != Activity.RESULT_OK) {
+            return;
+        }
+
+        int requestType = data.getIntExtra(PAIRING_DEVICE_REQUEST_TYPE, -1);
+        switch (requestType) {
+            case FAIL_ACTION:
+                showDialog(AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
+                break;
+            default:
+                break;
+        }
+    }
+
+    private String getDeviceName() {
+        // Keep device name in sync with Settings > About phone > Device name
+        String deviceName = Settings.Global.getString(getContext().getContentResolver(),
+                Settings.Global.DEVICE_NAME);
+        if (deviceName == null) {
+            deviceName = Build.MODEL;
+        }
+        return deviceName;
+    }
+
+    private void launchQrcodeScannerFragment() {
+        new SubSettingLauncher(getContext())
+                .setDestination(AdbQrcodeScannerFragment.class.getName())
+                .setSourceMetricsCategory(getMetricsCategory())
+                .setResultListener(this, PAIRING_DEVICE_REQUEST)
+                .launch();
+    }
+
+    public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider(R.xml.development_tile_settings) {
+
+                @Override
+                protected boolean isPageSearchEnabled(Context context) {
+                    return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context);
+                }
+            };
+}
diff --git a/src/com/android/settings/development/WirelessDebuggingPreferenceController.java b/src/com/android/settings/development/WirelessDebuggingPreferenceController.java
new file mode 100644
index 0000000..81575d2
--- /dev/null
+++ b/src/com/android/settings/development/WirelessDebuggingPreferenceController.java
@@ -0,0 +1,143 @@
+/*
+ * 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.development;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.debug.IAdbManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.MasterSwitchPreference;
+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 com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * This controls the master switch controller in the developer options page for
+ * "Wireless debugging".
+ */
+public class WirelessDebuggingPreferenceController extends DeveloperOptionsPreferenceController
+        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin,
+        LifecycleObserver, OnResume, OnPause {
+    private static final String TAG = "WirelessDebugPrefCtrl";
+    private final IAdbManager mAdbManager;
+    private final ContentResolver mContentResolver;
+    private final ContentObserver mSettingsObserver;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    public static final String KEY_TOGGLE_ADB_WIRELESS = "toggle_adb_wireless";
+
+    public WirelessDebuggingPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+        mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(Context.ADB_SERVICE));
+        mSettingsObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                updateState(mPreference);
+            }
+        };
+        mContentResolver = context.getContentResolver();
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        try {
+            return mAdbManager.isAdbWifiSupported();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to check if adb wifi is supported.", e);
+        }
+
+        return false;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_TOGGLE_ADB_WIRELESS;
+    }
+
+    /**
+     * Called when developer options is enabled and the preference is available
+     */
+    @Override
+    protected void onDeveloperOptionsSwitchEnabled() {
+        super.onDeveloperOptionsSwitchEnabled();
+        mPreference.setEnabled(true);
+    }
+
+    /**
+     * Called when developer options is disabled and the preference is available
+     */
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        super.onDeveloperOptionsSwitchDisabled();
+        mPreference.setEnabled(false);
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_WIFI_ENABLED,
+                AdbPreferenceController.ADB_SETTING_OFF);
+    }
+
+    @Override
+    public void onResume() {
+        mContentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED), false,
+                mSettingsObserver);
+    }
+
+    @Override
+    public void onPause() {
+        mContentResolver.unregisterContentObserver(mSettingsObserver);
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        boolean enabled = Settings.Global.getInt(mContentResolver,
+                Settings.Global.ADB_WIFI_ENABLED, AdbPreferenceController.ADB_SETTING_OFF)
+                    != AdbPreferenceController.ADB_SETTING_OFF;
+        ((MasterSwitchPreference) preference).setChecked(enabled);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean enabled = (Boolean) newValue;
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_WIFI_ENABLED,
+                enabled ? AdbPreferenceController.ADB_SETTING_ON
+                : AdbPreferenceController.ADB_SETTING_OFF);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java
index 9627eb0..75ec651 100644
--- a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java
+++ b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java
@@ -26,12 +26,16 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.StrictMode;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.settings.R;
 import com.android.settingslib.utils.ThreadUtils;
 
+import java.util.Map;
+
 /**
  * Provider stores and manages user interaction feedback for homepage contextual cards.
  */
@@ -40,10 +44,10 @@
     public static final String CARD_AUTHORITY = "com.android.settings.homepage.CardContentProvider";
 
     public static final Uri REFRESH_CARD_URI = new Uri.Builder()
-                    .scheme(ContentResolver.SCHEME_CONTENT)
-                    .authority(CardContentProvider.CARD_AUTHORITY)
-                    .appendPath(CardDatabaseHelper.CARD_TABLE)
-                    .build();
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(CardContentProvider.CARD_AUTHORITY)
+            .appendPath(CardDatabaseHelper.CARD_TABLE)
+            .build();
 
     public static final Uri DELETE_CARD_URI = new Uri.Builder()
             .scheme(ContentResolver.SCHEME_CONTENT)
@@ -81,6 +85,9 @@
         final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
         int numInserted = 0;
         final SQLiteDatabase database = mDBHelper.getWritableDatabase();
+        final boolean keepDismissalTimestampBeforeDeletion = getContext().getResources()
+                .getBoolean(R.bool.config_keep_contextual_card_dismissal_timestamp);
+        final Map<String, Long> dismissedTimeMap = new ArrayMap<>();
 
         try {
             maybeEnableStrictMode();
@@ -88,9 +95,42 @@
             final String table = getTableFromMatch(uri);
             database.beginTransaction();
 
-            // Here deletion first is avoiding redundant insertion. According to cl/215350754
+            if (keepDismissalTimestampBeforeDeletion) {
+                // Query the existing db and get dismissal info.
+                final String[] columns = new String[]{CardDatabaseHelper.CardColumns.NAME,
+                        CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP};
+                final String selection =
+                        CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " IS NOT NULL";
+                try (Cursor cursor = database.query(table, columns, selection,
+                        null/* selectionArgs */, null /* groupBy */,
+                        null /* having */, null /* orderBy */)) {
+                    // Save them to a Map
+                    for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+                        final String cardName = cursor.getString(cursor.getColumnIndex(
+                                CardDatabaseHelper.CardColumns.NAME));
+                        final long timestamp = cursor.getLong(cursor.getColumnIndex(
+                                CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP));
+                        dismissedTimeMap.put(cardName, timestamp);
+                    }
+                }
+            }
+
+            // Here delete data first to avoid redundant insertion. According to cl/215350754
             database.delete(table, null /* whereClause */, null /* whereArgs */);
+
             for (ContentValues value : values) {
+                if (keepDismissalTimestampBeforeDeletion) {
+                    // Replace dismissedTimestamp in each value if there is an old one.
+                    final String cardName =
+                            value.get(CardDatabaseHelper.CardColumns.NAME).toString();
+                    if (dismissedTimeMap.containsKey(cardName)) {
+                        // Replace the value of dismissedTimestamp
+                        value.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP,
+                                dismissedTimeMap.get(cardName));
+                        Log.d(TAG, "Replace dismissed time: " + cardName);
+                    }
+                }
+
                 long ret = database.insert(table, null /* nullColumnHack */, value);
                 if (ret != -1L) {
                     numInserted++;
diff --git a/src/com/android/settings/wifi/dpp/AdbQrCode.java b/src/com/android/settings/wifi/dpp/AdbQrCode.java
new file mode 100644
index 0000000..fb63e0a
--- /dev/null
+++ b/src/com/android/settings/wifi/dpp/AdbQrCode.java
@@ -0,0 +1,56 @@
+/*
+ * 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.wifi.dpp;
+
+import android.text.TextUtils;
+
+/**
+ * Extension of WifiQrCode to support ADB QR code format.
+ * It will be based on the ZXing format:
+ *
+ * WIFI:T:ADB;S:myname;P:mypassword;;
+ */
+public class AdbQrCode extends WifiQrCode {
+    static final String SECURITY_ADB = "ADB";
+
+    private WifiNetworkConfig mAdbConfig;
+
+    public AdbQrCode(String qrCode) throws IllegalArgumentException {
+        super(qrCode);
+
+        // Only accept the zxing format.
+        if (!WifiQrCode.SCHEME_ZXING_WIFI_NETWORK_CONFIG.equals(getScheme())) {
+            throw new IllegalArgumentException("DPP format not supported for ADB QR code");
+        }
+
+        mAdbConfig = getWifiNetworkConfig();
+        if (!SECURITY_ADB.equals(mAdbConfig.getSecurity())) {
+            throw new IllegalArgumentException("Invalid security type");
+        }
+
+        if (TextUtils.isEmpty(mAdbConfig.getSsid())) {
+            throw new IllegalArgumentException("Empty service name");
+        }
+
+        if (TextUtils.isEmpty(mAdbConfig.getPreSharedKey())) {
+            throw new IllegalArgumentException("Empty password");
+        }
+    }
+
+    public WifiNetworkConfig getAdbNetworkConfig() {
+        return mAdbConfig;
+    }
+}
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index dc638a5..c7173bc 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -72,6 +72,8 @@
     <!-- Whether or not extra preview panels should be used for screen zoom setting. -->
     <bool name="config_enable_extra_screen_zoom_preview">false</bool>
 
+    <!-- Whether dismissal timestamp should be kept before deletion -->
+    <bool name="config_keep_contextual_card_dismissal_timestamp">true</bool>
 
     <!-- List of a11y components on the device allowed to be enabled by Settings Slices -->
     <string-array name="config_settings_slices_accessibility_components" translatable="false">
diff --git a/tests/robotests/src/com/android/settings/SettingsActivityTest.java b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
index feb48ab..ada4c0e 100644
--- a/tests/robotests/src/com/android/settings/SettingsActivityTest.java
+++ b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
@@ -20,7 +20,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -82,7 +82,7 @@
     public void setTaskDescription_shouldUpdateIcon() {
         mActivity.setTaskDescription(mTaskDescription);
 
-        verify(mTaskDescription).setIcon(anyInt());
+        verify(mTaskDescription).setIcon(any());
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/accessibility/HtmlTextPreferenceTest.java b/tests/robotests/src/com/android/settings/accessibility/HtmlTextPreferenceTest.java
index 5ac5bad..63c7da1 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HtmlTextPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HtmlTextPreferenceTest.java
@@ -23,7 +23,6 @@
 import android.text.Html;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.TextView;
 
 import androidx.preference.PreferenceViewHolder;
 
@@ -36,9 +35,6 @@
 import org.robolectric.RuntimeEnvironment;
 import org.xml.sax.XMLReader;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /** Tests for {@link HtmlTextPreference} */
 @RunWith(RobolectricTestRunner.class)
 public final class HtmlTextPreferenceTest {
@@ -65,21 +61,13 @@
     }
 
     @Test
-    public void testUnsupportedTagList_keepRealContentWithoutTag() {
-        final List<String> testUnsupportedTagList = new ArrayList<>();
-        testUnsupportedTagList.add("testTag");
+    public void testTagHandler() {
         final String testStr = "<testTag>Real description</testTag>";
-        final String expectedStr = "Real description";
-        final String expectedTag = "unsupportedtag1";
 
-        mHtmlTextPreference.setUnsupportedTagList(testUnsupportedTagList);
         mHtmlTextPreference.setSummary(testStr);
         mHtmlTextPreference.setTagHandler(mTagHandler);
         mHtmlTextPreference.onBindViewHolder(mPreferenceViewHolder);
 
-        final TextView summaryView = mPreferenceViewHolder.itemView.findViewById(
-                android.R.id.summary);
-        assertThat(summaryView.getText().toString()).isEqualTo(expectedStr);
-        assertThat(mHandledTag).isEqualTo(expectedTag);
+        assertThat(mHandledTag).isEqualTo("testTag");
     }
 }
diff --git a/tests/robotests/src/com/android/settings/development/EnhancedConnectivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/EnhancedConnectivityPreferenceControllerTest.java
new file mode 100644
index 0000000..6fd6f55
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/EnhancedConnectivityPreferenceControllerTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class EnhancedConnectivityPreferenceControllerTest {
+    @Mock
+    private Context mContext;
+
+    @Mock
+    private SwitchPreference mPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private EnhancedConnectivityPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mController = new EnhancedConnectivityPreferenceController(mContext);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+                .thenReturn(mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void onPreferenceChanged_enhanceConnectivity_shouldBeOn() {
+        mController.onPreferenceChange(mPreference, true /* new value */);
+
+        assertThat(isSettingEnabled()).isTrue();
+    }
+
+    @Test
+    public void onPreferenceChanged_enhanceConnectivity_shouldBeOff() {
+        mController.onPreferenceChange(mPreference, false /* new value */);
+
+        assertThat(isSettingEnabled()).isFalse();
+    }
+
+    @Test
+    public void updateState_preferenceShouldBeChecked() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ENHANCED_CONNECTIVITY_ENABLED, 1 /* enabled */);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(true);
+    }
+
+    @Test
+    public void updateState_preferenceShouldNotBeChecked() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ENHANCED_CONNECTIVITY_ENABLED, 0 /* disabled */);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void onDeveloperOptionsDisabled_shouldDisablePreference() {
+        mController.onDeveloperOptionsDisabled();
+
+        verify(mPreference).setEnabled(false);
+        verify(mPreference).setChecked(false);
+
+        assertThat(isSettingEnabled()).isFalse();
+    }
+
+    private boolean isSettingEnabled() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
+                EnhancedConnectivityPreferenceController.ENHANCED_CONNECTIVITY_OFF
+                /* default off */)
+                == EnhancedConnectivityPreferenceController.ENHANCED_CONNECTIVITY_ON;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/WirelessDebuggingEnablerTest.java b/tests/robotests/src/com/android/settings/development/WirelessDebuggingEnablerTest.java
new file mode 100644
index 0000000..7f4f530
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/WirelessDebuggingEnablerTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.provider.Settings.Global;
+
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.settings.SettingsActivity;
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settings.widget.SwitchBar;
+import com.android.settings.widget.SwitchBarController;
+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.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowUtils.class)
+public class WirelessDebuggingEnablerTest {
+
+    @Mock
+    private SettingsActivity mActivity;
+    @Mock
+    private SwitchBar mSwitchBar;
+    @Mock
+    private WirelessDebuggingEnabler.OnEnabledListener mListener;
+
+    private WirelessDebuggingEnabler mWirelessDebuggingEnabler;
+    private Context mContext;
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+        mWirelessDebuggingEnabler = spy(new WirelessDebuggingEnabler(
+                mContext, new SwitchBarController(mSwitchBar), mListener, mLifecycle));
+    }
+
+    @Test
+    public void onCreation_shouldShowSwitchBar() {
+        verify(mSwitchBar).show();
+    }
+
+    @Test
+    public void teardownSwitchController_shouldHideSwitchBar() {
+        mWirelessDebuggingEnabler.teardownSwitchController();
+
+        verify(mSwitchBar).hide();
+    }
+
+    @Test
+    public void adbWifiEnabled_switchBarShouldBeChecked() {
+        // Set to disabled first otherwise we won't get the onChange() event
+        Global.putInt(mContext.getContentResolver(),
+                Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
+        mWirelessDebuggingEnabler.onResume();
+
+        verify(mSwitchBar).setChecked(false);
+        verify(mListener).onEnabled(false);
+
+        Global.putInt(mContext.getContentResolver(),
+                Global.ADB_WIFI_ENABLED, 1 /* setting enabled */);
+        final ContentObserver observer =
+                ReflectionHelpers.getField(mWirelessDebuggingEnabler, "mSettingsObserver");
+        observer.onChange(true, Global.getUriFor(Global.ADB_WIFI_ENABLED));
+
+        verify(mSwitchBar).setChecked(true);
+        // Should also get a callback
+        verify(mListener).onEnabled(true);
+    }
+
+    @Test
+    public void adbWifiEnabled_switchBarShouldNotBeChecked() {
+        Global.putInt(mContext.getContentResolver(),
+                Global.ADB_WIFI_ENABLED, 1 /* setting enabled */);
+        mWirelessDebuggingEnabler.onResume();
+
+        verify(mSwitchBar).setChecked(true);
+        verify(mListener).onEnabled(true);
+
+        Global.putInt(mContext.getContentResolver(),
+                Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
+        final ContentObserver observer =
+                ReflectionHelpers.getField(mWirelessDebuggingEnabler, "mSettingsObserver");
+        observer.onChange(true, Global.getUriFor(Global.ADB_WIFI_ENABLED));
+
+        verify(mSwitchBar).setChecked(false);
+        // Should also get a callback
+        verify(mListener).onEnabled(false);
+    }
+
+    @Test
+    public void onSwitchToggled_true_shouldSetAdbWifiEnabledTrue() {
+        Global.putInt(mContext.getContentResolver(),
+                Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
+        mWirelessDebuggingEnabler.onResume();
+
+        verify(mSwitchBar).setChecked(false);
+        verify(mListener).onEnabled(false);
+
+        mWirelessDebuggingEnabler.onSwitchToggled(true);
+
+        verify(mSwitchBar).setChecked(true);
+        verify(mListener).onEnabled(true);
+        assertThat(Global.getInt(mContext.getContentResolver(),
+                Global.ADB_WIFI_ENABLED, -1)).isEqualTo(1);
+        // Should also get a callback
+    }
+
+    @Test
+    public void onSwitchToggled_false_shouldSetAdbWifiEnabledFalse() {
+        Global.putInt(mContext.getContentResolver(),
+                Global.ADB_WIFI_ENABLED, 1 /* setting disabled */);
+        mWirelessDebuggingEnabler.onResume();
+
+        verify(mSwitchBar).setChecked(true);
+        verify(mListener).onEnabled(true);
+
+        mWirelessDebuggingEnabler.onSwitchToggled(false);
+
+        verify(mSwitchBar).setChecked(false);
+        verify(mListener).onEnabled(false);
+        assertThat(Global.getInt(mContext.getContentResolver(),
+            Global.ADB_WIFI_ENABLED, -1)).isEqualTo(0);
+        // Should also get a callback
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/WirelessDebuggingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/WirelessDebuggingPreferenceControllerTest.java
new file mode 100644
index 0000000..f755ec4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/WirelessDebuggingPreferenceControllerTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.debug.IAdbManager;
+import android.os.RemoteException;
+import android.provider.Settings.Global;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settings.widget.MasterSwitchPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowUtils.class)
+public class WirelessDebuggingPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private MasterSwitchPreference mPreference;
+    @Mock
+    private IAdbManager mAdbManager;
+    @Mock
+    private DevelopmentSettingsDashboardFragment mFragment;
+
+    private WirelessDebuggingPreferenceController mController;
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+    private Context mContext;
+    private ContentResolver mContentResolver;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mContentResolver = mContext.getContentResolver();
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+        mController = spy(new WirelessDebuggingPreferenceController(mContext, mLifecycle));
+        ReflectionHelpers.setField(mController, "mAdbManager", mAdbManager);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void isAvailable_isAdbWifiSupported_yes_shouldBeTrue() throws RemoteException {
+        when(mAdbManager.isAdbWifiSupported()).thenReturn(true);
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_isAdbWifiSupported_shouldBeFalse() throws RemoteException {
+        when(mAdbManager.isAdbWifiSupported()).thenReturn(false);
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void updateState_adbWifiEnabled_preferenceShouldBeChecked() {
+        Global.putInt(mContentResolver,
+                Global.ADB_WIFI_ENABLED, 1 /* setting enabled */);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(true);
+    }
+
+    @Test
+    public void updateState_adbWifiDisabled_preferenceShouldNotBeChecked() {
+        Global.putInt(mContentResolver,
+                Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void onPreferenceChange_turnOn_adbWifiEnabledTrue() {
+        mController.onPreferenceChange(null, true);
+
+        assertThat(Global.getInt(mContentResolver, Global.ADB_WIFI_ENABLED, -1)).isEqualTo(1);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java
index d13c97c..32af5d8 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java
@@ -38,6 +38,7 @@
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
 @RunWith(RobolectricTestRunner.class)
@@ -86,6 +87,25 @@
     }
 
     @Test
+    @Config(qualifiers = "mcc999")
+    public void bulkInsert_keepDismissalTimestamp_shouldHaveTimestamp() {
+        mResolver.bulkInsert(mUri, generateTwoRowsWithDismissTimestamp());
+
+        mResolver.bulkInsert(mUri, generateTwoRows());
+
+        assertThat(queryDismissedTimestamp()).isEqualTo(10001L);
+    }
+
+    @Test
+    public void bulkInsert_notKeepDismissalTimestamp_shouldNotHaveTimestamp() {
+        mResolver.bulkInsert(mUri, generateTwoRowsWithDismissTimestamp());
+
+        mResolver.bulkInsert(mUri, generateTwoRows());
+
+        assertThat(queryDismissedTimestamp()).isEqualTo(0L);
+    }
+
+    @Test
     public void cardData_query() {
         mResolver.insert(mUri, generateOneRow());
         final int count = getRowCount();
@@ -198,10 +218,40 @@
         return twoRows;
     }
 
+    private ContentValues[] generateTwoRowsWithDismissTimestamp() {
+        final ContentValues[] twoRows = new ContentValues[2];
+        twoRows[0] = generateOneRow();
+
+        final ContentValues values = new ContentValues();
+        values.put(CardDatabaseHelper.CardColumns.NAME, "toggle_airplane");
+        values.put(CardDatabaseHelper.CardColumns.TYPE, 1);
+        values.put(CardDatabaseHelper.CardColumns.SCORE, 0.95);
+        values.put(CardDatabaseHelper.CardColumns.SLICE_URI,
+                "content://com.android.settings.slices/action/toggle_airplane");
+        values.put(CardDatabaseHelper.CardColumns.CATEGORY, 2);
+        values.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME, "com.android.settings");
+        values.put(CardDatabaseHelper.CardColumns.APP_VERSION, 10001);
+        values.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP, 10001L);
+        twoRows[1] = values;
+
+        return twoRows;
+    }
+
     private int getRowCount() {
         final Cursor cr = mResolver.query(mUri, null, null, null);
         final int count = cr.getCount();
         cr.close();
         return count;
     }
+
+    private long queryDismissedTimestamp() {
+        final String[] columns = {CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP};
+        final String selection = CardDatabaseHelper.CardColumns.NAME + "=?";
+        final String[] selectionArgs = {"toggle_airplane"};
+        final Cursor cr = mResolver.query(mUri, columns, selection, selectionArgs, null);
+        cr.moveToFirst();
+        final long dismissedTimestamp = cr.getLong(0);
+        cr.close();
+        return  dismissedTimestamp;
+    }
 }
\ No newline at end of file