Merge changes from topic "Slice of Provider Model"

* changes:
  Slice of Provider Model: add slice structure
  The helper for slice of carrier and non-Carrier, used by ProviderModelSlice.
diff --git a/res/drawable/ic_signal_strength_zero_bar_no_internet.xml b/res/drawable/ic_signal_strength_zero_bar_no_internet.xml
new file mode 100644
index 0000000..f38a368
--- /dev/null
+++ b/res/drawable/ic_signal_strength_zero_bar_no_internet.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+<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="#FF000000"
+        android:pathData="M2,22l16,0l0,-2l-11,0l13,-13l0,1l2,0l0,-6z"
+        android:strokeAlpha="0.3"
+        android:fillAlpha="0.3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,10h2v8h-2z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,20h2v2h-2z"/>
+</vector>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f862174..50f9424 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -12527,6 +12527,15 @@
     <!-- Label text to turn off airplane mode. [CHAR LIMIT=40] -->
     <string name="turn_off_airplane_mode">Turn off Airplane Mode</string>
 
+    <!-- Provider Model: Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
+    <string name="mobile_data_connection_active">Active, <xliff:g id="network_data_rat" example="LTE">%1$s</xliff:g></string>
+    <!-- Provider Model: Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
+    <string name="mobile_data_off_summary">Internet off</string>
+    <!-- Provider Model: Summary indicating that non-carrier network unavailable [CHAR LIMIT=50] -->
+    <string name="non_carrier_network_unavailable">Non\u2011carrier networks unavailable</string>
+    <!-- Provider Model: Summary indicating that non-carrier and carrier network unavailable [CHAR LIMIT=50] -->
+    <string name="all_network_unavailable">Networks unavailable</string>
+
     <!-- Summary for preference when Bedtime mode is on [CHAR LIMIT=NONE] -->
     <string name="aware_summary_when_bedtime_on">Unavailable because bedtime mode is on</string>
 
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 4390aad..eca3869 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1156,5 +1156,4 @@
     public static boolean isProviderModelEnabled(Context context) {
         return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
     }
-
 }
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index 90e3ac4..c2881ac 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -103,8 +103,10 @@
         implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback,
         WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener {
 
-    private static final String TAG = "NetworkProviderSettings";
+    public static final String ACTION_NETWORK_PROVIDER_SETTINGS =
+            "android.settings.NETWORK_PROVIDER_SETTINGS";
 
+    private static final String TAG = "NetworkProviderSettings";
     // IDs of context menu
     static final int MENU_ID_CONNECT = Menu.FIRST + 1;
     @VisibleForTesting
diff --git a/src/com/android/settings/network/ProviderModelSlice.java b/src/com/android/settings/network/ProviderModelSlice.java
new file mode 100644
index 0000000..f6908c9
--- /dev/null
+++ b/src/com/android/settings/network/ProviderModelSlice.java
@@ -0,0 +1,204 @@
+/*
+ * 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.network;
+
+
+import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
+
+import static com.android.settings.slices.CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.slice.Slice;
+import androidx.slice.builders.ListBuilder;
+
+import com.android.settings.R;
+import com.android.settings.SubSettings;
+import com.android.settings.network.telephony.MobileNetworkUtils;
+import com.android.settings.network.telephony.NetworkProviderWorker;
+import com.android.settings.slices.CustomSliceable;
+import com.android.settings.slices.SliceBackgroundWorker;
+import com.android.settings.slices.SliceBuilderUtils;
+import com.android.settings.wifi.slice.WifiSlice;
+import com.android.settings.wifi.slice.WifiSliceItem;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * {@link CustomSliceable} for Wi-Fi and mobile data connection, used by generic clients.
+ */
+// ToDo If the provider model become default design in the future, the code needs to refactor
+// the whole structure and use new "data object", and then split provider model out of old design.
+public class ProviderModelSlice extends WifiSlice {
+
+    private static final String TAG = "ProviderModelSlice";
+    private final ProviderModelSliceHelper mHelper;
+
+    public ProviderModelSlice(Context context) {
+        super(context);
+        mHelper = getHelper();
+    }
+
+    @Override
+    public Uri getUri() {
+        return PROVIDER_MODEL_SLICE_URI;
+    }
+
+    private static void log(String s) {
+        Log.d(TAG, s);
+    }
+
+    protected boolean isApRowCollapsed() {
+        return false;
+    }
+
+    @Override
+    public Slice getSlice() {
+        // The provider model slice step:
+        // First section:  Add a Wi-Fi item which state is connected.
+        // 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.
+
+        if (mHelper.isAirplaneModeEnabled()) {
+            log("Airplane mode is enabled.");
+            // ToDo Next CL will add the Airplane mode Message.
+            return mHelper.createListBuilder(getUri()).build();
+        }
+
+        int maxListSize = 0;
+        List<WifiSliceItem> wifiList = null;
+        final NetworkProviderWorker worker = getWorker();
+        if (worker != null) {
+            // get Wi-Fi list.
+            wifiList = worker.getResults();
+            maxListSize = worker.getApRowCount();
+        } else {
+            log("network provider worker is null.");
+        }
+
+        final boolean hasCarrier = mHelper.hasCarrier();
+        log("hasCarrier: " + hasCarrier);
+
+
+        final ListBuilder listBuilder = mHelper.createListBuilder(getUri());
+
+        // First section:  Add a Wi-Fi item which state is connected.
+        final WifiSliceItem connectedWifiItem = mHelper.getConnectedWifiItem(wifiList);
+        if (connectedWifiItem != null) {
+            log("get Wi-Fi item witch is connected");
+            listBuilder.addRow(getWifiSliceItemRow(connectedWifiItem));
+            maxListSize--;
+        }
+
+        // Second section:  Add a carrier item.
+        if (hasCarrier) {
+            listBuilder.addRow(mHelper.createCarrierRow());
+            maxListSize--;
+        }
+
+        // Third section:  Add the Wi-Fi items which are not connected.
+        if (wifiList != null) {
+            log("get Wi-Fi items which are not connected");
+            final List<WifiSliceItem> disconnectedWifiList = wifiList.stream()
+                    .filter(wifiSliceItem -> wifiSliceItem.getConnectedState()
+                            != WifiEntry.CONNECTED_STATE_CONNECTED)
+                    .limit(maxListSize)
+                    .collect(Collectors.toList());
+            for (WifiSliceItem item : disconnectedWifiList) {
+                listBuilder.addRow(getWifiSliceItemRow(item));
+            }
+        }
+
+        // Fourth 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:
+        //    - while no wifi item + no carrier
+        //    - while no wifi item + no data capability
+        if (worker == null || wifiList == null) {
+            log("wifiList is null");
+            int resId = R.string.non_carrier_network_unavailable;
+            if (!hasCarrier || mHelper.isNoCarrierData()) {
+                log("No carrier item or no carrier data.");
+                resId = R.string.all_network_unavailable;
+            }
+
+            if (!hasCarrier) {
+                // If there is no item in ProviderModelItem, slice needs a header.
+                listBuilder.setHeader(mHelper.createHeader());
+            }
+            listBuilder.addGridRow(mHelper.createMessageGridRow(resId));
+        }
+
+        return listBuilder.build();
+    }
+
+    /**
+     * Update the current carrier's mobile data status.
+     */
+    @Override
+    public void onNotifyChange(Intent intent) {
+        final SubscriptionManager subscriptionManager = mHelper.getSubscriptionManager();
+        if (subscriptionManager == null) {
+            return;
+        }
+        final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
+                mHelper.isMobileDataEnabled());
+        final int defaultSubId = subscriptionManager.getDefaultDataSubscriptionId();
+        log("defaultSubId:" + defaultSubId);
+        if (!SubscriptionManager.isUsableSubscriptionId(defaultSubId)) {
+            return; // No subscription - do nothing.
+        }
+
+        MobileNetworkUtils.setMobileDataEnabled(mContext, defaultSubId, newState,
+                false /* disableOtherSubscriptions */);
+    }
+
+    @Override
+    public Intent getIntent() {
+        final String screenTitle = mContext.getText(R.string.provider_internet_settings).toString();
+        return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+                NetworkProviderSettings.class.getName(), "" /* key */, screenTitle,
+                SettingsEnums.SLICE)
+                .setClassName(mContext.getPackageName(), SubSettings.class.getName())
+                .setData(getUri());
+    }
+
+    @Override
+    public Class getBackgroundWorkerClass() {
+        return NetworkProviderWorker.class;
+    }
+
+    @VisibleForTesting
+    ProviderModelSliceHelper getHelper() {
+        return new ProviderModelSliceHelper(mContext, this);
+    }
+
+    @VisibleForTesting
+    NetworkProviderWorker getWorker() {
+        return SliceBackgroundWorker.getInstance(getUri());
+    }
+}
diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java
new file mode 100644
index 0000000..482695a
--- /dev/null
+++ b/src/com/android/settings/network/ProviderModelSliceHelper.java
@@ -0,0 +1,275 @@
+/*
+ * 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.network;
+
+import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.slice.builders.GridRowBuilder;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.SliceAction;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.network.telephony.MobileNetworkUtils;
+import com.android.settings.slices.CustomSliceable;
+import com.android.settings.wifi.slice.WifiSliceItem;
+import com.android.settingslib.WirelessUtils;
+import com.android.settingslib.net.SignalStrengthUtil;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+/**
+ * The helper is for slice of carrier and non-Carrier, used by ProviderModelSlice.
+ */
+public class ProviderModelSliceHelper {
+    private static final String TAG = "ProviderModelSlice";
+    private final SubscriptionManager mSubscriptionManager;
+    private final TelephonyManager mTelephonyManager;
+    protected final Context mContext;
+    private CustomSliceable mSliceable;
+
+    public ProviderModelSliceHelper(Context context, CustomSliceable sliceable) {
+        mContext = context;
+        mSliceable = sliceable;
+        mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
+    }
+
+    private static void log(String s) {
+        Log.d(TAG, s);
+    }
+
+    protected ListBuilder.HeaderBuilder createHeader() {
+        return new ListBuilder.HeaderBuilder()
+                .setTitle(mContext.getText(R.string.summary_placeholder))
+                .setPrimaryAction(getPrimarySliceAction());
+    }
+
+    protected ListBuilder createListBuilder(Uri uri) {
+        final ListBuilder builder = new ListBuilder(mContext, uri, ListBuilder.INFINITY)
+                .setAccentColor(-1)
+                .setKeywords(getKeywords());
+        return builder;
+    }
+
+    protected GridRowBuilder createMessageGridRow(int messageResId) {
+        final CharSequence title = mContext.getText(messageResId);
+        return new GridRowBuilder()
+                // Add cells to the grid row.
+                .addCell(new GridRowBuilder.CellBuilder().addTitleText(title))
+                .setPrimaryAction(getPrimarySliceAction());
+    }
+
+    @Nullable
+    protected WifiSliceItem getConnectedWifiItem(List<WifiSliceItem> wifiList) {
+        if (wifiList == null) {
+            return null;
+        }
+        Optional<WifiSliceItem> item = wifiList.stream()
+                .filter(x -> x.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED)
+                .findFirst();
+        return item.isPresent() ? item.get() : null;
+    }
+
+    protected boolean hasCarrier() {
+        if (isAirplaneModeEnabled()
+                || mSubscriptionManager == null || mTelephonyManager == null
+                || mSubscriptionManager.getDefaultDataSubscriptionId()
+                == mSubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return false;
+        }
+        return true;
+    }
+
+    protected ListBuilder.RowBuilder createCarrierRow() {
+        final String title = getMobileTitle();
+        final String summary = getMobileSummary();
+        Drawable drawable = mContext.getDrawable(
+                R.drawable.ic_signal_strength_zero_bar_no_internet);
+        try {
+            drawable = getMobileDrawable(drawable);
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+        final IconCompat levelIcon = Utils.createIconWithDrawable(drawable);
+        final PendingIntent toggleAction = mSliceable.getBroadcastIntent(mContext);
+        final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
+                "mobile_toggle" /* actionTitle */, isMobileDataEnabled());
+        final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder()
+                .setTitle(title)
+                .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE)
+                .addEndItem(toggleSliceAction)
+                .setPrimaryAction(toggleSliceAction)
+                .setSubtitle(summary);
+        return rowBuilder;
+    }
+
+    protected SliceAction getPrimarySliceAction() {
+        return SliceAction.createDeeplink(
+                getPrimaryAction(),
+                Utils.createIconWithDrawable(new ColorDrawable(Color.TRANSPARENT)),
+                ListBuilder.ICON_IMAGE, mContext.getText(R.string.summary_placeholder));
+    }
+
+    private PendingIntent getPrimaryAction() {
+        final Intent intent = new Intent("android.settings.NETWORK_PROVIDER_SETTINGS")
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return PendingIntent.getActivity(mContext, 0 /* requestCode */,
+                intent, PendingIntent.FLAG_IMMUTABLE /* flags */);
+    }
+
+    private boolean shouldInflateSignalStrength(int subId) {
+        return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
+    }
+
+    protected boolean isAirplaneModeEnabled() {
+        return WirelessUtils.isAirplaneModeOn(mContext);
+    }
+
+    protected boolean isMobileDataEnabled() {
+        if (mTelephonyManager == null) {
+            return false;
+        }
+        return mTelephonyManager.isDataEnabled();
+    }
+
+    protected boolean isDataSimActive() {
+        return MobileNetworkUtils.activeNetworkIsCellular(mContext);
+    }
+
+    protected boolean isNoCarrierData() {
+        if (mTelephonyManager == null) {
+            return false;
+        }
+        boolean mobileDataOnAndNoData = isMobileDataEnabled()
+                && mTelephonyManager.getDataState() != mTelephonyManager.DATA_CONNECTED;
+        ServiceState serviceState = mTelephonyManager.getServiceState();
+        boolean mobileDataOffAndOutOfService = !isMobileDataEnabled() && serviceState != null
+                && serviceState.getState() == serviceState.STATE_OUT_OF_SERVICE;
+        log("mobileDataOnAndNoData: " + mobileDataOnAndNoData
+                + ",mobileDataOffAndOutOfService: " + mobileDataOffAndOutOfService);
+        return mobileDataOnAndNoData || mobileDataOffAndOutOfService;
+    }
+
+    private boolean isAirplaneSafeNetworksModeEnabled() {
+        // TODO: isAirplaneSafeNetworksModeEnabled is not READY
+        return false;
+    }
+
+    @VisibleForTesting
+    Drawable getMobileDrawable(Drawable drawable) throws Throwable {
+        // set color and drawable
+        if (mTelephonyManager == null) {
+            log("mTelephonyManager == null");
+            return drawable;
+        }
+        if (!isNoCarrierData()) {
+            Semaphore lock = new Semaphore(0);
+            AtomicReference<Drawable> shared = new AtomicReference<>();
+            ThreadUtils.postOnMainThread(() -> {
+                shared.set(getDrawableWithSignalStrength());
+                lock.release();
+            });
+            lock.acquire();
+            drawable = shared.get();
+        }
+
+        if (isDataSimActive()) {
+            drawable.setTint(Utils.getColorAccentDefaultColor(mContext));
+        }
+        return drawable;
+    }
+
+    /**
+     * To get the signal bar icon with level.
+     *
+     * @return The Drawable which is a signal bar icon with level.
+     */
+    public Drawable getDrawableWithSignalStrength() {
+        final SignalStrength strength = mTelephonyManager.getSignalStrength();
+        int level = (strength == null) ? 0 : strength.getLevel();
+        int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+        if (mSubscriptionManager != null && shouldInflateSignalStrength(
+                mSubscriptionManager.getDefaultDataSubscriptionId())) {
+            level += 1;
+            numLevels += 1;
+        }
+        return MobileNetworkUtils.getSignalStrengthIcon(mContext, level, numLevels,
+                NO_CELL_DATA_TYPE_ICON, false);
+    }
+
+    private String getMobileSummary() {
+        String summary = "";
+        //TODO: get radio technology.
+        String networkType = "";
+        if (isDataSimActive()) {
+            summary = mContext.getString(R.string.mobile_data_connection_active, networkType);
+        } else if (!isMobileDataEnabled()) {
+            summary = mContext.getString(R.string.mobile_data_off_summary);
+        }
+        return summary;
+    }
+
+    private String getMobileTitle() {
+        String title = mContext.getText(R.string.mobile_data_settings_title).toString();
+        if (mSubscriptionManager == null) {
+            return title;
+        }
+        final SubscriptionInfo defaultSubscription = mSubscriptionManager.getActiveSubscriptionInfo(
+                mSubscriptionManager.getDefaultDataSubscriptionId());
+        if (defaultSubscription != null) {
+            title = defaultSubscription.getDisplayName().toString();
+        }
+        return title;
+    }
+
+    protected SubscriptionManager getSubscriptionManager() {
+        return mSubscriptionManager;
+    }
+
+    private Set<String> getKeywords() {
+        final String keywords = mContext.getString(R.string.keywords_internet);
+        return Arrays.stream(TextUtils.split(keywords, ","))
+                .map(String::trim)
+                .collect(Collectors.toSet());
+    }
+}
diff --git a/src/com/android/settings/network/telephony/NetworkProviderWorker.java b/src/com/android/settings/network/telephony/NetworkProviderWorker.java
new file mode 100644
index 0000000..bc82901
--- /dev/null
+++ b/src/com/android/settings/network/telephony/NetworkProviderWorker.java
@@ -0,0 +1,234 @@
+/*
+ * 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.network.telephony;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.network.MobileDataContentObserver;
+import com.android.settings.network.MobileDataEnabledListener;
+import com.android.settings.network.SubscriptionsChangeListener;
+import com.android.settings.wifi.slice.WifiScanWorker;
+
+import java.util.Collections;
+import java.util.concurrent.Executor;
+
+
+/**
+ * BackgroundWorker for Provider Model slice.
+ */
+public class NetworkProviderWorker extends WifiScanWorker implements
+        SignalStrengthListener.Callback, MobileDataEnabledListener.Client,
+        DataConnectivityListener.Client,
+        SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
+    private static final String TAG = "NetworkProviderWorker";
+    private static final int PROVIDER_MODEL_DEFAULT_EXPANDED_ROW_COUNT = 4;
+    private DataContentObserver mMobileDataObserver;
+    private SignalStrengthListener mSignalStrengthListener;
+    private SubscriptionsChangeListener mSubscriptionsListener;
+    private MobileDataEnabledListener mDataEnabledListener;
+    private DataConnectivityListener mConnectivityListener;
+
+    private final Context mContext;
+    @VisibleForTesting
+    final PhoneStateListener mPhoneStateListener;
+    private final SubscriptionManager mSubscriptionManager;
+    private final TelephonyManager mTelephonyManager;
+
+    public NetworkProviderWorker(Context context, Uri uri) {
+        super(context, uri);
+        // Mobile data worker
+        final Handler handler = new Handler(Looper.getMainLooper());
+        mMobileDataObserver = new DataContentObserver(handler, this);
+
+        mContext = context;
+        mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+
+        mPhoneStateListener = new NetworkProviderPhoneStateListener(handler::post);
+        mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
+        mDataEnabledListener = new MobileDataEnabledListener(context, this);
+        mConnectivityListener = new DataConnectivityListener(context, this);
+        mSignalStrengthListener = new SignalStrengthListener(context, this);
+    }
+
+    @Override
+    protected void onSlicePinned() {
+        mMobileDataObserver.register(mContext,
+                getDefaultSubscriptionId(mSubscriptionManager));
+
+        mSubscriptionsListener.start();
+        mDataEnabledListener.start(SubscriptionManager.getDefaultDataSubscriptionId());
+        mConnectivityListener.start();
+        mSignalStrengthListener.resume();
+        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE
+                | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE
+                | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED);
+
+        super.onSlicePinned();
+    }
+
+    @Override
+    protected void onSliceUnpinned() {
+        mMobileDataObserver.unregister(mContext);
+        mSubscriptionsListener.stop();
+        mDataEnabledListener.stop();
+        mConnectivityListener.stop();
+        mSignalStrengthListener.pause();
+        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        super.onSliceUnpinned();
+    }
+
+    @Override
+    public void close() {
+        mMobileDataObserver = null;
+        super.close();
+    }
+
+    @Override
+    public int getApRowCount() {
+        return PROVIDER_MODEL_DEFAULT_EXPANDED_ROW_COUNT;
+    }
+
+    /**
+     * To update the Slice.
+     */
+    public void updateSlice() {
+        notifySliceChange();
+    }
+
+    @Override
+    public void onSubscriptionsChanged() {
+        int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        Log.d(TAG, "onSubscriptionsChanged: defaultDataSubId:" + defaultDataSubId);
+
+        mSignalStrengthListener.updateSubscriptionIds(
+                SubscriptionManager.isUsableSubscriptionId(defaultDataSubId)
+                        ? Collections.singleton(defaultDataSubId) : Collections.emptySet());
+        if (defaultDataSubId != mDataEnabledListener.getSubId()) {
+            mDataEnabledListener.stop();
+            mDataEnabledListener.start(defaultDataSubId);
+        }
+        updateSlice();
+    }
+
+    @Override
+    public void onSignalStrengthChanged() {
+        Log.d(TAG, "onSignalStrengthChanged");
+        updateSlice();
+    }
+
+    @Override
+    public void onAirplaneModeChanged(boolean airplaneModeEnabled) {
+        Log.d(TAG, "onAirplaneModeChanged");
+        updateSlice();
+    }
+
+    @Override
+    public void onMobileDataEnabledChange() {
+        Log.d(TAG, "onMobileDataEnabledChange");
+        updateSlice();
+    }
+
+    @Override
+    public void onDataConnectivityChange() {
+        Log.d(TAG, "onDataConnectivityChange");
+        updateSlice();
+    }
+
+    /**
+     * Listen to update of mobile data change.
+     */
+    public class DataContentObserver extends ContentObserver {
+        private final NetworkProviderWorker mNetworkProviderWorker;
+
+        public DataContentObserver(Handler handler, NetworkProviderWorker backgroundWorker) {
+            super(handler);
+            mNetworkProviderWorker = backgroundWorker;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mNetworkProviderWorker.updateSlice();
+        }
+
+        /**
+         * To register the observer for mobile data changed.
+         * @param context the Context object.
+         * @param subId the default data subscription id.
+         */
+        public void register(Context context, int subId) {
+            final Uri uri = MobileDataContentObserver.getObservableUri(context, subId);
+            context.getContentResolver().registerContentObserver(uri, false, this);
+        }
+
+        /**
+         * To unregister the observer for mobile data changed.
+         * @param context the Context object.
+         */
+        public void unregister(Context context) {
+            context.getContentResolver().unregisterContentObserver(this);
+        }
+    }
+
+    class NetworkProviderPhoneStateListener extends PhoneStateListener {
+        NetworkProviderPhoneStateListener(Executor executor) {
+            super(executor);
+        }
+
+        @Override
+        public void onServiceStateChanged(ServiceState state) {
+            Log.d(TAG, "onServiceStateChanged voiceState=" + state.getState()
+                    + " dataState=" + state.getDataRegistrationState());
+            updateSlice();
+        }
+
+        @Override
+        public void onActiveDataSubscriptionIdChanged(int subId) {
+            Log.d(TAG, "onActiveDataSubscriptionIdChanged: subId=" + subId);
+            updateSlice();
+        }
+
+        @Override
+        public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
+            Log.d(TAG, "onDisplayInfoChanged: telephonyDisplayInfo=" + telephonyDisplayInfo);
+            updateSlice();
+        }
+    }
+
+    protected static int getDefaultSubscriptionId(SubscriptionManager subscriptionManager) {
+        final SubscriptionInfo defaultSubscription = subscriptionManager.getActiveSubscriptionInfo(
+                subscriptionManager.getDefaultDataSubscriptionId());
+
+        if (defaultSubscription == null) {
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID; // No default subscription
+        }
+        return defaultSubscription.getSubscriptionId();
+    }
+}
diff --git a/src/com/android/settings/panel/InternetConnectivityPanel.java b/src/com/android/settings/panel/InternetConnectivityPanel.java
index 6ae7089..312bf75 100644
--- a/src/com/android/settings/panel/InternetConnectivityPanel.java
+++ b/src/com/android/settings/panel/InternetConnectivityPanel.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.panel;
 
+import static com.android.settings.network.NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS;
+
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
@@ -51,17 +53,19 @@
 
     @Override
     public CharSequence getTitle() {
-        return mContext.getText(R.string.internet_connectivity_panel_title);
+        return mContext.getText(Utils.isProviderModelEnabled(mContext)
+                ? R.string.provider_internet_settings : R.string.internet_connectivity_panel_title);
     }
 
     @Override
     public List<Uri> getSlices() {
         final List<Uri> uris = new ArrayList<>();
-        uris.add(CustomSliceRegistry.WIFI_SLICE_URI);
-        uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI);
         if (Utils.isProviderModelEnabled(mContext)) {
+            uris.add(CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI);
             uris.add(CustomSliceRegistry.AIRPLANE_SAFE_NETWORKS_SLICE_URI);
         } else {
+            uris.add(CustomSliceRegistry.WIFI_SLICE_URI);
+            uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI);
             uris.add(AirplaneModePreferenceController.SLICE_URI);
         }
         return uris;
@@ -69,7 +73,8 @@
 
     @Override
     public Intent getSeeMoreIntent() {
-        return new Intent(Settings.ACTION_WIRELESS_SETTINGS)
+        return new Intent(Utils.isProviderModelEnabled(mContext)
+                ? ACTION_NETWORK_PROVIDER_SETTINGS : Settings.ACTION_WIRELESS_SETTINGS)
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     }
 
diff --git a/src/com/android/settings/slices/CustomSliceRegistry.java b/src/com/android/settings/slices/CustomSliceRegistry.java
index cf23cbd..be71b30 100644
--- a/src/com/android/settings/slices/CustomSliceRegistry.java
+++ b/src/com/android/settings/slices/CustomSliceRegistry.java
@@ -40,6 +40,7 @@
 import com.android.settings.media.MediaOutputIndicatorSlice;
 import com.android.settings.media.RemoteMediaSlice;
 import com.android.settings.network.AirplaneSafeNetworksSlice;
+import com.android.settings.network.ProviderModelSlice;
 import com.android.settings.network.telephony.MobileDataSlice;
 import com.android.settings.notification.zen.ZenModeButtonPreferenceController;
 import com.android.settings.wifi.calling.WifiCallingSliceHelper;
@@ -167,6 +168,17 @@
             .appendEncodedPath(SettingsSlicesContract.PATH_SETTING_ACTION)
             .appendPath("mobile_data")
             .build();
+
+    /**
+     * Full {@link Uri} for the Provider Model Slice.
+     */
+    public static final Uri PROVIDER_MODEL_SLICE_URI = new Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+            .appendEncodedPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+            .appendPath("provider_model")
+            .build();
+
     /**
      * Full {@link Uri} for the Alarm volume Slice.
      */
@@ -176,6 +188,7 @@
             .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
             .appendPath("alarm_volume")
             .build();
+
     /**
      * Full {@link Uri} for the Call Volume Slice.
      */
@@ -319,6 +332,7 @@
         sUriToSlice.put(LOW_STORAGE_SLICE_URI, LowStorageSlice.class);
         sUriToSlice.put(MEDIA_OUTPUT_INDICATOR_SLICE_URI, MediaOutputIndicatorSlice.class);
         sUriToSlice.put(MOBILE_DATA_SLICE_URI, MobileDataSlice.class);
+        sUriToSlice.put(PROVIDER_MODEL_SLICE_URI, ProviderModelSlice.class);
         sUriToSlice.put(WIFI_SLICE_URI, WifiSlice.class);
         sUriToSlice.put(DARK_THEME_SLICE_URI, DarkThemeSlice.class);
         sUriToSlice.put(REMOTE_MEDIA_SLICE_URI, RemoteMediaSlice.class);
diff --git a/src/com/android/settings/wifi/slice/WifiScanWorker.java b/src/com/android/settings/wifi/slice/WifiScanWorker.java
index 16c4ebc..6c0f4aa 100644
--- a/src/com/android/settings/wifi/slice/WifiScanWorker.java
+++ b/src/com/android/settings/wifi/slice/WifiScanWorker.java
@@ -61,7 +61,7 @@
     @VisibleForTesting
     final LifecycleRegistry mLifecycleRegistry;
     @VisibleForTesting
-    WifiPickerTracker mWifiPickerTracker;
+    protected WifiPickerTracker mWifiPickerTracker;
     // Worker thread used for WifiPickerTracker work
     private final HandlerThread mWorkerThread;
 
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index 7d009f4..56d87c3 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -147,7 +147,7 @@
         return builder;
     }
 
-    private ListBuilder.RowBuilder getWifiSliceItemRow(WifiSliceItem wifiSliceItem) {
+    protected ListBuilder.RowBuilder getWifiSliceItemRow(WifiSliceItem wifiSliceItem) {
         final CharSequence title = wifiSliceItem.getTitle();
         final IconCompat levelIcon = getWifiSliceItemLevelIcon(wifiSliceItem);
         final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder()
diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
new file mode 100644
index 0000000..abbc2ec
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
@@ -0,0 +1,307 @@
+/*
+ * 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.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.Uri;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import androidx.slice.Slice;
+import androidx.slice.builders.GridRowBuilder;
+import androidx.slice.builders.GridRowBuilder.CellBuilder;
+import androidx.slice.builders.ListBuilder;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.Utils;
+import com.android.settings.slices.CustomSliceable;
+import com.android.settings.testutils.ResourcesUtils;
+import com.android.settings.wifi.slice.WifiSliceItem;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class ProviderModelSliceHelperTest {
+    private Context mContext;
+    private MockProviderModelSliceHelper mProviderModelSliceHelper;
+    private PersistableBundle mBundle;
+    private Network mNetwork;
+    private NetworkCapabilities mNetworkCapabilities;
+
+    @Mock
+    private SubscriptionManager mSubscriptionManager;
+    @Mock
+    private CarrierConfigManager mCarrierConfigManager;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private ServiceState mServiceState;
+    @Mock
+    private WifiSliceItem mWifiSliceItem1;
+    @Mock
+    private WifiSliceItem mWifiSliceItem2;
+    @Mock
+    private SubscriptionInfo mDefaultDataSubscriptionInfo;
+    @Mock
+    private Drawable mDrawableWithSignalStrength;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        mBundle = new PersistableBundle();
+        mNetwork = new Network(anyInt());
+
+        when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+        when(mContext.getSystemService(CarrierConfigManager.class)).thenReturn(
+                mCarrierConfigManager);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle);
+        when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
+        when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork);
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+        when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+
+        TestCustomSliceable testCustomSliceable = new TestCustomSliceable();
+        mProviderModelSliceHelper = new MockProviderModelSliceHelper(mContext, testCustomSliceable);
+    }
+
+    @Test
+    public void createMessageGridRow_inputTheResourceId_verifyTitle() {
+        int messageResId = ResourcesUtils.getResourcesId(mContext, "string",
+                "non_carrier_network_unavailable");
+        CharSequence title = ResourcesUtils.getResourcesString(mContext,
+                "non_carrier_network_unavailable");
+
+        GridRowBuilder testGridRow = mProviderModelSliceHelper.createMessageGridRow(messageResId);
+        List<CellBuilder> cellItem = testGridRow.getCells();
+
+        assertThat(cellItem.get(0).getTitle()).isEqualTo(title);
+    }
+
+    @Test
+    public void getConnectedWifiItem_inputListInvolveOneConnectedWifiItem_verifyReturnItem() {
+        when(mWifiSliceItem1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+        when(mWifiSliceItem2.getConnectedState()).thenReturn(
+                WifiEntry.CONNECTED_STATE_DISCONNECTED);
+        List<WifiSliceItem> wifiList = new ArrayList<>();
+        wifiList.add(mWifiSliceItem1);
+        wifiList.add(mWifiSliceItem2);
+
+        WifiSliceItem testItem = mProviderModelSliceHelper.getConnectedWifiItem(wifiList);
+
+        assertThat(testItem).isNotNull();
+        assertEquals(mWifiSliceItem1, testItem);
+    }
+
+    @Test
+    public void getConnectedWifiItem_inputListInvolveNoConnectedWifiItem_verifyReturnItem() {
+        when(mWifiSliceItem1.getConnectedState()).thenReturn(
+                WifiEntry.CONNECTED_STATE_DISCONNECTED);
+        when(mWifiSliceItem2.getConnectedState()).thenReturn(
+                WifiEntry.CONNECTED_STATE_DISCONNECTED);
+        List<WifiSliceItem> wifiList = new ArrayList<>();
+        wifiList.add(mWifiSliceItem1);
+        wifiList.add(mWifiSliceItem2);
+
+        WifiSliceItem testItem = mProviderModelSliceHelper.getConnectedWifiItem(wifiList);
+
+        assertThat(testItem).isNull();
+    }
+
+    @Test
+    public void getConnectedWifiItem_inputNull_verifyReturnItem() {
+        List<WifiSliceItem> wifiList = null;
+
+        WifiSliceItem testItem = mProviderModelSliceHelper.getConnectedWifiItem(wifiList);
+
+        assertThat(testItem).isNull();
+    }
+
+    @Test
+    public void createCarrierRow_hasDefaultDataSubscriptionId_verifyTitle() {
+        String expectDisplayName = "Name1";
+        int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
+                mDefaultDataSubscriptionInfo);
+        when(mDefaultDataSubscriptionInfo.getDisplayName()).thenReturn(expectDisplayName);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        mBundle.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false);
+
+        ListBuilder.RowBuilder testRowBuild = mProviderModelSliceHelper.createCarrierRow();
+
+        assertThat(testRowBuild.getTitle()).isEqualTo(expectDisplayName);
+    }
+
+    @Test
+    public void isNoCarrierData_mobileDataOnAndNoData_returnTrue() {
+        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
+        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+
+        assertThat(mProviderModelSliceHelper.isNoCarrierData()).isTrue();
+    }
+
+    @Test
+    public void isNoCarrierData_mobileDataOffAndOutOfService_returnTrue() {
+        when(mTelephonyManager.isDataEnabled()).thenReturn(false);
+        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
+        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+
+        assertThat(mProviderModelSliceHelper.isNoCarrierData()).isTrue();
+    }
+
+    @Test
+    public void isNoCarrierData_mobileDataOnAndDataConnected_returnFalse() {
+        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
+        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+
+        assertThat(mProviderModelSliceHelper.isNoCarrierData()).isFalse();
+    }
+
+    @Test
+    public void isNoCarrierData_mobileDataOffAndVoiceIsInService_returnFalse() {
+        when(mTelephonyManager.isDataEnabled()).thenReturn(false);
+        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
+        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+
+        assertThat(mProviderModelSliceHelper.isNoCarrierData()).isFalse();
+    }
+
+    @Test
+    public void getMobileDrawable_noCarrierData_getMobileDrawable() throws Throwable {
+        when(mTelephonyManager.isDataEnabled()).thenReturn(false);
+        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
+        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
+                mDefaultDataSubscriptionInfo);
+        when(mConnectivityManager.getActiveNetwork()).thenReturn(null);
+        Drawable expectDrawable = mock(Drawable.class);
+
+        assertThat(mProviderModelSliceHelper.getMobileDrawable(expectDrawable)).isEqualTo(
+                expectDrawable);
+    }
+
+    @Test
+    public void getMobileDrawable_hasCarrierDataAndDataIsOnCellular_getMobileDrawable()
+            throws Throwable {
+        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
+        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        Drawable drawable = mock(Drawable.class);
+        int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
+                mDefaultDataSubscriptionInfo);
+        addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+
+        assertThat(mProviderModelSliceHelper.getMobileDrawable(drawable)).isEqualTo(
+                mDrawableWithSignalStrength);
+
+        verify(mDrawableWithSignalStrength).setTint(Utils.getColorAccentDefaultColor(mContext));
+    }
+
+    @Test
+    public void getMobileDrawable_hasCarrierDataAndDataIsOnWifi_getMobileDrawable()
+            throws Throwable {
+        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
+        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        Drawable drawable = mock(Drawable.class);
+        int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
+                mDefaultDataSubscriptionInfo);
+        addNetworkTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+
+        assertThat(mProviderModelSliceHelper.getMobileDrawable(drawable)).isEqualTo(
+                mDrawableWithSignalStrength);
+    }
+
+    private void addNetworkTransportType(int networkType) {
+        mNetworkCapabilities = new NetworkCapabilities().addTransportType(networkType);
+        when(mConnectivityManager.getNetworkCapabilities(mNetwork)).thenReturn(
+                mNetworkCapabilities);
+    }
+
+    private class TestCustomSliceable implements CustomSliceable {
+        TestCustomSliceable() {
+        }
+
+        @Override
+        public Slice getSlice() {
+            return null;
+        }
+
+        @Override
+        public Uri getUri() {
+            return Uri.parse("content://android.settings.slices/action/provider_model");
+        }
+
+        @Override
+        public Intent getIntent() {
+            return new Intent();
+        }
+    }
+
+    private class MockProviderModelSliceHelper extends ProviderModelSliceHelper {
+        MockProviderModelSliceHelper(Context context, CustomSliceable sliceable) {
+            super(context, sliceable);
+        }
+
+        @Override
+        public Drawable getDrawableWithSignalStrength() {
+            return mDrawableWithSignalStrength;
+        }
+    }
+}
diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
new file mode 100644
index 0000000..9c16b8a
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
@@ -0,0 +1,333 @@
+/*
+ * 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.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+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.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import androidx.slice.Slice;
+import androidx.slice.SliceProvider;
+import androidx.slice.builders.GridRowBuilder;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.SliceAction;
+import androidx.slice.widget.SliceLiveData;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.Utils;
+import com.android.settings.network.telephony.NetworkProviderWorker;
+import com.android.settings.testutils.ResourcesUtils;
+import com.android.settings.wifi.slice.WifiSliceItem;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class ProviderModelSliceTest {
+    private static final Uri PROVIDER_MODEL_SLICE_URI =
+            Uri.parse("content://com.android.settings.slices/action/provider_model");
+    private static final int MOCK_SLICE_LEVEL = 3;
+
+    private Context mContext;
+    private MockProviderModelSlice mMockProviderModelSlice;
+    List<WifiSliceItem> mWifiList = new ArrayList<>();
+    private ListBuilder mListBuilder;
+    private MockNetworkProviderWorker mMockNetworkProviderWorker;
+
+    @Mock
+    private SubscriptionManager mSubscriptionManager;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private ProviderModelSliceHelper mProviderModelSliceHelper;
+    @Mock
+    private WifiSliceItem mMockWifiSliceItem1;
+    @Mock
+    private WifiSliceItem mMockWifiSliceItem2;
+    @Mock
+    private WifiSliceItem mMockWifiSliceItem3;
+    @Mock
+    ListBuilder.RowBuilder mMockCarrierRowBuild;
+    @Mock
+    ListBuilder.HeaderBuilder mMockHeader;
+    @Mock
+    GridRowBuilder mMockGridRowBuilderNonCarrierNetworkUnavailable;
+    @Mock
+    GridRowBuilder mMockGridRowBuilderAllNetworkUnavailable;
+
+    @Before
+    @UiThreadTest
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(ApplicationProvider.getApplicationContext());
+
+        when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+        when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+        when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+        when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
+
+
+        // Set-up specs for SliceMetadata.
+        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+        mMockNetworkProviderWorker = new MockNetworkProviderWorker(mContext,
+                PROVIDER_MODEL_SLICE_URI);
+        mMockProviderModelSlice = new MockProviderModelSlice(mContext, mMockNetworkProviderWorker);
+        mListBuilder = spy(new ListBuilder(mContext, PROVIDER_MODEL_SLICE_URI,
+                ListBuilder.INFINITY).setAccentColor(-1));
+        when(mProviderModelSliceHelper.createListBuilder(PROVIDER_MODEL_SLICE_URI)).thenReturn(
+                mListBuilder);
+
+        mWifiList = new ArrayList<>();
+        mMockNetworkProviderWorker.updateSelfResults(mWifiList);
+
+        mockBuilder();
+    }
+
+    @Test
+    @UiThreadTest
+    public void getSlice_noWorkerAndNoCarrier_getOneHeaderOneGridRowWithAllNetworkUnavailable() {
+        mWifiList.clear();
+        mMockProviderModelSlice = new MockProviderModelSlice(mContext, null);
+        mockHelperCondition(false, false, false, null);
+
+        final Slice slice = mMockProviderModelSlice.getSlice();
+
+        assertThat(slice).isNotNull();
+        verify(mListBuilder, times(1)).setHeader(mMockHeader);
+        verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
+    }
+
+    @Test
+    @UiThreadTest
+    public void getSlice_noWifiAndNoCarrier_getOneHeaderOneGridRowWithAllNetworkUnavailable() {
+        mWifiList.clear();
+        mMockNetworkProviderWorker.updateSelfResults(null);
+        mockHelperCondition(false, false, false, null);
+
+        final Slice slice = mMockProviderModelSlice.getSlice();
+
+        assertThat(slice).isNotNull();
+        verify(mListBuilder, times(1)).setHeader(mMockHeader);
+        verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
+    }
+
+    @Test
+    @UiThreadTest
+    public void getSlice_noWifiAndHasCarrierNoData_oneCarrierOneGridRowWithAllNetworkUnavailable() {
+        mWifiList.clear();
+        mMockNetworkProviderWorker.updateSelfResults(null);
+        mockHelperCondition(false, true, true, null);
+
+        final Slice slice = mMockProviderModelSlice.getSlice();
+
+        assertThat(slice).isNotNull();
+        verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
+        verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
+    }
+
+    @Test
+    @UiThreadTest
+    public void getSlice_noWifiAndNoCarrier_oneCarrierOneGridRowWithNonCarrierNetworkUnavailable() {
+        mWifiList.clear();
+        mMockProviderModelSlice = new MockProviderModelSlice(mContext, null);
+        mockHelperCondition(false, true, false, null);
+
+        final Slice slice = mMockProviderModelSlice.getSlice();
+
+        assertThat(slice).isNotNull();
+        verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
+        verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderNonCarrierNetworkUnavailable);
+    }
+
+    @Test
+    @UiThreadTest
+    public void getSlice_haveTwoWifiAndOneCarrier_getCarrierAndTwoWiFi() {
+        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, false, mWifiList.get(0));
+
+        final Slice slice = mMockProviderModelSlice.getSlice();
+
+        assertThat(slice).isNotNull();
+        verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
+        verify(mListBuilder, times(3)).addRow(any(ListBuilder.RowBuilder.class));
+    }
+
+    @Test
+    @UiThreadTest
+    public void getSlice_haveOneConnectedWifiAndTwoDisconnectedWifiAndNoCarrier_getTwoRow() {
+        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);
+        mockWifiItemCondition(mMockWifiSliceItem3, "wifi3", "wifi3",
+                WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi3_key", true);
+        mWifiList.add(mMockWifiSliceItem3);
+        mMockNetworkProviderWorker.updateSelfResults(mWifiList);
+        mockHelperCondition(false, false, false, mWifiList.get(0));
+
+        final Slice slice = mMockProviderModelSlice.getSlice();
+
+        assertThat(slice).isNotNull();
+        verify(mListBuilder, times(3)).addRow(any(ListBuilder.RowBuilder.class));
+    }
+
+    @Test
+    @UiThreadTest
+    public void getSlice_haveTwoDisconnectedWifiAndNoCarrier_getTwoRow() {
+        mWifiList.clear();
+        mockWifiItemCondition(mMockWifiSliceItem1, "wifi1", "wifi1",
+                WifiEntry.CONNECTED_STATE_DISCONNECTED, "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, false, false, null);
+
+        final Slice slice = mMockProviderModelSlice.getSlice();
+
+        assertThat(slice).isNotNull();
+        verify(mListBuilder, times(2)).addRow(any(ListBuilder.RowBuilder.class));
+    }
+
+    @Test
+    public void providerModelSlice_hasCorrectUri() {
+        assertThat(mMockProviderModelSlice.getUri()).isEqualTo(PROVIDER_MODEL_SLICE_URI);
+    }
+
+    private void mockHelperCondition(boolean airplaneMode, boolean hasCarrier,
+            boolean isNoCarrierData, WifiSliceItem connectedWifiItem) {
+        when(mProviderModelSliceHelper.isAirplaneModeEnabled()).thenReturn(airplaneMode);
+        when(mProviderModelSliceHelper.hasCarrier()).thenReturn(hasCarrier);
+        when(mProviderModelSliceHelper.isNoCarrierData()).thenReturn(isNoCarrierData);
+        when(mProviderModelSliceHelper.getConnectedWifiItem(any())).thenReturn(connectedWifiItem);
+    }
+
+    private void mockWifiItemCondition(WifiSliceItem mockWifiItem, String title, String summary,
+            int connectedState, String key, boolean shouldEditBeforeConnect) {
+        when(mockWifiItem.getTitle()).thenReturn(title);
+        when(mockWifiItem.getSummary()).thenReturn(summary);
+        when(mockWifiItem.getConnectedState()).thenReturn(connectedState);
+        when(mockWifiItem.getLevel()).thenReturn(MOCK_SLICE_LEVEL);
+        when(mockWifiItem.getKey()).thenReturn(key);
+        when(mockWifiItem.shouldEditBeforeConnect()).thenReturn(shouldEditBeforeConnect);
+    }
+
+    private void mockBuilder() {
+        SliceAction mockSliceAction = getPrimarySliceAction();
+        when(mMockHeader.getTitle()).thenReturn("mockHeader");
+        when(mMockHeader.getPrimaryAction()).thenReturn(mockSliceAction);
+        when(mProviderModelSliceHelper.createHeader()).thenReturn(mMockHeader);
+
+        int resId = ResourcesUtils.getResourcesId(mContext, "string",
+                "non_carrier_network_unavailable");
+        when(mProviderModelSliceHelper.createMessageGridRow(resId)).thenReturn(
+                mMockGridRowBuilderNonCarrierNetworkUnavailable);
+        resId = ResourcesUtils.getResourcesId(mContext, "string",
+                "all_network_unavailable");
+        when(mProviderModelSliceHelper.createMessageGridRow(resId)).thenReturn(
+                mMockGridRowBuilderAllNetworkUnavailable);
+
+        when(mMockCarrierRowBuild.getTitle()).thenReturn("mockRow");
+        when(mMockCarrierRowBuild.getPrimaryAction()).thenReturn(mockSliceAction);
+        when(mProviderModelSliceHelper.createCarrierRow()).thenReturn(mMockCarrierRowBuild);
+    }
+
+    private SliceAction getPrimarySliceAction() {
+        return SliceAction.createDeeplink(
+                getPrimaryAction(),
+                Utils.createIconWithDrawable(new ColorDrawable(Color.TRANSPARENT)),
+                ListBuilder.ICON_IMAGE,
+                ResourcesUtils.getResourcesString(mContext, "summary_placeholder"));
+    }
+
+    private PendingIntent getPrimaryAction() {
+        final Intent intent = new Intent("android.settings.NETWORK_PROVIDER_SETTINGS")
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return PendingIntent.getActivity(mContext, 0 /* requestCode */,
+                intent, PendingIntent.FLAG_IMMUTABLE /* flags */);
+    }
+
+    public class MockNetworkProviderWorker extends NetworkProviderWorker {
+        MockNetworkProviderWorker(Context context, Uri uri) {
+            super(context, uri);
+        }
+
+        public void updateSelfResults(List<WifiSliceItem> results) {
+            this.updateResults(results);
+        }
+    }
+
+    public class MockProviderModelSlice extends ProviderModelSlice {
+        private MockNetworkProviderWorker mNetworkProviderWorker;
+
+        MockProviderModelSlice(Context context, MockNetworkProviderWorker networkProviderWorker) {
+            super(context);
+            mNetworkProviderWorker = networkProviderWorker;
+        }
+
+        @Override
+        ProviderModelSliceHelper getHelper() {
+            return mProviderModelSliceHelper;
+        }
+
+        @Override
+        NetworkProviderWorker getWorker() {
+            return mNetworkProviderWorker;
+        }
+    }
+}
diff --git a/tests/unit/src/com/android/settings/network/telephony/NetworkProviderWorkerTest.java b/tests/unit/src/com/android/settings/network/telephony/NetworkProviderWorkerTest.java
new file mode 100644
index 0000000..a4fc745
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/telephony/NetworkProviderWorkerTest.java
@@ -0,0 +1,264 @@
+/*
+ * 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.network.telephony;
+
+import static com.android.settings.slices.CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.wifitrackerlib.WifiEntry;
+import com.android.wifitrackerlib.WifiPickerTracker;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class NetworkProviderWorkerTest {
+    private Context mContext;
+    private MockNetworkProviderWorker mMockNetworkProviderWorker;
+
+    @Mock
+    WifiPickerTracker mMockWifiPickerTracker;
+    @Mock
+    private SubscriptionManager mSubscriptionManager;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+
+    @Before
+    @UiThreadTest
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(ApplicationProvider.getApplicationContext());
+
+        when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+        when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+        when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+
+        mMockNetworkProviderWorker = new MockNetworkProviderWorker(mContext,
+                PROVIDER_MODEL_SLICE_URI);
+        mMockNetworkProviderWorker.setWifiPickerTracker(mMockWifiPickerTracker);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onConstructor_shouldBeInCreatedState() {
+        assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
+                .isEqualTo(Lifecycle.State.CREATED);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onSlicePinned_shouldBeInResumedState() {
+        mMockNetworkProviderWorker.onSlicePinned();
+
+        assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
+                .isEqualTo(Lifecycle.State.RESUMED);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onSliceUnpinned_shouldBeInCreatedState() {
+        mMockNetworkProviderWorker.onSlicePinned();
+        mMockNetworkProviderWorker.onSliceUnpinned();
+
+        assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
+                .isEqualTo(Lifecycle.State.CREATED);
+    }
+
+    @Test
+    @UiThreadTest
+    public void close_shouldBeInDestroyedState() {
+        mMockNetworkProviderWorker.close();
+
+        assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
+                .isEqualTo(Lifecycle.State.DESTROYED);
+    }
+
+    @Test
+    @UiThreadTest
+    public void getWifiEntry_connectedWifiKey_shouldGetConnectedWifi() {
+        final String key = "key";
+        final WifiEntry connectedWifiEntry = mock(WifiEntry.class);
+        when(connectedWifiEntry.getKey()).thenReturn(key);
+        when(mMockWifiPickerTracker.getConnectedWifiEntry()).thenReturn(connectedWifiEntry);
+
+        assertThat(mMockNetworkProviderWorker.getWifiEntry(key)).isEqualTo(connectedWifiEntry);
+    }
+
+    @Test
+    @UiThreadTest
+    public void getWifiEntry_reachableWifiKey_shouldGetReachableWifi() {
+        final String key = "key";
+        final WifiEntry reachableWifiEntry = mock(WifiEntry.class);
+        when(reachableWifiEntry.getKey()).thenReturn(key);
+        when(mMockWifiPickerTracker.getWifiEntries()).thenReturn(Arrays.asList(reachableWifiEntry));
+
+        assertThat(mMockNetworkProviderWorker.getWifiEntry(key)).isEqualTo(reachableWifiEntry);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onSubscriptionsChanged_notifySubscriptionChanged_callUpdateSlice() {
+        mMockNetworkProviderWorker.onSlicePinned();
+        mMockNetworkProviderWorker.receiveNotification(false);
+
+        mMockNetworkProviderWorker.onSubscriptionsChanged();
+
+        assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
+    }
+
+    @Test
+    @UiThreadTest
+    public void onAirplaneModeChanged_airplaneModeOn_callUpdateSlice() {
+        mMockNetworkProviderWorker.onSlicePinned();
+        mMockNetworkProviderWorker.receiveNotification(false);
+
+        mMockNetworkProviderWorker.onAirplaneModeChanged(false);
+
+        assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
+    }
+
+    @Test
+    @UiThreadTest
+    public void onAirplaneModeChanged_airplaneModeOff_callUpdateSlice() {
+        mMockNetworkProviderWorker.onSlicePinned();
+        mMockNetworkProviderWorker.receiveNotification(false);
+
+        mMockNetworkProviderWorker.onAirplaneModeChanged(true);
+
+        assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
+    }
+
+    @Test
+    @UiThreadTest
+    public void onSignalStrengthChanged_notifySignalStrengthChanged_callUpdateSlice() {
+        mMockNetworkProviderWorker.onSlicePinned();
+        mMockNetworkProviderWorker.receiveNotification(false);
+
+        mMockNetworkProviderWorker.onSignalStrengthChanged();
+
+        assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
+    }
+
+    @Test
+    @UiThreadTest
+    public void onMobileDataEnabledChange_notifyMobileDataEnabledChanged_callUpdateSlice() {
+        mMockNetworkProviderWorker.onSlicePinned();
+        mMockNetworkProviderWorker.receiveNotification(false);
+
+        mMockNetworkProviderWorker.onMobileDataEnabledChange();
+
+        assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
+    }
+
+    @Test
+    @UiThreadTest
+    public void onDataConnectivityChange_notifyDataConnectivityChanged_callUpdateSlice() {
+        mMockNetworkProviderWorker.onSlicePinned();
+        mMockNetworkProviderWorker.receiveNotification(false);
+
+        mMockNetworkProviderWorker.onDataConnectivityChange();
+
+        assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
+    }
+
+    @Test
+    @UiThreadTest
+    public void onServiceStateChanged_notifyPhoneStateListener_callUpdateSlice() {
+        mMockNetworkProviderWorker.onSlicePinned();
+        mMockNetworkProviderWorker.receiveNotification(false);
+
+        mMockNetworkProviderWorker.mPhoneStateListener.onServiceStateChanged(new ServiceState());
+
+        assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
+    }
+
+    @Test
+    @UiThreadTest
+    public void onActiveDataSubscriptionIdChanged_notifyPhoneStateListener_callUpdateSlice() {
+        mMockNetworkProviderWorker.onSlicePinned();
+        mMockNetworkProviderWorker.receiveNotification(false);
+
+        mMockNetworkProviderWorker.mPhoneStateListener.onActiveDataSubscriptionIdChanged(1);
+
+        assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
+    }
+
+    @Test
+    @UiThreadTest
+    public void onDisplayInfoChanged_notifyPhoneStateListener_callUpdateSlice() {
+        mMockNetworkProviderWorker.onSlicePinned();
+        mMockNetworkProviderWorker.receiveNotification(false);
+
+        mMockNetworkProviderWorker.mPhoneStateListener.onDisplayInfoChanged(
+                new TelephonyDisplayInfo(14, 0));
+
+        assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
+    }
+
+    public class MockNetworkProviderWorker extends NetworkProviderWorker {
+        private boolean mHasNotification = false;
+
+        MockNetworkProviderWorker(Context context, Uri uri) {
+            super(context, uri);
+        }
+
+        public void receiveNotification(boolean inputValue) {
+            mHasNotification = inputValue;
+        }
+
+        public boolean hasNotification() {
+            return mHasNotification;
+        }
+
+        @Override
+        public void updateSlice() {
+            super.updateSlice();
+            receiveNotification(true);
+        }
+
+        public void setWifiPickerTracker(WifiPickerTracker wifiPickerTracker) {
+            mWifiPickerTracker = wifiPickerTracker;
+        }
+    }
+}
diff --git a/tests/unit/src/com/android/settings/testutils/ResourcesUtils.java b/tests/unit/src/com/android/settings/testutils/ResourcesUtils.java
index 89cc7b3..8ef310d 100644
--- a/tests/unit/src/com/android/settings/testutils/ResourcesUtils.java
+++ b/tests/unit/src/com/android/settings/testutils/ResourcesUtils.java
@@ -17,15 +17,43 @@
 
 import android.content.Context;
 
+/**
+ * Test util to provide the correct resources.
+ */
 public final class ResourcesUtils {
+    /**
+     * Return a resource identifier for the given resource name.
+     * @param context Context to use.
+     * @param type Optional default resource type to find, if "type/" is not included in the name.
+     *             Can be null to require an explicit type.
+     * @param name The name of the desired resource.
+     * @return The associated resource identifier. Returns 0 if no such resource was found.
+     * (0 is not a valid resource ID.)
+     */
     public static int getResourcesId(Context context, String type, String name) {
         return context.getResources().getIdentifier(name, type, context.getPackageName());
     }
 
+    /**
+     * Returns a localized string from the application's package's default string table.
+     * @param context Context to use.
+     * @param name The name of the desired resource.
+     * @return The string data associated with the resource, stripped of styled text information.
+     */
     public static String getResourcesString(Context context, String name) {
         return context.getResources().getString(getResourcesId(context, "string", name));
     }
 
+    /**
+     * Return the string value associated with a particular neame of resource,
+     * substituting the format arguments as defined in {@link java.util.Formatter}
+     * and {@link java.lang.String#format}. It will be stripped of any styled text
+     * information.
+     * @param context Context to use.
+     * @param name The name of the desired resource.
+     * @param value The format arguments that will be used for substitution.
+     * @return The string data associated with the resource, stripped of styled text information.
+     */
     public static String getResourcesString(Context context, String name, Object... value) {
         return context.getResources().getString(getResourcesId(context, "string", name), value);
     }