Merge "Instrument metrics for smart-auto-rotate and screen timeout" into sc-dev
diff --git a/res/drawable/ic_notification_silence.xml b/res/drawable/ic_notification_silence.xml
index 9da56dd..4b71d65 100644
--- a/res/drawable/ic_notification_silence.xml
+++ b/res/drawable/ic_notification_silence.xml
@@ -14,28 +14,13 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:id="@+id/back">
-        <shape android:shape="oval">
-            <solid
-                android:color="@color/notification_silence_color" />
-            <size
-                android:height="24dp"
-                android:width="24dp"/>
-        </shape>
-    </item>
-    <item
-        android:id="@+id/fore"
-        android:gravity="center">
-        <vector
-            android:width="13dp"
-            android:height="13dp"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+            android:width="24dp"
+            android:height="24dp"
             android:viewportWidth="24"
-            android:viewportHeight="24">
-            <path
-                android:fillColor="#FFFFFFFF"
-                android:pathData="M15,14.5c-1.38,0 -2.5,-1.12 -2.5,-2.5c0,-0.28 -0.22,-0.5 -0.5,-0.5s-0.5,0.22 -0.5,0.5c0,1.38 -1.12,2.5 -2.5,2.5S6.5,13.38 6.5,12c0,-0.28 -0.22,-0.5 -0.5,-0.5c-0.24,0 -0.46,0.18 -0.49,0.42C5.41,12.55 4.89,13 4.27,13H2v-2h1.71C4.1,10.11 5,9.5 6,9.5c1.38,0 2.5,1.12 2.5,2.5c0,0.28 0.22,0.5 0.5,0.5s0.5,-0.22 0.5,-0.5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5c0,0.28 0.22,0.5 0.5,0.5s0.5,-0.22 0.5,-0.5c0,-1.38 1.12,-2.5 2.5,-2.5c1.02,0 1.91,0.6 2.29,1.5H22v2h-2.27c-0.62,0 -1.14,-0.45 -1.23,-1.08c-0.04,-0.24 -0.25,-0.42 -0.49,-0.42c-0.28,0 -0.5,0.22 -0.5,0.5C17.5,13.38 16.38,14.5 15,14.5z"/>
-        </vector>
-    </item>
-</layer-list>
+            android:viewportHeight="24"
+            android:tint="?android:attr/colorControlNormal">
+    <path
+        android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM16,16L2.81,2.81 1.39,4.22l4.85,4.85C6.09,9.68 6,10.33 6,11v6L4,17v2h12.17l3.61,3.61 1.41,-1.41L16,16zM8,17l0.01,-6.16L14.17,17L8,17zM12,6.5c2.49,0 4,2.02 4,4.5v2.17l2,2L18,11c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68c-0.78,0.18 -1.45,0.52 -2.04,0.95L9.93,7.1c0.58,-0.37 1.27,-0.6 2.07,-0.6z"
+        android:fillColor="@android:color/white"/>
+</vector>
diff --git a/res/drawable/ic_ongoing_notification.xml b/res/drawable/ic_ongoing_notification.xml
new file mode 100644
index 0000000..238b517
--- /dev/null
+++ b/res/drawable/ic_ongoing_notification.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,18h0.17l-1.09,-1.09 1.41,-1.41L16,19l-3.5,3.5 -1.41,-1.41L12.17,20L12,20c-2.05,0 -4.09,-0.78 -5.66,-2.34 -3.12,-3.12 -3.12,-8.19 0,-11.31l1.41,1.41c-2.34,2.34 -2.34,6.15 0,8.49C8.89,17.38 10.4,18 12,18zM17.66,6.34c3.12,3.12 3.12,8.19 0,11.31l-1.41,-1.41c2.34,-2.34 2.34,-6.15 0,-8.49C15.11,6.62 13.6,6 12,6h-0.17l1.09,1.09L11.5,8.5 8,5l3.5,-3.5 1.41,1.41L11.83,4L12,4c2.05,0 4.09,0.78 5.66,2.34z"/>
+</vector>
diff --git a/res/xml/notification_access_permission_details.xml b/res/xml/notification_access_permission_details.xml
index 55a47f5..0b76430 100644
--- a/res/xml/notification_access_permission_details.xml
+++ b/res/xml/notification_access_permission_details.xml
@@ -34,19 +34,23 @@
     <CheckBoxPreference
         android:key="type_filter_ongoing"
         android:title="@string/notif_type_ongoing"
-        settings:controller="com.android.settings.applications.specialaccess.notificationaccess.OngoingTypeFilterPreferenceController"/>/>
+        android:icon="@drawable/ic_ongoing_notification"
+        settings:controller="com.android.settings.applications.specialaccess.notificationaccess.OngoingTypeFilterPreferenceController"/>
     <CheckBoxPreference
         android:key="type_filter_conversation"
         android:title="@string/notif_type_conversation"
-        settings:controller="com.android.settings.applications.specialaccess.notificationaccess.ConversationTypeFilterPreferenceController"/>/>
+        android:icon="@drawable/ic_promote_conversation"
+        settings:controller="com.android.settings.applications.specialaccess.notificationaccess.ConversationTypeFilterPreferenceController"/>
     <CheckBoxPreference
         android:key="type_filter_alerting"
         android:title="@string/notif_type_alerting"
-        settings:controller="com.android.settings.applications.specialaccess.notificationaccess.AlertingTypeFilterPreferenceController"/>/>
+        android:icon="@drawable/ic_notification_alert"
+        settings:controller="com.android.settings.applications.specialaccess.notificationaccess.AlertingTypeFilterPreferenceController"/>
     <CheckBoxPreference
         android:key="type_filter_silent"
         android:title="@string/notif_type_silent"
-        settings:controller="com.android.settings.applications.specialaccess.notificationaccess.SilentTypeFilterPreferenceController"/>/>
+        android:icon="@drawable/ic_notification_silence"
+        settings:controller="com.android.settings.applications.specialaccess.notificationaccess.SilentTypeFilterPreferenceController"/>
 
     <Preference
         android:key="bridged_apps"
diff --git a/res/xml/notification_access_settings.xml b/res/xml/notification_access_settings.xml
index 43f7c49..b523d7d 100644
--- a/res/xml/notification_access_settings.xml
+++ b/res/xml/notification_access_settings.xml
@@ -21,4 +21,13 @@
     android:key="notification_access_screen"
     android:title="@string/manage_notification_access_title"
     settings:searchable="false"
-    settings:controller="com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessScreenPreferenceController" />
+    settings:controller="com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessScreenPreferenceController">
+
+    <PreferenceCategory
+        android:key="allowed"
+        android:title="@string/app_permission_summary_allowed"/>
+
+    <PreferenceCategory
+        android:key="not_allowed"
+        android:title="@string/app_permission_summary_not_allowed"/>
+</PreferenceScreen>
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java
index 94736e4..1144f12 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.applications.specialaccess.notificationaccess;
 
+import android.companion.ICompanionDeviceManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -30,8 +32,10 @@
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.notification.NotificationBackend;
 import com.android.settings.widget.EntityHeaderController;
 import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.widget.LayoutPreference;
 
@@ -43,6 +47,10 @@
     private PackageInfo mPackageInfo;
     private PackageManager mPm;
     private CharSequence mServiceName;
+    private ICompanionDeviceManager mCdm;
+    private LocalBluetoothManager mBm;
+    private ComponentName mCn;
+    private int mUserId;
 
     public HeaderPreferenceController(Context context, String key) {
         super(context, key);
@@ -68,6 +76,26 @@
         return this;
     }
 
+    public HeaderPreferenceController setCdm(ICompanionDeviceManager cdm) {
+        mCdm = cdm;
+        return this;
+    }
+
+    public HeaderPreferenceController setBluetoothManager(LocalBluetoothManager bm) {
+        mBm = bm;
+        return this;
+    }
+
+    public HeaderPreferenceController setCn(ComponentName cn) {
+        mCn = cn;
+        return this;
+    }
+
+    public HeaderPreferenceController setUserId(int userId) {
+        mUserId = userId;
+        return this;
+    }
+
     @Override
     public int getAvailabilityStatus() {
         return AVAILABLE;
@@ -88,6 +116,8 @@
                         .getBadgedIcon(mPackageInfo.applicationInfo))
                 .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm))
                 .setSummary(mServiceName)
+                .setSecondSummary(new NotificationBackend().getDeviceList(
+                        mCdm, mBm, mCn.getPackageName(), mUserId))
                 .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
                 .setPackageName(mPackageInfo.packageName)
                 .setUid(mPackageInfo.applicationInfo.uid)
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
index a205b57..16aa078 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
@@ -21,7 +21,7 @@
 import android.app.Activity;
 import android.app.NotificationManager;
 import android.app.settings.SettingsEnums;
-import android.bluetooth.BluetoothAdapter;
+import android.companion.ICompanionDeviceManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -30,6 +30,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.os.Bundle;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -45,14 +46,13 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.applications.AppInfoBase;
 import com.android.settings.applications.manageapplications.ManageApplications;
+import com.android.settings.bluetooth.Utils;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.notification.NotificationBackend;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.RestrictedLockUtilsInternal;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
-import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 
@@ -99,7 +99,12 @@
                 .setFragment(this)
                 .setPackageInfo(mPackageInfo)
                 .setPm(context.getPackageManager())
-                .setServiceName(mServiceName);
+                .setServiceName(mServiceName)
+                .setBluetoothManager(Utils.getLocalBtManager(context))
+                .setCdm(ICompanionDeviceManager.Stub.asInterface(
+                        ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE)))
+                .setCn(mComponentName)
+                .setUserId(mUserId);
         getPreferenceControllers().forEach(controllers -> {
             controllers.forEach(controller -> {
                 if (controller instanceof TypeFilterPreferenceController) {
diff --git a/src/com/android/settings/network/ProviderModelSlice.java b/src/com/android/settings/network/ProviderModelSlice.java
index c020f06..a1fdb1c 100644
--- a/src/com/android/settings/network/ProviderModelSlice.java
+++ b/src/com/android/settings/network/ProviderModelSlice.java
@@ -83,8 +83,9 @@
         // Second section:  Add a carrier item.
         // Third section:  Add the Wi-Fi items which are not connected.
         // Fourth section:  If device has connection problem, this row show the message for user.
+        boolean hasEthernet = isEthernetConnected();
         final ListBuilder listBuilder = mHelper.createListBuilder(getUri());
-        if (mHelper.isAirplaneModeEnabled() && !mWifiManager.isWifiEnabled()) {
+        if (mHelper.isAirplaneModeEnabled() && !mWifiManager.isWifiEnabled() && !hasEthernet) {
             log("Airplane mode is enabled.");
             return listBuilder.build();
         }
@@ -104,12 +105,12 @@
         log("hasCarrier: " + hasCarrier);
 
         // First section:  Add a Ethernet or Wi-Fi item which state is connected.
-        if (isEthernetConnected()) {
+        final WifiSliceItem connectedWifiItem = mHelper.getConnectedWifiItem(wifiList);
+        if (hasEthernet) {
             log("get Ethernet item which is connected");
             listBuilder.addRow(createEthernetRow());
             maxListSize--;
         } else {
-            final WifiSliceItem connectedWifiItem = mHelper.getConnectedWifiItem(wifiList);
             if (connectedWifiItem != null) {
                 log("get Wi-Fi item which is connected");
                 listBuilder.addRow(getWifiSliceItemRow(connectedWifiItem));
@@ -126,7 +127,14 @@
             maxListSize--;
         }
 
-        // Third section:  Add the Wi-Fi items which are not connected.
+        // Third section:  Add the connected Wi-Fi item to Wi-Fi list if the Ethernet is connected.
+        if (connectedWifiItem != null && hasEthernet) {
+            log("get Wi-Fi item which is connected");
+            listBuilder.addRow(getWifiSliceItemRow(connectedWifiItem));
+            maxListSize--;
+        }
+
+        // Fourth section:  Add the Wi-Fi items which are not connected.
         if (wifiList != null && wifiList.size() > 0) {
             log("get Wi-Fi items which are not connected. Wi-Fi items : " + wifiList.size());
 
@@ -140,7 +148,7 @@
             }
         }
 
-        // Fourth section:  If device has connection problem, this row show the message for user.
+        // Fifth section:  If device has connection problem, this row show the message for user.
         // 1) show non_carrier_network_unavailable:
         //    - while no wifi item
         // 2) show all_network_unavailable:
@@ -154,7 +162,7 @@
                 resId = R.string.all_network_unavailable;
             }
 
-            if (!hasCarrier) {
+            if (!hasCarrier && !hasEthernet) {
                 // If there is no item in ProviderModelItem, slice needs a header.
                 listBuilder.setHeader(mHelper.createHeader(
                         NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS));
diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
index 12d6295..a642d9c 100644
--- a/src/com/android/settings/notification/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -20,12 +20,14 @@
 import android.app.NotificationManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.settings.SettingsEnums;
+import android.companion.ICompanionDeviceManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.os.Bundle;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -35,6 +37,7 @@
 import android.view.View;
 import android.widget.Toast;
 
+import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
@@ -57,6 +60,9 @@
 @SearchIndexable
 public class NotificationAccessSettings extends EmptyTextSettings {
     private static final String TAG = "NotifAccessSettings";
+    private static final String ALLOWED_KEY = "allowed";
+    private static final String NOT_ALLOWED_KEY = "not_allowed";
+
     private static final ManagedServiceSettings.Config CONFIG =
             new ManagedServiceSettings.Config.Builder()
                     .setTag(TAG)
@@ -76,6 +82,7 @@
     private DevicePolicyManager mDpm;
     private ServiceListing mServiceListing;
     private IconDrawableFactory mIconDrawableFactory;
+    private NotificationBackend mBackend = new NotificationBackend();
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -93,7 +100,6 @@
                 .setTag(CONFIG.tag)
                 .build();
         mServiceListing.addCallback(this::updateList);
-        setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext));
 
         if (UserManager.get(mContext).isManagedProfile()) {
             // Apps in the work profile do not support notification listeners.
@@ -127,7 +133,11 @@
         final int managedProfileId = Utils.getManagedProfileId(um, UserHandle.myUserId());
 
         final PreferenceScreen screen = getPreferenceScreen();
-        screen.removeAll();
+        final PreferenceCategory allowedCategory = screen.findPreference(ALLOWED_KEY);
+        allowedCategory.removeAll();
+        final PreferenceCategory notAllowedCategory = screen.findPreference(NOT_ALLOWED_KEY);
+        notAllowedCategory.removeAll();
+
         services.sort(new PackageItemInfo.DisplayNameComparator(mPm));
         for (ServiceInfo service : services) {
             final ComponentName cn = new ComponentName(service.packageName, service.name);
@@ -145,9 +155,11 @@
             pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo,
                     UserHandle.getUserId(service.applicationInfo.uid)));
             pref.setKey(cn.flattenToString());
-            pref.setSummary(mNm.isNotificationListenerAccessGranted(cn)
-                    ? R.string.app_permission_summary_allowed
-                    : R.string.app_permission_summary_not_allowed);
+            pref.setSummary(mBackend.getDeviceList(ICompanionDeviceManager.Stub.asInterface(
+                    ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE)),
+                    com.android.settings.bluetooth.Utils.getLocalBtManager(mContext),
+                    service.packageName,
+                    UserHandle.myUserId()));
             if (managedProfileId != UserHandle.USER_NULL
                     && !mDpm.isNotificationListenerServicePermitted(
                     service.packageName, managedProfileId)) {
@@ -173,7 +185,11 @@
                         return true;
                     });
             pref.setKey(cn.flattenToString());
-            screen.addPreference(pref);
+            if (mNm.isNotificationListenerAccessGranted(cn)) {
+                allowedCategory.addPreference(pref);
+            } else {
+                notAllowedCategory.addPreference(pref);
+            }
         }
         highlightPreferenceIfNeeded();
     }
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 8a7e737..4347ca5 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -30,6 +30,7 @@
 import android.app.role.RoleManager;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
+import android.companion.ICompanionDeviceManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -55,14 +56,18 @@
 
 import com.android.settingslib.R;
 import com.android.settingslib.Utils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.notification.ConversationIconFactory;
 import com.android.settingslib.utils.StringUtil;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 public class NotificationBackend {
     private static final String TAG = "NotificationBackend";
@@ -138,6 +143,35 @@
         }
     }
 
+    static public CharSequence getDeviceList(ICompanionDeviceManager cdm, LocalBluetoothManager lbm,
+            String pkg, int userId) {
+        boolean multiple = false;
+        StringBuilder sb = new StringBuilder();
+
+        try {
+            List<String> associatedMacAddrs = cdm.getAssociations(pkg, userId);
+            if (associatedMacAddrs != null) {
+                for (String assocMac : associatedMacAddrs) {
+                    final Collection<CachedBluetoothDevice> cachedDevices =
+                            lbm.getCachedDeviceManager().getCachedDevicesCopy();
+                    for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) {
+                        if (Objects.equals(assocMac, cachedBluetoothDevice.getAddress())) {
+                            if (multiple) {
+                                sb.append(", ");
+                            } else {
+                                multiple = true;
+                            }
+                            sb.append(cachedBluetoothDevice.getName());
+                        }
+                    }
+                }
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error calling CDM", e);
+        }
+        return sb.toString();
+    }
+
     public boolean isSystemApp(Context context, ApplicationInfo app) {
         try {
             PackageInfo info = context.getPackageManager().getPackageInfo(
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
index cb50609..17471b5 100644
--- a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
+++ b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
@@ -26,24 +26,50 @@
 
 import android.app.role.RoleManager;
 import android.app.usage.UsageEvents;
+import android.bluetooth.BluetoothAdapter;
+import android.companion.ICompanionDeviceManager;
+import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Parcel;
 
 import com.android.settings.notification.NotificationBackend.AppRow;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
+import com.google.common.collect.ImmutableList;
+
+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 java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
 public class NotificationBackendTest {
 
+    @Mock
+    LocalBluetoothManager mBm;
+    @Mock
+    ICompanionDeviceManager mCdm;
+    @Mock
+    CachedBluetoothDeviceManager mCbm;
+    ComponentName mCn = new ComponentName("a", "b");
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mBm.getCachedDeviceManager()).thenReturn(mCbm);
+    }
+
     @Test
     public void testMarkAppRow_unblockablePackage() {
         AppRow appRow = new AppRow();
@@ -138,4 +164,69 @@
         parcel.setDataPosition(0);
         return UsageEvents.CREATOR.createFromParcel(parcel);
     }
+
+    @Test
+    public void getDeviceList_noAssociations() throws Exception {
+        when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(null);
+
+        Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
+        CachedBluetoothDevice cbd1 = mock(CachedBluetoothDevice.class);
+        when(cbd1.getAddress()).thenReturn("00:00:00:00:00:10");
+        when(cbd1.getName()).thenReturn("Device 1");
+        cachedDevices.add(cbd1);
+        when(mCbm.getCachedDevicesCopy()).thenReturn(cachedDevices);
+
+        BluetoothAdapter.getDefaultAdapter().enable();
+
+        assertThat(new NotificationBackend().getDeviceList(
+                mCdm, mBm, mCn.getPackageName(), 0).toString()).isEmpty();
+    }
+
+    @Test
+    public void getDeviceList_associationsButNoDevice() throws Exception {
+        List<String> macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20");
+        when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs);
+
+        when(mCbm.getCachedDevicesCopy()).thenReturn(new ArrayList<>());
+
+        assertThat(new NotificationBackend().getDeviceList(
+                mCdm, mBm, mCn.getPackageName(), 0).toString()).isEmpty();
+    }
+
+    @Test
+    public void getDeviceList_singleDevice() throws Exception {
+        List<String> macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20");
+        when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs);
+
+        Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
+        CachedBluetoothDevice cbd1 = mock(CachedBluetoothDevice.class);
+        when(cbd1.getAddress()).thenReturn(macs.get(0));
+        when(cbd1.getName()).thenReturn("Device 1");
+        cachedDevices.add(cbd1);
+        when(mCbm.getCachedDevicesCopy()).thenReturn(cachedDevices);
+
+        assertThat(new NotificationBackend().getDeviceList(
+                mCdm, mBm, mCn.getPackageName(), 0).toString()).isEqualTo("Device 1");
+    }
+
+    @Test
+    public void getDeviceList_multipleDevices() throws Exception {
+        List<String> macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20");
+        when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs);
+
+        Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
+        CachedBluetoothDevice cbd1 = mock(CachedBluetoothDevice.class);
+        when(cbd1.getAddress()).thenReturn(macs.get(0));
+        when(cbd1.getName()).thenReturn("Device 1");
+        cachedDevices.add(cbd1);
+
+        CachedBluetoothDevice cbd2 = mock(CachedBluetoothDevice.class);
+        when(cbd2.getAddress()).thenReturn(macs.get(1));
+        when(cbd2.getName()).thenReturn("Device 2");
+        cachedDevices.add(cbd2);
+        when(mCbm.getCachedDevicesCopy()).thenReturn(cachedDevices);
+
+        assertThat(new NotificationBackend().getDeviceList(
+                mCdm, mBm, mCn.getPackageName(), 0).toString()).isEqualTo("Device 1, Device 2");
+    }
 }
diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
index 476d854..705f60e 100644
--- a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
+++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
@@ -264,7 +264,7 @@
 
     @Test
     @UiThreadTest
-    public void getSlice_connectedEthernet_getOneEthernetAndOneCarrierAndTwoWiFi() {
+    public void getSlice_haveEthernetAndCarrierAndTwoDisconnectedWifi_getFourRow() {
         mWifiList.clear();
         mockWifiItemCondition(mMockWifiSliceItem1, "wifi1", "wifi1",
                 WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi1_key", true);
@@ -273,9 +273,29 @@
                 WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi2_key", true);
         mWifiList.add(mMockWifiSliceItem2);
         mMockNetworkProviderWorker.updateSelfResults(mWifiList);
-        when(mProviderModelSliceHelper.isAirplaneModeEnabled()).thenReturn(false);
-        when(mProviderModelSliceHelper.hasCarrier()).thenReturn(true);
-        when(mProviderModelSliceHelper.isDataSimActive()).thenReturn(true);
+        mockHelperCondition(false, true, true, null);
+        when(mMockNetworkProviderWorker.isEthernetConnected()).thenReturn(true);
+
+        final Slice slice = mMockProviderModelSlice.getSlice();
+
+        assertThat(slice).isNotNull();
+        assertThat(mMockProviderModelSlice.hasCreateEthernetRow()).isTrue();
+        verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
+        verify(mListBuilder, times(4)).addRow(any(ListBuilder.RowBuilder.class));
+    }
+
+    @Test
+    @UiThreadTest
+    public void getSlice_haveEthernetAndCarrierAndConnectedWifiAndDisconnectedWifi_getFourRow() {
+        mWifiList.clear();
+        mockWifiItemCondition(mMockWifiSliceItem1, "wifi1", "wifi1",
+                WifiEntry.CONNECTED_STATE_CONNECTED, "wifi1_key", true);
+        mWifiList.add(mMockWifiSliceItem1);
+        mockWifiItemCondition(mMockWifiSliceItem2, "wifi2", "wifi2",
+                WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi2_key", true);
+        mWifiList.add(mMockWifiSliceItem2);
+        mMockNetworkProviderWorker.updateSelfResults(mWifiList);
+        mockHelperCondition(false, true, true, mWifiList.get(0));
         when(mMockNetworkProviderWorker.isEthernetConnected()).thenReturn(true);
 
         final Slice slice = mMockProviderModelSlice.getSlice();