Merge "Fix panel bugs"
diff --git a/res/layout/settings_panel.xml b/res/layout/settings_panel.xml
index aec898c..3405ef0 100644
--- a/res/layout/settings_panel.xml
+++ b/res/layout/settings_panel.xml
@@ -16,4 +16,5 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/main_content"
     android:layout_height="match_parent"
-    android:layout_width="match_parent"/>
\ No newline at end of file
+    android:layout_width="match_parent"
+    android:animateLayoutChanges="true"/>
\ No newline at end of file
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItem.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItem.java
index 305281c..370a4df 100644
--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItem.java
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItem.java
@@ -30,6 +30,7 @@
 
     private static final String TAG = "DeviceAdminListItem";
 
+    private final UserHandle mUserHandle;
     private final String mKey;
     private final DeviceAdminInfo mInfo;
     private final CharSequence mName;
@@ -39,7 +40,8 @@
 
     public DeviceAdminListItem(Context context, DeviceAdminInfo info) {
         mInfo = info;
-        mKey = mInfo.getComponent().flattenToString();
+        mUserHandle = new UserHandle(getUserIdFromDeviceAdminInfo(mInfo));
+        mKey = mUserHandle.getIdentifier() + "@" + mInfo.getComponent().flattenToString();
         mDPM = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
         final PackageManager pm = context.getPackageManager();
         mName = mInfo.loadLabel(pm);
@@ -48,8 +50,7 @@
         } catch (Resources.NotFoundException exception) {
             Log.w(TAG, "Setting description to null because can't find resource: " + mKey);
         }
-        mIcon = pm.getUserBadgedIcon(mInfo.loadIcon(pm),
-                new UserHandle(DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo)));
+        mIcon = pm.getUserBadgedIcon(mInfo.loadIcon(pm), mUserHandle);
     }
 
     @Override
@@ -70,8 +71,7 @@
     }
 
     public boolean isActive() {
-        return mDPM.isAdminActiveAsUser(mInfo.getComponent(),
-                DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo));
+        return mDPM.isAdminActiveAsUser(mInfo.getComponent(), getUserIdFromDeviceAdminInfo(mInfo));
     }
 
     public Drawable getIcon() {
@@ -79,16 +79,25 @@
     }
 
     public boolean isEnabled() {
-        return !mDPM.isRemovingAdmin(mInfo.getComponent(),
-                DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo));
+        return !mDPM.isRemovingAdmin(mInfo.getComponent(), getUserIdFromDeviceAdminInfo(mInfo));
     }
 
     public UserHandle getUser() {
-        return new UserHandle(DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo));
+        return new UserHandle(getUserIdFromDeviceAdminInfo(mInfo));
     }
 
     public Intent getLaunchIntent(Context context) {
         return new Intent(context, DeviceAdminAdd.class)
                 .putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mInfo.getComponent());
     }
+
+    /**
+     * Extracts the user id from a device admin info object.
+     *
+     * @param adminInfo the device administrator info.
+     * @return identifier of the user associated with the device admin.
+     */
+    private static int getUserIdFromDeviceAdminInfo(DeviceAdminInfo adminInfo) {
+        return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
+    }
 }
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
index 319d62f..7b139d9 100644
--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
@@ -53,6 +53,9 @@
 import com.android.settingslib.widget.FooterPreference;
 import com.android.settingslib.widget.FooterPreferenceMixinCompat;
 
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -249,8 +252,7 @@
                 Log.w(TAG, "Unable to load component: " + activeAdmin);
                 continue;
             }
-            final DeviceAdminInfo deviceAdminInfo = DeviceAdminUtils.createDeviceAdminInfo(
-                    mContext, ai);
+            final DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(mContext, ai);
             if (deviceAdminInfo == null) {
                 continue;
             }
@@ -286,7 +288,7 @@
                     && alreadyAddedComponents.contains(riComponentName)) {
                 continue;
             }
-            DeviceAdminInfo deviceAdminInfo = DeviceAdminUtils.createDeviceAdminInfo(
+            DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(
                     mContext, resolveInfo.activityInfo);
             // add only visible ones (note: active admins are added regardless of visibility)
             if (deviceAdminInfo != null && deviceAdminInfo.isVisible()) {
@@ -297,4 +299,20 @@
             }
         }
     }
+
+    /**
+     * Creates a device admin info object for the resolved intent that points to the component of
+     * the device admin.
+     *
+     * @param ai ActivityInfo for the admin component.
+     * @return new {@link DeviceAdminInfo} object or null if there was an error.
+     */
+    private static DeviceAdminInfo createDeviceAdminInfo(Context context, ActivityInfo ai) {
+        try {
+            return new DeviceAdminInfo(context, ai);
+        } catch (XmlPullParserException | IOException e) {
+            Log.w(TAG, "Skipping " + ai, e);
+        }
+        return null;
+    }
 }
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminUtils.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminUtils.java
deleted file mode 100644
index 13d9d20..0000000
--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminUtils.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications.specialaccess.deviceadmin;
-
-import android.app.admin.DeviceAdminInfo;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.os.UserHandle;
-import android.util.Log;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-public class DeviceAdminUtils {
-
-    private static final String TAG = "DeviceAdminUtils";
-
-    /**
-     * Creates a device admin info object for the resolved intent that points to the component of
-     * the device admin.
-     *
-     * @param ai ActivityInfo for the admin component.
-     * @return new {@link DeviceAdminInfo} object or null if there was an error.
-     */
-    public static DeviceAdminInfo createDeviceAdminInfo(Context context, ActivityInfo ai) {
-        try {
-            return new DeviceAdminInfo(context, ai);
-        } catch (XmlPullParserException | IOException e) {
-            Log.w(TAG, "Skipping " + ai, e);
-        }
-        return null;
-    }
-
-    /**
-     * Extracts the user id from a device admin info object.
-     *
-     * @param adminInfo the device administrator info.
-     * @return identifier of the user associated with the device admin.
-     */
-    public static int getUserIdFromDeviceAdminInfo(DeviceAdminInfo adminInfo) {
-        return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
-    }
-}
diff --git a/src/com/android/settings/deviceinfo/hardwareinfo/SerialNumberPreferenceController.java b/src/com/android/settings/deviceinfo/hardwareinfo/SerialNumberPreferenceController.java
index 4485c53..01cd5f3 100644
--- a/src/com/android/settings/deviceinfo/hardwareinfo/SerialNumberPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/hardwareinfo/SerialNumberPreferenceController.java
@@ -21,6 +21,7 @@
 
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.slices.Sliceable;
 
 public class SerialNumberPreferenceController extends BasePreferenceController {
 
@@ -40,6 +41,17 @@
     }
 
     @Override
+    public boolean isCopyableSlice() {
+        return true;
+    }
+
+    @Override
+    public void copy() {
+        Sliceable.setCopyContent(mContext, getSummary(),
+                mContext.getText(R.string.status_serial_number));
+    }
+
+    @Override
     public CharSequence getSummary() {
         return Build.getSerial();
     }
diff --git a/src/com/android/settings/gestures/PreventRingingSwitchPreferenceController.java b/src/com/android/settings/gestures/PreventRingingSwitchPreferenceController.java
index 35ff2ec..e21bb75 100644
--- a/src/com/android/settings/gestures/PreventRingingSwitchPreferenceController.java
+++ b/src/com/android/settings/gestures/PreventRingingSwitchPreferenceController.java
@@ -61,6 +61,17 @@
             LayoutPreference pref = screen.findPreference(getPreferenceKey());
             if (pref != null) {
                 mSettingObserver = new SettingObserver(pref);
+                pref.setOnPreferenceClickListener(preference -> {
+                    int preventRinging = Settings.Secure.getInt(mContext.getContentResolver(),
+                            Settings.Secure.VOLUME_HUSH_GESTURE,
+                            Settings.Secure.VOLUME_HUSH_VIBRATE);
+                    boolean isChecked = preventRinging != Settings.Secure.VOLUME_HUSH_OFF;
+                    Settings.Secure.putInt(mContext.getContentResolver(),
+                            Settings.Secure.VOLUME_HUSH_GESTURE, isChecked
+                                    ? Settings.Secure.VOLUME_HUSH_OFF
+                                    : Settings.Secure.VOLUME_HUSH_VIBRATE);
+                    return true;
+                });
                 mSwitch = pref.findViewById(R.id.switch_bar);
                 if (mSwitch != null) {
                     mSwitch.addOnSwitchChangeListener(this);
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
index 42e18ed..d6ea6ca 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.Cursor;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
 import android.text.format.DateUtils;
diff --git a/src/com/android/settings/nfc/NfcPreferenceController.java b/src/com/android/settings/nfc/NfcPreferenceController.java
index 04f288d..2ca3b23 100644
--- a/src/com/android/settings/nfc/NfcPreferenceController.java
+++ b/src/com/android/settings/nfc/NfcPreferenceController.java
@@ -15,20 +15,26 @@
  */
 package com.android.settings.nfc;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.Uri;
 import android.nfc.NfcAdapter;
 import android.provider.Settings;
-
+import android.util.Log;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreference;
 
 import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.slices.SliceBackgroundWorker;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
 import com.android.settingslib.core.lifecycle.events.OnResume;
 
+import java.io.IOException;
+
 public class NfcPreferenceController extends TogglePreferenceController
         implements LifecycleObserver, OnResume, OnPause {
 
@@ -51,8 +57,7 @@
             return;
         }
 
-        final SwitchPreference switchPreference =
-                (SwitchPreference) screen.findPreference(getPreferenceKey());
+        final SwitchPreference switchPreference = screen.findPreference(getPreferenceKey());
 
         mNfcEnabler = new NfcEnabler(mContext, switchPreference);
 
@@ -87,14 +92,6 @@
     }
 
     @Override
-    public IntentFilter getIntentFilter() {
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
-        filter.addAction(NfcAdapter.EXTRA_ADAPTER_STATE);
-        return filter;
-    }
-
-    @Override
     public boolean hasAsyncUpdate() {
         return true;
     }
@@ -105,6 +102,11 @@
     }
 
     @Override
+    public Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() {
+        return NfcSliceWorker.class;
+    }
+
+    @Override
     public void onResume() {
         if (mAirplaneModeObserver != null) {
             mAirplaneModeObserver.register();
@@ -135,4 +137,77 @@
                 Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
         return toggleable != null && toggleable.contains(Settings.Global.RADIO_NFC);
     }
+
+    /**
+     * Listener for background changes to NFC.
+     *
+     * <p>
+     *     Listen to broadcasts from {@link NfcAdapter}. The worker will call notify changed on the
+     *     NFC Slice only when the following extras are present in the broadcast:
+     *     <ul>
+     *      <li>{@link NfcAdapter#STATE_ON}</li>
+     *      <li>{@link NfcAdapter#STATE_OFF}</li>
+     *     </ul>
+     */
+    public static class NfcSliceWorker extends SliceBackgroundWorker<Void> {
+
+        private static final String TAG = "NfcSliceWorker";
+
+        private static final IntentFilter NFC_FILTER =
+                new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+
+        private NfcUpdateReceiver mUpdateObserver;
+
+        public NfcSliceWorker(Context context, Uri uri) {
+            super(context, uri);
+            mUpdateObserver = new NfcUpdateReceiver(this);
+        }
+
+        @Override
+        protected void onSlicePinned() {
+            getContext().registerReceiver(mUpdateObserver, NFC_FILTER);
+        }
+
+        @Override
+        protected void onSliceUnpinned() {
+            getContext().unregisterReceiver(mUpdateObserver);
+        }
+
+        @Override
+        public void close() throws IOException {
+            mUpdateObserver = null;
+        }
+
+        public void updateSlice() {
+            notifySliceChange();
+        }
+
+        public class NfcUpdateReceiver extends BroadcastReceiver {
+
+            private final int NO_EXTRA = -1;
+
+            private final NfcSliceWorker mSliceBackgroundWorker;
+
+            public NfcUpdateReceiver(NfcSliceWorker sliceWorker) {
+                mSliceBackgroundWorker = sliceWorker;
+            }
+
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final int nfcStateExtra = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,
+                        NO_EXTRA);
+
+                // Do nothing if state change is empty, or an intermediate step.
+                if ( (nfcStateExtra == NO_EXTRA)
+                        || (nfcStateExtra == NfcAdapter.STATE_TURNING_ON)
+                        || (nfcStateExtra == NfcAdapter.STATE_TURNING_OFF)) {
+                    Log.d(TAG, "Transitional update, dropping broadcast");
+                    return;
+                }
+
+                Log.d(TAG, "Nfc broadcast received, updating Slice.");
+                mSliceBackgroundWorker.updateSlice();
+            }
+        }
+    }
 }
diff --git a/src/com/android/settings/slices/CustomSliceable.java b/src/com/android/settings/slices/CustomSliceable.java
index b48f22a..0b97bed 100644
--- a/src/com/android/settings/slices/CustomSliceable.java
+++ b/src/com/android/settings/slices/CustomSliceable.java
@@ -84,18 +84,6 @@
     Intent getIntent();
 
     /**
-     * Settings Slices which require background work, such as updating lists should implement a
-     * {@link SliceBackgroundWorker} and return it here. An example of background work is updating
-     * a list of Wifi networks available in the area.
-     *
-     * @return a {@link Class<? extends SliceBackgroundWorker>} to perform background work for the
-     * slice.
-     */
-    default Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() {
-        return null;
-    }
-
-    /**
      * Standardize the intents returned to indicate actions by the Slice.
      * <p>
      *     The {@link PendingIntent} is linked to {@link SliceBroadcastReceiver} where the Intent
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 5c662e5..397b2fc 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -153,7 +153,7 @@
             if (filter != null) {
                 registerIntentToUri(filter, sliceUri);
             }
-            ThreadUtils.postOnMainThread(() -> startBackgroundWorker(sliceable));
+            ThreadUtils.postOnMainThread(() -> startBackgroundWorker(sliceable, sliceUri));
             return;
         }
 
@@ -326,20 +326,19 @@
         }
     }
 
-    private void startBackgroundWorker(CustomSliceable sliceable) {
+    private void startBackgroundWorker(Sliceable sliceable, Uri uri) {
         final Class workerClass = sliceable.getBackgroundWorkerClass();
         if (workerClass == null) {
             return;
         }
 
-        final Uri uri = sliceable.getUri();
         if (mPinnedWorkers.containsKey(uri)) {
             return;
         }
 
         Log.d(TAG, "Starting background worker for: " + uri);
         final SliceBackgroundWorker worker = SliceBackgroundWorker.getInstance(
-                getContext(), sliceable);
+                getContext(), sliceable, uri);
         mPinnedWorkers.put(uri, worker);
         worker.onSlicePinned();
     }
@@ -397,6 +396,8 @@
             registerIntentToUri(filter, uri);
         }
 
+        ThreadUtils.postOnMainThread(() -> startBackgroundWorker(controller, uri));
+
         final List<Uri> pinnedSlices = getContext().getSystemService(
                 SliceManager.class).getPinnedSlices();
         if (pinnedSlices.contains(uri)) {
diff --git a/src/com/android/settings/slices/SliceBackgroundWorker.java b/src/com/android/settings/slices/SliceBackgroundWorker.java
index 995394e..559aa71 100644
--- a/src/com/android/settings/slices/SliceBackgroundWorker.java
+++ b/src/com/android/settings/slices/SliceBackgroundWorker.java
@@ -80,13 +80,12 @@
      * Returns the singleton instance of the {@link SliceBackgroundWorker} for specified {@link
      * CustomSliceable}
      */
-    static SliceBackgroundWorker getInstance(Context context, CustomSliceable sliceable) {
-        final Uri uri = sliceable.getUri();
+    static SliceBackgroundWorker getInstance(Context context, Sliceable sliceable, Uri uri) {
         SliceBackgroundWorker worker = getInstance(uri);
         if (worker == null) {
             final Class<? extends SliceBackgroundWorker> workerClass =
                     sliceable.getBackgroundWorkerClass();
-            worker = createInstance(context, uri, workerClass);
+            worker = createInstance(context.getApplicationContext(), uri, workerClass);
             LIVE_WORKERS.put(uri, worker);
         }
         return worker;
diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java
index 2a9b491..b2ea583 100644
--- a/src/com/android/settings/slices/SliceBroadcastReceiver.java
+++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java
@@ -197,7 +197,7 @@
             return;
         }
 
-        ((Sliceable) controller).copy();
+        controller.copy();
     }
 
     /**
diff --git a/src/com/android/settings/slices/Sliceable.java b/src/com/android/settings/slices/Sliceable.java
index b00ab82..c1661f8 100644
--- a/src/com/android/settings/slices/Sliceable.java
+++ b/src/com/android/settings/slices/Sliceable.java
@@ -91,4 +91,16 @@
         final String toast = context.getString(R.string.copyable_slice_toast, messageTitle);
         Toast.makeText(context, toast, Toast.LENGTH_SHORT).show();
     }
+
+    /**
+     * Settings Slices which require background work, such as updating lists should implement a
+     * {@link SliceBackgroundWorker} and return it here. An example of background work is updating
+     * a list of Wifi networks available in the area.
+     *
+     * @return a {@link Class<? extends SliceBackgroundWorker>} to perform background work for the
+     * slice.
+     */
+    default Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() {
+        return null;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItemTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItemTest.java
index 17703e3..7392fb1 100644
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItemTest.java
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItemTest.java
@@ -67,7 +67,7 @@
 
         DeviceAdminListItem item = new DeviceAdminListItem(mContext, mDeviceAdminInfo);
 
-        assertThat(item.getKey()).isEqualTo(cn.flattenToShortString());
+        assertThat(item.getKey()).isEqualTo("0@" + cn.flattenToShortString());
         assertThat(item.getName()).isEqualTo(label);
         assertThat(item.getDescription()).isEqualTo(description);
     }
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/hardwareinfo/SerialNumberPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/hardwareinfo/SerialNumberPreferenceControllerTest.java
new file mode 100644
index 0000000..c2ae4af
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/hardwareinfo/SerialNumberPreferenceControllerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.hardwareinfo;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Build;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class SerialNumberPreferenceControllerTest {
+
+    private Context mContext;
+    private SerialNumberPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mController = new SerialNumberPreferenceController(mContext, "test");
+    }
+
+    @Test
+    public void isCopyableSlice() {
+        assertThat(mController.isSliceable()).isTrue();
+        assertThat(mController.isCopyableSlice()).isTrue();
+    }
+
+    @Test
+    public void copy_shouldPutSerialNumberToClipBoard() {
+        mController.copy();
+
+        final ClipboardManager clipboardManager = mContext.getSystemService(ClipboardManager.class);
+        final ClipData data = clipboardManager.getPrimaryClip();
+
+        assertThat(data.getItemAt(0).getText().toString()).contains(Build.getSerial());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/gestures/PreventRingingSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/PreventRingingSwitchPreferenceControllerTest.java
index 5f221f5..85eeacc 100644
--- a/tests/robotests/src/com/android/settings/gestures/PreventRingingSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/PreventRingingSwitchPreferenceControllerTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -29,8 +30,10 @@
 import android.provider.Settings;
 
 import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
 
 import com.android.settings.widget.SwitchBar;
+import com.android.settingslib.widget.LayoutPreference;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -95,4 +98,16 @@
         mController.updateState(mPreference);
         verify(mController.mSwitch, times(1)).setChecked(true);
     }
+
+    @Test
+    public void testPreferenceClickListenerAttached() {
+        PreferenceScreen preferenceScreen = mock(PreferenceScreen.class);
+        LayoutPreference mLayoutPreference = mock(LayoutPreference.class);
+        when(preferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mLayoutPreference);
+        mController.displayPreference(preferenceScreen);
+
+        verify(mLayoutPreference, times(1))
+                .setOnPreferenceClickListener(any());
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java
index 8883ddf..e3672c9 100644
--- a/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java
@@ -19,10 +19,13 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
 import android.nfc.NfcAdapter;
 import android.nfc.NfcManager;
 import android.os.UserManager;
@@ -31,6 +34,10 @@
 import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreference;
 
+import com.android.settings.nfc.NfcPreferenceController.NfcSliceWorker;
+import com.android.settings.nfc.NfcPreferenceController.NfcSliceWorker.NfcUpdateReceiver;
+import com.android.settings.slices.SliceBuilderUtils;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -246,4 +253,67 @@
 
         assertThat(mNfcController.mAirplaneModeObserver).isNull();
     }
+
+    @Test
+    public void ncfSliceWorker_nfcBroadcast_noExtra_sliceDoesntUpdate() {
+        final NfcSliceWorker worker = spy(new NfcSliceWorker(mContext, getDummyUri()));
+        final NfcUpdateReceiver receiver = worker.new NfcUpdateReceiver(worker);
+        final Intent triggerIntent = new Intent(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+
+        receiver.onReceive(mContext, triggerIntent);
+
+        verify(worker, times(0)).updateSlice();
+    }
+
+    @Test
+    public void ncfSliceWorker_nfcBroadcast_turningOn_sliceDoesntUpdate() {
+        final NfcSliceWorker worker = spy(new NfcSliceWorker(mContext, getDummyUri()));
+        final NfcUpdateReceiver receiver = worker.new NfcUpdateReceiver(worker);
+        final Intent triggerIntent = new Intent(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+        triggerIntent.putExtra(NfcAdapter.EXTRA_ADAPTER_STATE, NfcAdapter.STATE_TURNING_ON);
+
+        receiver.onReceive(mContext, triggerIntent);
+
+        verify(worker, times(0)).updateSlice();
+    }
+
+    @Test
+    public void ncfSliceWorker_nfcBroadcast_turningOff_sliceDoesntUpdate() {
+        final NfcSliceWorker worker = spy(new NfcSliceWorker(mContext, getDummyUri()));
+        final NfcUpdateReceiver receiver = worker.new NfcUpdateReceiver(worker);
+        final Intent triggerIntent = new Intent(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+        triggerIntent.putExtra(NfcAdapter.EXTRA_ADAPTER_STATE, NfcAdapter.STATE_TURNING_OFF);
+
+        receiver.onReceive(mContext, triggerIntent);
+
+        verify(worker, times(0)).updateSlice();
+    }
+
+    @Test
+    public void ncfSliceWorker_nfcBroadcast_nfcOn_sliceUpdates() {
+        final NfcSliceWorker worker = spy(new NfcSliceWorker(mContext, getDummyUri()));
+        final NfcUpdateReceiver receiver = worker.new NfcUpdateReceiver(worker);
+        final Intent triggerIntent = new Intent(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+        triggerIntent.putExtra(NfcAdapter.EXTRA_ADAPTER_STATE, NfcAdapter.STATE_ON);
+
+        receiver.onReceive(mContext, triggerIntent);
+
+        verify(worker).updateSlice();
+    }
+
+    @Test
+    public void ncfSliceWorker_nfcBroadcast_nfcOff_sliceUpdates() {
+        final NfcSliceWorker worker = spy(new NfcSliceWorker(mContext, getDummyUri()));
+        final NfcUpdateReceiver receiver = worker.new NfcUpdateReceiver(worker);
+        final Intent triggerIntent = new Intent(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+        triggerIntent.putExtra(NfcAdapter.EXTRA_ADAPTER_STATE, NfcAdapter.STATE_OFF);
+
+        receiver.onReceive(mContext, triggerIntent);
+
+        verify(worker).updateSlice();
+    }
+
+    private Uri getDummyUri() {
+        return SliceBuilderUtils.getUri("action/nfc", false);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index 726e4bd..a693f34 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -64,6 +64,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
@@ -184,6 +185,20 @@
     }
 
     @Test
+    public void loadSlice_registersBackgroundListener() {
+        insertSpecialCase(KEY);
+        final Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
+
+        mProvider.loadSlice(uri);
+
+        Robolectric.flushForegroundThreadScheduler();
+        Robolectric.flushBackgroundThreadScheduler();
+
+        assertThat(mProvider.mPinnedWorkers.get(uri).getClass())
+                .isEqualTo(FakeToggleController.TestWorker.class);
+    }
+
+    @Test
     public void testLoadSlice_doesNotCacheWithoutPin() {
         insertSpecialCase(KEY);
         final Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeToggleController.java b/tests/robotests/src/com/android/settings/testutils/FakeToggleController.java
index d1677cd..e785487 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeToggleController.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeToggleController.java
@@ -19,10 +19,14 @@
 
 import android.content.Context;
 import android.content.IntentFilter;
+import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.provider.Settings;
 
 import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.slices.SliceBackgroundWorker;
+
+import java.io.IOException;
 
 public class FakeToggleController extends TogglePreferenceController {
 
@@ -71,6 +75,11 @@
     }
 
     @Override
+    public Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() {
+        return TestWorker.class;
+    }
+
+    @Override
     public boolean hasAsyncUpdate() {
         return mIsAsyncUpdate;
     }
@@ -78,4 +87,23 @@
     public void setAsyncUpdate(boolean isAsyncUpdate) {
         mIsAsyncUpdate = isAsyncUpdate;
     }
+
+    public static class TestWorker extends SliceBackgroundWorker<Void> {
+
+        public TestWorker(Context context, Uri uri) {
+            super(context, uri);
+        }
+
+        @Override
+        protected void onSlicePinned() {
+        }
+
+        @Override
+        protected void onSliceUnpinned() {
+        }
+
+        @Override
+        public void close() throws IOException {
+        }
+    }
 }
diff --git a/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java b/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java
index ede4631..19b1360 100644
--- a/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java
+++ b/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java
@@ -41,6 +41,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.provider.Settings;
+import android.support.test.uiautomator.UiDevice;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.espresso.intent.Intents;
@@ -55,7 +56,9 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class AppNotificationSettingsTest {
+    private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard";
 
+    private UiDevice mUiDevice;
     private Context mTargetContext;
     private Instrumentation mInstrumentation;
 
@@ -68,9 +71,14 @@
     private NotificationChannel mUngroupedChannel;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mTargetContext = mInstrumentation.getTargetContext();
+
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mUiDevice.wakeUp();
+        mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND);
+
         mNm  = (NotificationManager) mTargetContext.getSystemService(Context.NOTIFICATION_SERVICE);
 
         mGroup1 = new NotificationChannelGroup(this.getClass().getName() + "1", "group1");
@@ -87,7 +95,8 @@
     @Test
     public void launchNotificationSetting_shouldNotHaveAppInfoLink() {
         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
-                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName());
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
         mInstrumentation.startActivitySync(intent);
 
@@ -99,60 +108,38 @@
     @Test
     public void launchNotificationSetting_showGroupsWithMultipleChannels() {
         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
-                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName());
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         mInstrumentation.startActivitySync(intent);
         onView(allOf(withText(mGroup1.getName().toString()))).check(
                 matches(isDisplayed()));
-        try {
-            onView(allOf(withText(mGroup1Channel1.getName().toString())))
-                    .check(matches(isDisplayed()));
-            fail("Channel erroneously appearing");
-        } catch (Exception e) {
-            // expected
-        }
-        // links to group page
-        Intents.init();
-        onView(allOf(withText(mGroup1.getName().toString()))).perform(click());
-        intended(allOf(hasExtra(EXTRA_SHOW_FRAGMENT,
-                ChannelGroupNotificationSettings.class.getName())));
-        Intents.release();
+        onView(allOf(withText(mGroup1Channel1.getName().toString()))).check(
+                matches(isDisplayed()));
+        onView(allOf(withText(mGroup1Channel2.getName().toString()))).check(
+                matches(isDisplayed()));
     }
 
     @Test
     public void launchNotificationSetting_showUngroupedChannels() {
         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
-                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName());
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         mInstrumentation.startActivitySync(intent);
         onView(allOf(withText(mUngroupedChannel.getName().toString())))
                 .check(matches(isDisplayed()));
-        // links directly to channel page
-        Intents.init();
-        onView(allOf(withText(mUngroupedChannel.getName().toString()))).perform(click());
-        intended(allOf(hasExtra(EXTRA_SHOW_FRAGMENT, ChannelNotificationSettings.class.getName())));
-        Intents.release();
     }
 
     @Test
     public void launchNotificationSetting_showGroupsWithOneChannel() {
         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
-                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName());
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         mInstrumentation.startActivitySync(intent);
 
+        onView(allOf(withText(mGroup2.getName().toString())))
+                .check(matches(isDisplayed()));
         onView(allOf(withText(mGroup2Channel1.getName().toString())))
                 .check(matches(isDisplayed()));
-        try {
-            onView(allOf(withText(mGroup2.getName().toString()))).check(
-                    matches(isDisplayed()));
-            fail("Group erroneously appearing");
-        } catch (Exception e) {
-            // expected
-        }
-
-        // links directly to channel page
-        Intents.init();
-        onView(allOf(withText(mGroup2Channel1.getName().toString()))).perform(click());
-        intended(allOf(hasExtra(EXTRA_SHOW_FRAGMENT, ChannelNotificationSettings.class.getName())));
-        Intents.release();
     }
 
     private NotificationChannel createChannel(NotificationChannelGroup group,
diff --git a/tests/unit/src/com/android/settings/notification/ChannelNotificationSettingsTest.java b/tests/unit/src/com/android/settings/notification/ChannelNotificationSettingsTest.java
index f7a5a82..9a3a994 100644
--- a/tests/unit/src/com/android/settings/notification/ChannelNotificationSettingsTest.java
+++ b/tests/unit/src/com/android/settings/notification/ChannelNotificationSettingsTest.java
@@ -36,6 +36,7 @@
 import android.os.Process;
 import android.os.ServiceManager;
 import android.provider.Settings;
+import android.support.test.uiautomator.UiDevice;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -48,17 +49,23 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ChannelNotificationSettingsTest {
+    private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard";
 
+    private UiDevice mUiDevice;
     private Context mTargetContext;
     private Instrumentation mInstrumentation;
     private NotificationChannel mNotificationChannel;
     private NotificationManager mNm;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mTargetContext = mInstrumentation.getTargetContext();
 
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mUiDevice.wakeUp();
+        mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND);
+
         mNm  = (NotificationManager) mTargetContext.getSystemService(Context.NOTIFICATION_SERVICE);
         mNotificationChannel = new NotificationChannel(this.getClass().getName(),
                 this.getClass().getName(), IMPORTANCE_MIN);
@@ -69,7 +76,8 @@
     public void launchNotificationSetting_shouldNotCrash() {
         final Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
                 .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
-                .putExtra(Settings.EXTRA_CHANNEL_ID, mNotificationChannel.getId());
+                .putExtra(Settings.EXTRA_CHANNEL_ID, mNotificationChannel.getId())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         mInstrumentation.startActivitySync(intent);
 
         onView(allOf(withText(mNotificationChannel.getName().toString()))).check(
@@ -90,12 +98,12 @@
 
         final Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
                 .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
-                .putExtra(Settings.EXTRA_CHANNEL_ID, blocked.getId());
+                .putExtra(Settings.EXTRA_CHANNEL_ID, blocked.getId())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         mInstrumentation.startActivitySync(intent);
 
-        onView(allOf(withText("Off"), isDisplayed())).check(matches(isDisplayed()));
-        onView(allOf(withText("Android is blocking this category of notifications from"
-                + " appearing on this device"))).check(matches(isDisplayed()));
+        onView(allOf(withText("At your request, Android is blocking this category of notifications"
+                + " from appearing on this device"))).check(matches(isDisplayed()));
 
         try {
             onView(allOf(withText("On the lock screen"))).check(matches(isDisplayed()));
diff --git a/tests/unit/src/com/android/settings/notification/ZenModeSettingsIntegrationTest.java b/tests/unit/src/com/android/settings/notification/ZenModeSettingsIntegrationTest.java
index 2fe4074..4120a07 100644
--- a/tests/unit/src/com/android/settings/notification/ZenModeSettingsIntegrationTest.java
+++ b/tests/unit/src/com/android/settings/notification/ZenModeSettingsIntegrationTest.java
@@ -37,27 +37,29 @@
     @Test
     public void testZenModeSettingsPreferences() {
         launchZenSettings();
-        onView(withText("Behavior")).check(matches(isDisplayed()));
-        onView(withText("Turn on automatically")).check(matches(isDisplayed()));
+        onView(withText("Calls")).check(matches(isDisplayed()));
+        onView(withText("SMS, MMS, and messaging apps")).check(matches(isDisplayed()));
+        onView(withText("Restrict notifications")).check(matches(isDisplayed()));
+        onView(withText("Duration")).check(matches(isDisplayed()));
+        onView(withText("Schedules")).check(matches(isDisplayed()));
     }
 
     @Test
     public void testZenModeBehaviorPreferences() {
         launchZenBehaviorSettings();
-        onView(withText("Alarms")).check(matches(isDisplayed()));
-        onView(withText("Media and system feedback")).check(matches(isDisplayed()));
-        onView(withText("Reminders")).check(matches(isDisplayed()));
-        onView(withText("Events")).check(matches(isDisplayed()));
-        onView(withText("Messages")).check(matches(isDisplayed()));
         onView(withText("Calls")).check(matches(isDisplayed()));
-        onView(withText("Repeat callers")).check(matches(isDisplayed()));
+        onView(withText("SMS, MMS, and messaging apps")).check(matches(isDisplayed()));
+        onView(withText("Restrict notifications")).check(matches(isDisplayed()));
+        onView(withText("Duration")).check(matches(isDisplayed()));
+        onView(withText("Schedules")).check(matches(isDisplayed()));
     }
 
     @Test
     public void testZenModeAutomationPreferences() {
         launchZenAutomationSettings();
-        onView(withText("Weekend")).check(matches(isDisplayed()));
-        onView(withText("Add rule")).check(matches(isDisplayed()));
+        onView(withText("Sleeping")).check(matches(isDisplayed()));
+        onView(withText("Event")).check(matches(isDisplayed()));
+        onView(withText("Add more")).check(matches(isDisplayed()));
     }
 
     private void launchZenSettings() {