Revert "[Wi-Fi] Apply WifiTrackerLib objects in Wi-Fi Slice"

This reverts commit 7b1aded2a6ab83a24933bc02a7aa19ca0394e345.

Reason for revert: Settings is crashing for WiFi selection in SetupWizard on wembley

Bug: 161434533
Change-Id: I1d90e9bae1b31862fba674db0d7497e43f987a7f
Test: Locally reverted, reflashed, and was able to select WiFi without Settings crashing.
Exempt-From-Owner-Approval: Revert to clear up P0 while all owners are outside of work hours
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 00cdf40..b49557e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2195,8 +2195,6 @@
     <string name="wifi_turned_on_message">Wi\u2011Fi turned on</string>
     <!-- A notification for connected to a Wi-Fi network [CHAR LIMIT=NONE] -->
     <string name="wifi_connected_to_message">Connected to <xliff:g id="network_name" example="MyNetwork">%1$s</xliff:g></string>
-    <!-- A notification for connecting to a Wi-Fi network [CHAR LIMIT=NONE] -->
-    <string name="wifi_connecting_to_message">Connecting to <xliff:g id="network_name" example="MyNetwork">%1$s</xliff:g></string>
     <!-- Button label to connecting progress to a Wi-Fi network [CHAR LIMIT=20] -->
     <string name="wifi_connecting">Connecting\u2026</string>
     <!-- Failured notification for connect -->
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
index 3b75ebf..9f0023a 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
@@ -40,6 +40,7 @@
 import com.android.settings.homepage.contextualcards.slices.BluetoothUpdateWorker;
 import com.android.settings.homepage.contextualcards.slices.SwipeDismissalDelegate;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.wifi.slice.ContextualWifiScanWorker;
 
 public class ContextualCardsFragment extends InstrumentedFragment implements
         FocusRecyclerView.FocusListener {
@@ -79,6 +80,7 @@
         super.onStart();
         registerScreenOffReceiver();
         registerKeyEventReceiver();
+        ContextualWifiScanWorker.newVisibleUiSession();
         mContextualCardManager.loadContextualCards(LoaderManager.getInstance(this),
                 sRestartLoaderNeeded);
         sRestartLoaderNeeded = false;
diff --git a/src/com/android/settings/wifi/AddNetworkFragment.java b/src/com/android/settings/wifi/AddNetworkFragment.java
index 31dc21f..7e17e38 100644
--- a/src/com/android/settings/wifi/AddNetworkFragment.java
+++ b/src/com/android/settings/wifi/AddNetworkFragment.java
@@ -32,7 +32,6 @@
 
 import com.android.settings.R;
 import com.android.settings.core.InstrumentedFragment;
-import com.android.settings.wifi.dpp.WifiDppQrCodeScannerFragment;
 import com.android.settings.wifi.dpp.WifiDppUtils;
 
 /**
@@ -117,7 +116,7 @@
             }
 
             final WifiConfiguration config = data.getParcelableExtra(
-                    WifiDppQrCodeScannerFragment.KEY_WIFI_CONFIGURATION);
+                    WifiDialogActivity.KEY_WIFI_CONFIGURATION);
             successfullyFinish(config);
         }
     }
diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java
index b25d13f..7782786 100644
--- a/src/com/android/settings/wifi/WifiDialogActivity.java
+++ b/src/com/android/settings/wifi/WifiDialogActivity.java
@@ -16,56 +16,51 @@
 
 package com.android.settings.wifi;
 
+import android.app.Activity;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.net.ConnectivityManager;
-import android.net.NetworkScoreManager;
+import android.net.NetworkInfo;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.ActionListener;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
-import android.os.SimpleClock;
-import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.R;
 import com.android.settings.SetupWizardUtils;
 import com.android.settings.wifi.dpp.WifiDppUtils;
-import com.android.settingslib.core.lifecycle.ObservableActivity;
-import com.android.wifitrackerlib.NetworkDetailsTracker;
-import com.android.wifitrackerlib.WifiEntry;
+import com.android.settingslib.wifi.AccessPoint;
 
 import com.google.android.setupcompat.util.WizardManagerHelper;
 
-import java.time.Clock;
-import java.time.ZoneOffset;
-
-/**
- * The activity shows a CONNECT_MODE Wi-fi editor dialog.
- */
-public class WifiDialogActivity extends ObservableActivity implements
-        WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener {
+public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialogListener,
+        DialogInterface.OnDismissListener {
 
     private static final String TAG = "WifiDialogActivity";
 
-    public static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key";
+    public static final String KEY_ACCESS_POINT_STATE = "access_point_state";
+
+    /**
+     * Boolean extra indicating whether this activity should connect to an access point on the
+     * caller's behalf. If this is set to false, the caller should check
+     * {@link #KEY_WIFI_CONFIGURATION} in the result data and save that using
+     * {@link WifiManager#connect(WifiConfiguration, ActionListener)}. Default is true.
+     */
+    @VisibleForTesting
+    static final String KEY_CONNECT_FOR_CALLER = "connect_for_caller";
+
+    public static final String KEY_WIFI_CONFIGURATION = "wifi_configuration";
 
     private static final int RESULT_CONNECTED = RESULT_FIRST_USER;
     private static final int RESULT_FORGET = RESULT_FIRST_USER + 1;
 
     private static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0;
 
-    // Max age of tracked WifiEntries.
-    private static final long MAX_SCAN_AGE_MILLIS = 15_000;
-    // Interval between initiating NetworkDetailsTracker scans.
-    private static final long SCAN_INTERVAL_MILLIS = 10_000;
+    private WifiDialog mDialog;
 
-    private WifiDialog2 mDialog;
     private Intent mIntent;
-    private NetworkDetailsTracker mNetworkDetailsTracker;
-    private HandlerThread mWorkerThread;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -76,43 +71,18 @@
 
         super.onCreate(savedInstanceState);
 
-        mWorkerThread = new HandlerThread(
-                TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
-                Process.THREAD_PRIORITY_BACKGROUND);
-        mWorkerThread.start();
-        final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
-            @Override
-            public long millis() {
-                return SystemClock.elapsedRealtime();
-            }
-        };
-        mNetworkDetailsTracker = NetworkDetailsTracker.createNetworkDetailsTracker(
-                getLifecycle(),
-                this,
-                getSystemService(WifiManager.class),
-                getSystemService(ConnectivityManager.class),
-                getSystemService(NetworkScoreManager.class),
-                new Handler(Looper.getMainLooper()),
-                mWorkerThread.getThreadHandler(),
-                elapsedRealtimeClock,
-                MAX_SCAN_AGE_MILLIS,
-                SCAN_INTERVAL_MILLIS,
-                mIntent.getStringExtra(KEY_CHOSEN_WIFIENTRY_KEY));
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        if (mDialog != null) {
-            return;
+        final Bundle accessPointState = mIntent.getBundleExtra(KEY_ACCESS_POINT_STATE);
+        AccessPoint accessPoint = null;
+        if (accessPointState != null) {
+            accessPoint = new AccessPoint(this, accessPointState);
         }
 
         if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
-            mDialog = WifiDialog2.createModal(this, this, mNetworkDetailsTracker.getWifiEntry(),
-                    WifiConfigUiBase2.MODE_CONNECT, R.style.SuwAlertDialogThemeCompat_Light);
+            mDialog = WifiDialog.createModal(this, this, accessPoint,
+                    WifiConfigUiBase.MODE_CONNECT, R.style.SuwAlertDialogThemeCompat_Light);
         } else {
-            mDialog = WifiDialog2.createModal(this, this, mNetworkDetailsTracker.getWifiEntry(),
-                    WifiConfigUiBase2.MODE_CONNECT);
+            mDialog = WifiDialog.createModal(
+                    this, this, accessPoint, WifiConfigUiBase.MODE_CONNECT);
         }
         mDialog.show();
         mDialog.setOnDismissListener(this);
@@ -120,44 +90,82 @@
 
     @Override
     public void finish() {
-        overridePendingTransition(0, 0);
-
         super.finish();
+        overridePendingTransition(0, 0);
     }
 
     @Override
     public void onDestroy() {
+        super.onDestroy();
         if (mDialog != null && mDialog.isShowing()) {
             mDialog.dismiss();
             mDialog = null;
         }
-        mWorkerThread.quit();
-
-        super.onDestroy();
     }
 
     @Override
-    public void onForget(WifiDialog2 dialog) {
-        final WifiEntry wifiEntry = dialog.getController().getWifiEntry();
-        if (wifiEntry != null && wifiEntry.canForget()) {
-            wifiEntry.forget(null /* callback */);
+    public void onForget(WifiDialog dialog) {
+        final WifiManager wifiManager = getSystemService(WifiManager.class);
+        final AccessPoint accessPoint = dialog.getController().getAccessPoint();
+        if (accessPoint != null) {
+            if (!accessPoint.isSaved()) {
+                if (accessPoint.getNetworkInfo() != null &&
+                        accessPoint.getNetworkInfo().getState() != NetworkInfo.State.DISCONNECTED) {
+                    // Network is active but has no network ID - must be ephemeral.
+                    wifiManager.disableEphemeralNetwork(
+                            AccessPoint.convertToQuotedString(accessPoint.getSsidStr()));
+                } else {
+                    // Should not happen, but a monkey seems to trigger it
+                    Log.e(TAG, "Failed to forget invalid network " + accessPoint.getConfig());
+                }
+            } else {
+                wifiManager.forget(accessPoint.getConfig().networkId, null /* listener */);
+            }
         }
 
+        Intent resultData = new Intent();
+        if (accessPoint != null) {
+            Bundle accessPointState = new Bundle();
+            accessPoint.saveWifiState(accessPointState);
+            resultData.putExtra(KEY_ACCESS_POINT_STATE, accessPointState);
+        }
         setResult(RESULT_FORGET);
         finish();
     }
 
     @Override
-    public void onSubmit(WifiDialog2 dialog) {
-        final WifiEntry wifiEntry = dialog.getController().getWifiEntry();
+    public void onSubmit(WifiDialog dialog) {
         final WifiConfiguration config = dialog.getController().getConfig();
-        if (config == null && wifiEntry != null && wifiEntry.canConnect()) {
-            wifiEntry.connect(null /* callback */);
-        } else {
-            getSystemService(WifiManager.class).connect(config, null /* listener */);
+        final AccessPoint accessPoint = dialog.getController().getAccessPoint();
+        final WifiManager wifiManager = getSystemService(WifiManager.class);
+
+        if (getIntent().getBooleanExtra(KEY_CONNECT_FOR_CALLER, true)) {
+            if (config == null) {
+                if (accessPoint != null && accessPoint.isSaved()) {
+                    wifiManager.connect(accessPoint.getConfig(), null /* listener */);
+                }
+            } else {
+                wifiManager.save(config, null /* listener */);
+                if (accessPoint != null) {
+                    // accessPoint is null for "Add network"
+                    NetworkInfo networkInfo = accessPoint.getNetworkInfo();
+                    if (networkInfo == null || !networkInfo.isConnected()) {
+                        wifiManager.connect(config, null /* listener */);
+                    }
+                }
+            }
         }
 
-        setResult(RESULT_CONNECTED);
+        Intent resultData = new Intent();
+        if (accessPoint != null) {
+            Bundle accessPointState = new Bundle();
+            accessPoint.saveWifiState(accessPointState);
+            resultData.putExtra(KEY_ACCESS_POINT_STATE, accessPointState);
+        }
+        if (config != null) {
+            resultData.putExtra(KEY_WIFI_CONFIGURATION, config);
+        }
+        setResult(RESULT_CONNECTED, resultData);
         finish();
     }
 
@@ -168,7 +176,7 @@
     }
 
     @Override
-    public void onScan(WifiDialog2 dialog, String ssid) {
+    public void onScan(WifiDialog dialog, String ssid) {
         Intent intent = WifiDppUtils.getEnrolleeQrCodeScannerIntent(ssid);
         WizardManagerHelper.copyWizardManagerExtras(mIntent, intent);
 
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
index cee3ccd..accef12 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
@@ -57,6 +57,7 @@
 import androidx.lifecycle.ViewModelProviders;
 
 import com.android.settings.R;
+import com.android.settings.wifi.WifiDialogActivity;
 import com.android.settings.wifi.qrcode.QrCamera;
 import com.android.settings.wifi.qrcode.QrDecorateView;
 import com.android.wifitrackerlib.WifiEntry;
@@ -90,7 +91,7 @@
     // Key for Bundle usage
     private static final String KEY_IS_CONFIGURATOR_MODE = "key_is_configurator_mode";
     private static final String KEY_LATEST_ERROR_CODE = "key_latest_error_code";
-    public static final String KEY_WIFI_CONFIGURATION = "key_wifi_configuration";
+    private static final String KEY_WIFI_CONFIGURATION = "key_wifi_configuration";
 
     private static final int ARG_RESTART_CAMERA = 1;
 
@@ -688,7 +689,8 @@
     @Override
     public void onSuccess() {
         final Intent resultIntent = new Intent();
-        resultIntent.putExtra(KEY_WIFI_CONFIGURATION, mEnrolleeWifiConfiguration);
+        resultIntent.putExtra(WifiDialogActivity.KEY_WIFI_CONFIGURATION,
+                mEnrolleeWifiConfiguration);
 
         final Activity hostActivity = getActivity();
         hostActivity.setResult(Activity.RESULT_OK, resultIntent);
diff --git a/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java b/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java
index 779a57e..5c92d81 100644
--- a/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java
+++ b/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java
@@ -19,74 +19,61 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.text.TextUtils;
-import android.widget.Toast;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.settings.R;
-import com.android.settings.slices.SliceBackgroundWorker;
+import com.android.settings.wifi.WifiConnectListener;
 import com.android.settings.wifi.WifiDialogActivity;
-import com.android.wifitrackerlib.WifiEntry;
-import com.android.wifitrackerlib.WifiEntry.ConnectCallback;
+import com.android.settings.wifi.WifiUtils;
+import com.android.settingslib.wifi.AccessPoint;
 
 /**
  * This receiver helps connect to Wi-Fi network
  */
 public class ConnectToWifiHandler extends BroadcastReceiver {
 
-    static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key";
-    static final String KEY_WIFI_SLICE_URI = "key_wifi_slice_uri";
-
     @Override
     public void onReceive(Context context, Intent intent) {
         if (context == null || intent == null) {
             return;
         }
-        final String key = intent.getStringExtra(KEY_CHOSEN_WIFIENTRY_KEY);
-        if (TextUtils.isEmpty(key)) {
-            return;
+
+        final Network network = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
+        final Bundle accessPointState = intent.getBundleExtra(
+                WifiDialogActivity.KEY_ACCESS_POINT_STATE);
+
+        if (network != null) {
+            WifiScanWorker.clearClickedWifi();
+            final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+            // start captive portal app to sign in to network
+            cm.startCaptivePortalApp(network);
+        } else if (accessPointState != null) {
+            connect(context, new AccessPoint(context, accessPointState));
         }
-        if (intent.getParcelableExtra(KEY_WIFI_SLICE_URI) == null) {
-            return;
-        }
-        final WifiScanWorker worker = getWifiScanWorker(intent);
-        if (worker == null) {
-            return;
-        }
-        final WifiEntry wifiEntry = worker.getWifiEntry(key);
-        if (wifiEntry == null) {
-            return;
-        }
-        wifiEntry.connect(new WifiEntryConnectCallback(context, wifiEntry));
     }
 
     @VisibleForTesting
-    WifiScanWorker getWifiScanWorker(Intent intent) {
-        return SliceBackgroundWorker.getInstance(intent.getParcelableExtra(KEY_WIFI_SLICE_URI));
-    }
+    void connect(Context context, AccessPoint accessPoint) {
+        ContextualWifiScanWorker.saveSession();
+        WifiScanWorker.saveClickedWifi(accessPoint);
 
-    @VisibleForTesting
-    static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
-        final Context mContext;
-        final WifiEntry mWifiEntry;
+        final WifiConnectListener connectListener = new WifiConnectListener(context);
+        switch (WifiUtils.getConnectingType(accessPoint)) {
+            case WifiUtils.CONNECT_TYPE_OSU_PROVISION:
+                accessPoint.startOsuProvisioning(connectListener);
+                break;
 
-        WifiEntryConnectCallback(Context context, WifiEntry connectWifiEntry) {
-            mContext = context;
-            mWifiEntry = connectWifiEntry;
-        }
+            case WifiUtils.CONNECT_TYPE_OPEN_NETWORK:
+                accessPoint.generateOpenNetworkConfig();
 
-        @Override
-        public void onConnectResult(@ConnectStatus int status) {
-            if (status == ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
-                final Intent intent = new Intent(mContext, WifiDialogActivity.class)
-                        .putExtra(WifiDialogActivity.KEY_CHOSEN_WIFIENTRY_KEY, mWifiEntry.getKey());
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                mContext.startActivity(intent);
-            } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
-                Toast.makeText(mContext, R.string.wifi_failed_connect_message,
-                        Toast.LENGTH_SHORT).show();
-            }
+            case WifiUtils.CONNECT_TYPE_SAVED_NETWORK:
+                final WifiManager wifiManager = context.getSystemService(WifiManager.class);
+                wifiManager.connect(accessPoint.getConfig(), connectListener);
+                break;
         }
     }
 }
diff --git a/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
index aa73a17..616c92f 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.net.Uri;
+import android.os.SystemClock;
 
 import com.android.settings.slices.SliceBackgroundWorker;
 
@@ -26,12 +27,44 @@
  */
 public class ContextualWifiScanWorker extends WifiScanWorker {
 
+    private static long sVisibleUiSessionToken;
+    private static long sActiveSession;
+
     public ContextualWifiScanWorker(Context context, Uri uri) {
         super(context, uri);
     }
 
+    /**
+     * Starts a new visible UI session for the purpose of automatically starting captive portal.
+     *
+     * A visible UI session is defined as a duration of time when a UI screen is visible to user.
+     * Going to a sub-page and coming out breaks the continuation, leaving the page and coming back
+     * breaks it too.
+     */
+    public static void newVisibleUiSession() {
+        sVisibleUiSessionToken = SystemClock.elapsedRealtime();
+    }
+
+    static void saveSession() {
+        sActiveSession = sVisibleUiSessionToken;
+    }
+
+    @Override
+    protected void clearClickedWifiOnSliceUnpinned() {
+        // Do nothing for contextual Wi-Fi slice
+    }
+
+    @Override
+    protected boolean isSessionValid() {
+        if (sVisibleUiSessionToken != sActiveSession) {
+            clearClickedWifi();
+            return false;
+        }
+        return true;
+    }
+
     @Override
     protected int getApRowCount() {
         return ContextualWifiSlice.getApRowCount();
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
index 4806573..ea9a745 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
@@ -18,8 +18,10 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
 import android.net.Uri;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
@@ -35,7 +37,7 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.slices.CustomSliceRegistry;
 import com.android.settings.slices.CustomSliceable;
-import com.android.wifitrackerlib.WifiEntry;
+import com.android.settingslib.wifi.AccessPoint;
 
 /**
  * {@link CustomSliceable} for Wi-Fi, used by contextual homepage.
@@ -50,12 +52,8 @@
     @VisibleForTesting
     static boolean sApRowCollapsed;
 
-    private final ConnectivityManager mConnectivityManager;
-
     public ContextualWifiSlice(Context context) {
         super(context);
-
-        mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
     }
 
     @Override
@@ -86,17 +84,16 @@
     }
 
     @Override
-    protected ListBuilder.RowBuilder getHeaderRow(boolean isWifiEnabled,
-            WifiSliceItem wifiSliceItem) {
-        final ListBuilder.RowBuilder builder = super.getHeaderRow(isWifiEnabled, wifiSliceItem);
-        builder.setTitleItem(getHeaderIcon(isWifiEnabled, wifiSliceItem), ListBuilder.ICON_IMAGE);
+    protected ListBuilder.RowBuilder getHeaderRow(boolean isWifiEnabled, AccessPoint accessPoint) {
+        final ListBuilder.RowBuilder builder = super.getHeaderRow(isWifiEnabled, accessPoint);
+        builder.setTitleItem(getHeaderIcon(isWifiEnabled, accessPoint), ListBuilder.ICON_IMAGE);
         if (sApRowCollapsed) {
-            builder.setSubtitle(getHeaderSubtitle(wifiSliceItem));
+            builder.setSubtitle(getSubtitle(accessPoint));
         }
         return builder;
     }
 
-    private IconCompat getHeaderIcon(boolean isWifiEnabled, WifiSliceItem wifiSliceItem) {
+    private IconCompat getHeaderIcon(boolean isWifiEnabled, AccessPoint accessPoint) {
         final Drawable drawable;
         final int tint;
         if (!isWifiEnabled) {
@@ -106,8 +103,7 @@
         } else {
             // get icon of medium signal strength
             drawable = mContext.getDrawable(com.android.settingslib.Utils.getWifiIconResource(2));
-            if (wifiSliceItem != null
-                    && wifiSliceItem.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
+            if (isNetworkConnected(accessPoint)) {
                 tint = Utils.getColorAccentDefaultColor(mContext);
             } else {
                 tint = Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal);
@@ -117,16 +113,49 @@
         return Utils.createIconWithDrawable(drawable);
     }
 
-    private CharSequence getHeaderSubtitle(WifiSliceItem wifiSliceItem) {
-        if (wifiSliceItem == null
-                || wifiSliceItem.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
+    private boolean isNetworkConnected(AccessPoint accessPoint) {
+        if (accessPoint == null) {
+            return false;
+        }
+
+        final NetworkInfo networkInfo = accessPoint.getNetworkInfo();
+        if (networkInfo == null) {
+            return false;
+        }
+
+        return networkInfo.getState() == State.CONNECTED;
+    }
+
+    private CharSequence getSubtitle(AccessPoint accessPoint) {
+        if (isCaptivePortal()) {
+            final int id = mContext.getResources()
+                    .getIdentifier("network_available_sign_in", "string", "android");
+            return mContext.getText(id);
+        }
+
+        if (accessPoint == null) {
             return mContext.getText(R.string.disconnected);
         }
-        if (wifiSliceItem.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTING) {
-            return mContext.getString(R.string.wifi_connecting_to_message,
-                wifiSliceItem.getTitle());
+
+        final NetworkInfo networkInfo = accessPoint.getNetworkInfo();
+        if (networkInfo == null) {
+            return mContext.getText(R.string.disconnected);
         }
-        return mContext.getString(R.string.wifi_connected_to_message, wifiSliceItem.getTitle());
+
+        final State state = networkInfo.getState();
+        DetailedState detailedState;
+        if (state == State.CONNECTING) {
+            detailedState = DetailedState.CONNECTING;
+        } else if (state == State.CONNECTED) {
+            detailedState = DetailedState.CONNECTED;
+        } else {
+            detailedState = networkInfo.getDetailedState();
+        }
+
+        final String[] formats = mContext.getResources().getStringArray(
+                R.array.wifi_status_with_ssid);
+        final int index = detailedState.ordinal();
+        return String.format(formats[index], accessPoint.getTitle());
     }
 
     private boolean hasWorkingNetwork() {
diff --git a/src/com/android/settings/wifi/slice/WifiScanWorker.java b/src/com/android/settings/wifi/slice/WifiScanWorker.java
index 631faac..9d0f821 100644
--- a/src/com/android/settings/wifi/slice/WifiScanWorker.java
+++ b/src/com/android/settings/wifi/slice/WifiScanWorker.java
@@ -16,187 +16,243 @@
 
 package com.android.settings.wifi.slice;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
 import static com.android.settings.wifi.slice.WifiSlice.DEFAULT_EXPANDED_ROW_COUNT;
 
 import android.content.Context;
+import android.content.Intent;
 import android.net.ConnectivityManager;
-import android.net.NetworkScoreManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.net.Uri;
-import android.net.wifi.WifiManager;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.os.SimpleClock;
-import android.os.SystemClock;
+import android.net.wifi.WifiInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
 
+import com.android.internal.util.Preconditions;
 import com.android.settings.slices.SliceBackgroundWorker;
-import com.android.settingslib.utils.ThreadUtils;
-import com.android.wifitrackerlib.WifiEntry;
-import com.android.wifitrackerlib.WifiEntry.WifiEntryCallback;
-import com.android.wifitrackerlib.WifiPickerTracker;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
 
-import java.time.Clock;
-import java.time.ZoneOffset;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * {@link SliceBackgroundWorker} for Wi-Fi, used by {@link WifiSlice}.
  */
-public class WifiScanWorker extends SliceBackgroundWorker<WifiSliceItem> implements
-        WifiPickerTracker.WifiPickerTrackerCallback, LifecycleOwner, WifiEntryCallback {
+public class WifiScanWorker extends SliceBackgroundWorker<AccessPoint> implements
+        WifiTracker.WifiListener {
 
     private static final String TAG = "WifiScanWorker";
 
-    // Max age of tracked WifiEntries.
-    private static final long MAX_SCAN_AGE_MILLIS = 15_000;
-    // Interval between initiating WifiPickerTracker scans.
-    private static final long SCAN_INTERVAL_MILLIS = 10_000;
+    @VisibleForTesting
+    WifiNetworkCallback mNetworkCallback;
 
-    @VisibleForTesting
-    final LifecycleRegistry mLifecycleRegistry;
-    @VisibleForTesting
-    WifiPickerTracker mWifiPickerTracker;
-    // Worker thread used for WifiPickerTracker work
-    private final HandlerThread mWorkerThread;
+    private final Context mContext;
+    private final ConnectivityManager mConnectivityManager;
+    private final WifiTracker mWifiTracker;
+
+    private static String sClickedWifiSsid;
 
     public WifiScanWorker(Context context, Uri uri) {
         super(context, uri);
-
-        mLifecycleRegistry = new LifecycleRegistry(this);
-
-        mWorkerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
-        mWorkerThread.start();
-        final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
-            @Override
-            public long millis() {
-                return SystemClock.elapsedRealtime();
-            }
-        };
-        mWifiPickerTracker = new WifiPickerTracker(getLifecycle(), context,
-                context.getSystemService(WifiManager.class),
-                context.getSystemService(ConnectivityManager.class),
-                context.getSystemService(NetworkScoreManager.class),
-                ThreadUtils.getUiThreadHandler(),
-                mWorkerThread.getThreadHandler(),
-                elapsedRealtimeClock,
-                MAX_SCAN_AGE_MILLIS,
-                SCAN_INTERVAL_MILLIS,
-                this);
-
-        mLifecycleRegistry.markState(Lifecycle.State.INITIALIZED);
-        mLifecycleRegistry.markState(Lifecycle.State.CREATED);
+        mContext = context;
+        mConnectivityManager = context.getSystemService(ConnectivityManager.class);
+        mWifiTracker = new WifiTracker(mContext, this /* wifiListener */,
+                true /* includeSaved */, true /* includeScans */);
     }
 
     @Override
     protected void onSlicePinned() {
-        mLifecycleRegistry.markState(Lifecycle.State.STARTED);
-        mLifecycleRegistry.markState(Lifecycle.State.RESUMED);
-        updateResults();
+        mWifiTracker.onStart();
+        onAccessPointsChanged();
     }
 
     @Override
     protected void onSliceUnpinned() {
-        mLifecycleRegistry.markState(Lifecycle.State.STARTED);
-        mLifecycleRegistry.markState(Lifecycle.State.CREATED);
+        mWifiTracker.onStop();
+        unregisterNetworkCallback();
+        clearClickedWifiOnSliceUnpinned();
     }
 
     @Override
     public void close() {
-        mLifecycleRegistry.markState(Lifecycle.State.DESTROYED);
-        mWorkerThread.quit();
+        mWifiTracker.onDestroy();
     }
 
     @Override
-    public Lifecycle getLifecycle() {
-        return mLifecycleRegistry;
-    }
-
-    /** Called when the state of Wifi has changed. */
-    @Override
-    public void onWifiStateChanged() {
-        updateResults();
-    }
-
-    /**
-     * Update the results when data changes
-     */
-    @Override
-    public void onWifiEntriesChanged() {
-        updateResults();
-    }
-
-    /**
-     * Indicates the state of the WifiEntry has changed and clients may retrieve updates through
-     * the WifiEntry getter methods.
-     */
-    @Override
-    public void onUpdated() {
+    public void onWifiStateChanged(int state) {
         notifySliceChange();
     }
 
+    @Override
+    public void onConnectedChanged() {
+    }
+
+    @Override
+    public void onAccessPointsChanged() {
+        // in case state has changed
+        if (!mWifiTracker.getManager().isWifiEnabled()) {
+            updateResults(null);
+            return;
+        }
+        // AccessPoints are sorted by the WifiTracker
+        final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
+        final List<AccessPoint> resultList = new ArrayList<>();
+        final int apRowCount = getApRowCount();
+        for (AccessPoint ap : accessPoints) {
+            if (ap.isReachable()) {
+                resultList.add(clone(ap));
+                if (resultList.size() >= apRowCount) {
+                    break;
+                }
+            }
+        }
+        updateResults(resultList);
+    }
+
     protected int getApRowCount() {
         return DEFAULT_EXPANDED_ROW_COUNT;
     }
 
-    @Override
-    public void onNumSavedSubscriptionsChanged() {
-        // Do nothing.
+    private AccessPoint clone(AccessPoint accessPoint) {
+        final Bundle savedState = new Bundle();
+        accessPoint.saveWifiState(savedState);
+        return new AccessPoint(mContext, savedState);
     }
 
     @Override
-    public void onNumSavedNetworksChanged() {
-        // Do nothing.
-    }
+    protected boolean areListsTheSame(List<AccessPoint> a, List<AccessPoint> b) {
+        if (!a.equals(b)) {
+            return false;
+        }
 
-    /**
-     * To get the WifiEntry of key.
-     */
-    public WifiEntry getWifiEntry(String key) {
-        // Get specified WifiEntry.
-        WifiEntry keyWifiEntry = null;
-        final WifiEntry connectedWifiEntry = mWifiPickerTracker.getConnectedWifiEntry();
-        if (connectedWifiEntry != null && TextUtils.equals(key, connectedWifiEntry.getKey())) {
-            keyWifiEntry = connectedWifiEntry;
-        } else {
-            for (WifiEntry wifiEntry : mWifiPickerTracker.getWifiEntries()) {
-                if (TextUtils.equals(key, wifiEntry.getKey())) {
-                    keyWifiEntry = wifiEntry;
-                    break;
-                }
+        // compare access point states one by one
+        final int listSize = a.size();
+        for (int i = 0; i < listSize; i++) {
+            if (a.get(i).getDetailedState() != b.get(i).getDetailedState()) {
+                return false;
             }
         }
-        return keyWifiEntry;
+        return true;
     }
 
-    @VisibleForTesting
-    void updateResults() {
-        if (mWifiPickerTracker.getWifiState() != WifiManager.WIFI_STATE_ENABLED
-                || mLifecycleRegistry.getCurrentState() != Lifecycle.State.RESUMED) {
-            super.updateResults(null);
+    static void saveClickedWifi(AccessPoint accessPoint) {
+        sClickedWifiSsid = accessPoint.getSsidStr();
+    }
+
+    static void clearClickedWifi() {
+        sClickedWifiSsid = null;
+    }
+
+    static boolean isWifiClicked(WifiInfo info) {
+        final String ssid = WifiInfo.sanitizeSsid(info.getSSID());
+        return !TextUtils.isEmpty(ssid) && TextUtils.equals(ssid, sClickedWifiSsid);
+    }
+
+    protected void clearClickedWifiOnSliceUnpinned() {
+        clearClickedWifi();
+    }
+
+    protected boolean isSessionValid() {
+        return true;
+    }
+
+    public void registerNetworkCallback(Network wifiNetwork) {
+        if (wifiNetwork == null) {
             return;
         }
 
-        final List<WifiSliceItem> resultList = new ArrayList<>();
-        final WifiEntry connectedWifiEntry = mWifiPickerTracker.getConnectedWifiEntry();
-        if (connectedWifiEntry != null) {
-            connectedWifiEntry.setListener(this);
-            resultList.add(new WifiSliceItem(getContext(), connectedWifiEntry));
+        if (mNetworkCallback != null && mNetworkCallback.isSameNetwork(wifiNetwork)) {
+            return;
         }
-        for (WifiEntry wifiEntry : mWifiPickerTracker.getWifiEntries()) {
-            if (resultList.size() >= getApRowCount()) {
-                break;
+
+        unregisterNetworkCallback();
+
+        mNetworkCallback = new WifiNetworkCallback(wifiNetwork);
+        mConnectivityManager.registerNetworkCallback(
+                new NetworkRequest.Builder()
+                        .clearCapabilities()
+                        .addTransportType(TRANSPORT_WIFI)
+                        .build(),
+                mNetworkCallback,
+                new Handler(Looper.getMainLooper()));
+    }
+
+    public void unregisterNetworkCallback() {
+        if (mNetworkCallback != null) {
+            try {
+                mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+            } catch (RuntimeException e) {
+                Log.e(TAG, "Unregistering CaptivePortalNetworkCallback failed.", e);
             }
-            if (wifiEntry.getLevel() != WifiEntry.WIFI_LEVEL_UNREACHABLE) {
-                wifiEntry.setListener(this);
-                resultList.add(new WifiSliceItem(getContext(), wifiEntry));
+            mNetworkCallback = null;
+        }
+    }
+
+    class WifiNetworkCallback extends NetworkCallback {
+
+        private final Network mNetwork;
+        private boolean mIsCaptivePortal;
+        private boolean mHasPartialConnectivity;
+        private boolean mIsValidated;
+
+        WifiNetworkCallback(Network network) {
+            mNetwork = Preconditions.checkNotNull(network);
+        }
+
+        @Override
+        public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+            if (!isSameNetwork(network)) {
+                return;
+            }
+
+            final boolean prevIsCaptivePortal = mIsCaptivePortal;
+            final boolean prevHasPartialConnectivity = mHasPartialConnectivity;
+            final boolean prevIsValidated = mIsValidated;
+
+            mIsCaptivePortal = nc.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+            mHasPartialConnectivity = nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+            mIsValidated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
+
+            if (prevIsCaptivePortal == mIsCaptivePortal
+                    && prevHasPartialConnectivity == mHasPartialConnectivity
+                    && prevIsValidated == mIsValidated) {
+                return;
+            }
+
+            notifySliceChange();
+
+            // Automatically start captive portal
+            if (!prevIsCaptivePortal && mIsCaptivePortal
+                    && isWifiClicked(mWifiTracker.getManager().getConnectionInfo())
+                    && isSessionValid()) {
+                final Intent intent = new Intent(mContext, ConnectToWifiHandler.class)
+                        .putExtra(ConnectivityManager.EXTRA_NETWORK, network)
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                // Sending a broadcast in the system process needs to specify a user
+                mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
             }
         }
-        super.updateResults(resultList);
+
+        /**
+         * Returns true if the supplied network is not null and is the same as the originally
+         * supplied value.
+         */
+        public boolean isSameNetwork(Network network) {
+            return mNetwork.equals(network);
+        }
     }
 }
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index 324d68c..a489b05 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -29,6 +29,9 @@
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
@@ -49,8 +52,9 @@
 import com.android.settings.slices.SliceBuilderUtils;
 import com.android.settings.wifi.WifiDialogActivity;
 import com.android.settings.wifi.WifiSettings;
-import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
-import com.android.wifitrackerlib.WifiEntry;
+import com.android.settings.wifi.WifiUtils;
+import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
+import com.android.settingslib.wifi.AccessPoint;
 
 import java.util.Arrays;
 import java.util.List;
@@ -67,10 +71,12 @@
 
     protected final Context mContext;
     protected final WifiManager mWifiManager;
+    protected final ConnectivityManager mConnectivityManager;
 
     public WifiSlice(Context context) {
         mContext = context;
         mWifiManager = mContext.getSystemService(WifiManager.class);
+        mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
     }
 
     @Override
@@ -81,16 +87,17 @@
     @Override
     public Slice getSlice() {
         final boolean isWifiEnabled = isWifiEnabled();
-        ListBuilder listBuilder = getListBuilder(isWifiEnabled, null /* wifiSliceItem */);
+        ListBuilder listBuilder = getListBuilder(isWifiEnabled, null /* accessPoint */);
         if (!isWifiEnabled) {
+            WifiScanWorker.clearClickedWifi();
             return listBuilder.build();
         }
 
         final WifiScanWorker worker = SliceBackgroundWorker.getInstance(getUri());
-        final List<WifiSliceItem> apList = worker != null ? worker.getResults() : null;
+        final List<AccessPoint> apList = worker != null ? worker.getResults() : null;
         final int apCount = apList == null ? 0 : apList.size();
-        final boolean isFirstApActive = apCount > 0
-                && apList.get(0).getConnectedState() != WifiEntry.CONNECTED_STATE_DISCONNECTED;
+        final boolean isFirstApActive = apCount > 0 && apList.get(0).isActive();
+        handleNetworkCallback(worker, isFirstApActive);
 
         if (isFirstApActive) {
             // refresh header subtext
@@ -105,7 +112,7 @@
         final CharSequence placeholder = mContext.getText(R.string.summary_placeholder);
         for (int i = 0; i < DEFAULT_EXPANDED_ROW_COUNT; i++) {
             if (i < apCount) {
-                listBuilder.addRow(getWifiSliceItemRow(apList.get(i)));
+                listBuilder.addRow(getAccessPointRow(apList.get(i)));
             } else if (i == apCount) {
                 listBuilder.addRow(getLoadingRow(placeholder));
             } else {
@@ -117,12 +124,22 @@
         return listBuilder.build();
     }
 
+    private void handleNetworkCallback(WifiScanWorker worker, boolean isFirstApActive) {
+        if (worker == null) {
+            return;
+        }
+        if (isFirstApActive) {
+            worker.registerNetworkCallback(mWifiManager.getCurrentNetwork());
+        } else {
+            worker.unregisterNetworkCallback();
+        }
+    }
+
     protected boolean isApRowCollapsed() {
         return false;
     }
 
-    protected ListBuilder.RowBuilder getHeaderRow(boolean isWifiEnabled,
-            WifiSliceItem wifiSliceItem) {
+    protected ListBuilder.RowBuilder getHeaderRow(boolean isWifiEnabled, AccessPoint accessPoint) {
         final IconCompat icon = IconCompat.createWithResource(mContext,
                 R.drawable.ic_settings_wireless);
         final String title = mContext.getString(R.string.wifi_settings);
@@ -135,90 +152,115 @@
                 .setPrimaryAction(primarySliceAction);
     }
 
-    private ListBuilder getListBuilder(boolean isWifiEnabled, WifiSliceItem wifiSliceItem) {
+    private ListBuilder getListBuilder(boolean isWifiEnabled, AccessPoint accessPoint) {
         final PendingIntent toggleAction = getBroadcastIntent(mContext);
         final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
                 null /* actionTitle */, isWifiEnabled);
         final ListBuilder builder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
                 .setAccentColor(COLOR_NOT_TINTED)
                 .setKeywords(getKeywords())
-                .addRow(getHeaderRow(isWifiEnabled, wifiSliceItem))
+                .addRow(getHeaderRow(isWifiEnabled, accessPoint))
                 .addAction(toggleSliceAction);
         return builder;
     }
 
-    private ListBuilder.RowBuilder getWifiSliceItemRow(WifiSliceItem wifiSliceItem) {
-        final CharSequence title = wifiSliceItem.getTitle();
-        final IconCompat levelIcon = getWifiSliceItemLevelIcon(wifiSliceItem);
+    private ListBuilder.RowBuilder getAccessPointRow(AccessPoint accessPoint) {
+        final boolean isCaptivePortal = accessPoint.isActive() && isCaptivePortal();
+        final CharSequence title = accessPoint.getTitle();
+        final CharSequence summary = getAccessPointSummary(accessPoint, isCaptivePortal);
+        final IconCompat levelIcon = getAccessPointLevelIcon(accessPoint);
         final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder()
                 .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE)
                 .setTitle(title)
-                .setSubtitle(wifiSliceItem.getSummary())
-                .setContentDescription(wifiSliceItem.getContentDescription())
-                .setPrimaryAction(getWifiEntryAction(wifiSliceItem, levelIcon, title));
+                .setSubtitle(summary)
+                .setPrimaryAction(getAccessPointAction(accessPoint, isCaptivePortal, levelIcon,
+                        title));
 
-        final IconCompat endIcon = getEndIcon(wifiSliceItem);
-        if (endIcon != null) {
-            rowBuilder.addEndItem(endIcon, ListBuilder.ICON_IMAGE);
+        if (isCaptivePortal) {
+            rowBuilder.addEndItem(getCaptivePortalEndAction(accessPoint, title));
+        } else {
+            final IconCompat endIcon = getEndIcon(accessPoint);
+            if (endIcon != null) {
+                rowBuilder.addEndItem(endIcon, ListBuilder.ICON_IMAGE);
+            }
         }
         return rowBuilder;
     }
 
-    private IconCompat getWifiSliceItemLevelIcon(WifiSliceItem wifiSliceItem) {
+    private CharSequence getAccessPointSummary(AccessPoint accessPoint, boolean isCaptivePortal) {
+        if (isCaptivePortal) {
+            return mContext.getText(R.string.wifi_tap_to_sign_in);
+        }
+
+        final CharSequence summary = accessPoint.getSettingsSummary();
+        return TextUtils.isEmpty(summary) ? mContext.getText(R.string.disconnected) : summary;
+    }
+
+    private IconCompat getAccessPointLevelIcon(AccessPoint accessPoint) {
         final @ColorInt int tint;
-        if (wifiSliceItem.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
-            tint = Utils.getColorAccentDefaultColor(mContext);
-        } else if (wifiSliceItem.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
-            tint = Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal);
-        } else {
-            tint = Utils.getDisabled(mContext, Utils.getColorAttrDefaultColor(mContext,
+        if (accessPoint.isActive()) {
+            final NetworkInfo.State state = accessPoint.getNetworkInfo().getState();
+            if (state == NetworkInfo.State.CONNECTED) {
+                tint = Utils.getColorAccentDefaultColor(mContext);
+            } else { // connecting
+                tint = Utils.getDisabled(mContext, Utils.getColorAttrDefaultColor(mContext,
                         android.R.attr.colorControlNormal));
+            }
+        } else {
+            tint = Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal);
         }
 
         final Drawable drawable = mContext.getDrawable(
-                com.android.settingslib.Utils.getWifiIconResource(wifiSliceItem.getLevel()));
+                com.android.settingslib.Utils.getWifiIconResource(accessPoint.getLevel()));
         drawable.setTint(tint);
         return Utils.createIconWithDrawable(drawable);
     }
 
-    private IconCompat getEndIcon(WifiSliceItem wifiSliceItem) {
-        if (wifiSliceItem.getConnectedState() != WifiEntry.CONNECTED_STATE_DISCONNECTED) {
+    private IconCompat getEndIcon(AccessPoint accessPoint) {
+        if (accessPoint.isActive()) {
             return null;
-        }
-
-        if (wifiSliceItem.getSecurity() != WifiEntry.SECURITY_NONE) {
+        } else if (accessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
             return IconCompat.createWithResource(mContext, R.drawable.ic_friction_lock_closed);
+        } else if (accessPoint.isMetered()) {
+            return IconCompat.createWithResource(mContext, R.drawable.ic_friction_money);
         }
         return null;
     }
 
-    private SliceAction getWifiEntryAction(WifiSliceItem wifiSliceItem, IconCompat icon,
-            CharSequence title) {
-        final int requestCode = wifiSliceItem.getKey().hashCode();
+    private SliceAction getCaptivePortalEndAction(AccessPoint accessPoint, CharSequence title) {
+        return getAccessPointAction(accessPoint, false /* isCaptivePortal */,
+                IconCompat.createWithResource(mContext, R.drawable.ic_settings_accent), title);
+    }
 
-        if (wifiSliceItem.getConnectedState() != WifiEntry.CONNECTED_STATE_DISCONNECTED) {
-            final Bundle bundle = new Bundle();
-            bundle.putString(WifiNetworkDetailsFragment2.KEY_CHOSEN_WIFIENTRY_KEY,
-                    wifiSliceItem.getKey());
+    private SliceAction getAccessPointAction(AccessPoint accessPoint, boolean isCaptivePortal,
+            IconCompat icon, CharSequence title) {
+        final int requestCode = accessPoint.hashCode();
+        if (isCaptivePortal) {
+            final Intent intent = new Intent(mContext, ConnectToWifiHandler.class)
+                    .putExtra(ConnectivityManager.EXTRA_NETWORK, mWifiManager.getCurrentNetwork());
+            return getBroadcastAction(requestCode, intent, icon, title);
+        }
+
+        final Bundle extras = new Bundle();
+        accessPoint.saveWifiState(extras);
+
+        if (accessPoint.isActive()) {
             final Intent intent = new SubSettingLauncher(mContext)
                     .setTitleRes(R.string.pref_title_network_details)
-                    .setDestination(WifiNetworkDetailsFragment2.class.getName())
-                    .setArguments(bundle)
+                    .setDestination(WifiNetworkDetailsFragment.class.getName())
+                    .setArguments(extras)
                     .setSourceMetricsCategory(SettingsEnums.WIFI)
                     .toIntent();
             return getActivityAction(requestCode, intent, icon, title);
-        }
-
-        if (wifiSliceItem.shouldEditBeforeConnect()) {
+        } else if (WifiUtils.getConnectingType(accessPoint) != WifiUtils.CONNECT_TYPE_OTHERS) {
+            final Intent intent = new Intent(mContext, ConnectToWifiHandler.class)
+                    .putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras);
+            return getBroadcastAction(requestCode, intent, icon, title);
+        } else {
             final Intent intent = new Intent(mContext, WifiDialogActivity.class)
-                    .putExtra(WifiDialogActivity.KEY_CHOSEN_WIFIENTRY_KEY, wifiSliceItem.getKey());
+                    .putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras);
             return getActivityAction(requestCode, intent, icon, title);
         }
-
-        final Intent intent = new Intent(mContext, ConnectToWifiHandler.class)
-                .putExtra(ConnectToWifiHandler.KEY_CHOSEN_WIFIENTRY_KEY, wifiSliceItem.getKey())
-                .putExtra(ConnectToWifiHandler.KEY_WIFI_SLICE_URI, getUri());
-        return getBroadcastAction(requestCode, intent, icon, title);
     }
 
     private SliceAction getActivityAction(int requestCode, Intent intent, IconCompat icon,
@@ -249,6 +291,12 @@
                 .setSubtitle(title);
     }
 
+    protected boolean isCaptivePortal() {
+        final NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(
+                mWifiManager.getCurrentNetwork());
+        return WifiUtils.canSignIntoNetwork(nc);
+    }
+
     /**
      * Update the current wifi status to the boolean value keyed by
      * {@link android.app.slice.Slice#EXTRA_TOGGLE_STATE} on {@param intent}.
diff --git a/src/com/android/settings/wifi/slice/WifiSliceItem.java b/src/com/android/settings/wifi/slice/WifiSliceItem.java
deleted file mode 100644
index 7a0f0d7..0000000
--- a/src/com/android/settings/wifi/slice/WifiSliceItem.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.wifi.slice;
-
-import android.content.Context;
-import android.text.TextUtils;
-
-import com.android.settingslib.R;
-import com.android.wifitrackerlib.WifiEntry;
-
-/**
- * The data set which is needed by a Wi-Fi Slice, it collects necessary data from {@link WifiEntry}
- * and provides similar getter methods for corresponding data.
- */
-public class WifiSliceItem {
-
-    private final Context mContext;
-    private final String mKey;
-    private final String mTitle;
-    private final int mSecurity;
-    private final int mConnectedState;
-    private final int mLevel;
-    private final boolean mShouldEditBeforeConnect;
-    private final String mSummary;
-
-    // These values must be kept within [WifiEntry.WIFI_LEVEL_MIN, WifiEntry.WIFI_LEVEL_MAX]
-    private static final int[] WIFI_CONNECTION_STRENGTH = {
-            R.string.accessibility_no_wifi,
-            R.string.accessibility_wifi_one_bar,
-            R.string.accessibility_wifi_two_bars,
-            R.string.accessibility_wifi_three_bars,
-            R.string.accessibility_wifi_signal_full
-    };
-
-    public WifiSliceItem(Context context, WifiEntry wifiEntry) {
-        mContext = context;
-        mKey = wifiEntry.getKey();
-        mTitle = wifiEntry.getTitle();
-        mSecurity = wifiEntry.getSecurity();
-        mConnectedState = wifiEntry.getConnectedState();
-        mLevel = wifiEntry.getLevel();
-        mShouldEditBeforeConnect = wifiEntry.shouldEditBeforeConnect();
-        mSummary = wifiEntry.getSummary(false /* concise */);
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (!(other instanceof WifiSliceItem)) {
-            return false;
-        }
-
-        final WifiSliceItem otherItem = (WifiSliceItem) other;
-        if (!TextUtils.equals(getKey(), otherItem.getKey())) {
-            return false;
-        }
-        if (getConnectedState() != otherItem.getConnectedState()) {
-            return false;
-        }
-        if (getLevel() != otherItem.getLevel()) {
-            return false;
-        }
-        if (!TextUtils.equals(getSummary(), otherItem.getSummary())) {
-            return false;
-        }
-        return true;
-    }
-
-    public String getKey() {
-        return mKey;
-    }
-
-    public String getTitle() {
-        return mTitle;
-    }
-
-    public int getSecurity() {
-        return mSecurity;
-    }
-
-    public int getConnectedState() {
-        return mConnectedState;
-    }
-
-    public int getLevel() {
-        return mLevel;
-    }
-
-    /**
-     * In Wi-Fi picker, when users click a saved network, it will connect to the Wi-Fi network.
-     * However, for some special cases, Wi-Fi picker should show Wi-Fi editor UI for users to edit
-     * security or password before connecting. Or users will always get connection fail results.
-     */
-    public boolean shouldEditBeforeConnect() {
-        return mShouldEditBeforeConnect;
-    }
-
-    /**
-     * Returns a 'NOT' concise summary, this is different from WifiEntry#getSummary().
-     */
-    public String getSummary() {
-        return mSummary;
-    }
-
-    /**
-     * This method has similar code as WifiEntryPreference#buildContentDescription().
-     * TODO(b/154191825): Adds WifiEntry#getContentDescription() to replace the duplicate code.
-     */
-    public CharSequence getContentDescription() {
-        CharSequence contentDescription = mTitle;
-        if (!TextUtils.isEmpty(mSummary)) {
-            contentDescription = TextUtils.concat(contentDescription, ",", mSummary);
-        }
-        if (mLevel >= 0 && mLevel < WIFI_CONNECTION_STRENGTH.length) {
-            contentDescription = TextUtils.concat(contentDescription, ",",
-                    mContext.getString(WIFI_CONNECTION_STRENGTH[mLevel]));
-        }
-        return TextUtils.concat(contentDescription, ",", mSecurity == WifiEntry.SECURITY_NONE
-                ? mContext.getString(R.string.accessibility_wifi_security_type_none)
-                : mContext.getString(R.string.accessibility_wifi_security_type_secured));
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiManager.java
index ea57bf7..80db53e 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiManager.java
@@ -18,7 +18,6 @@
 
 import static org.robolectric.RuntimeEnvironment.application;
 
-import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
@@ -82,11 +81,6 @@
         return false;
     }
 
-    @Implementation
-    protected List<ScanResult> getScanResults() {
-        return new ArrayList<ScanResult>();
-    }
-
     public static ShadowWifiManager get() {
         return Shadow.extract(application.getSystemService(WifiManager.class));
     }
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
index 12f7c67..41d1bbe 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
@@ -50,7 +50,7 @@
 
     private static final String AP1_SSID = "\"ap1\"";
     @Mock
-    private WifiConfigController2 mController;
+    private WifiConfigController mController;
 
     @Before
     public void setUp() {
@@ -63,12 +63,8 @@
 
     @Test
     public void onSubmit_shouldConnectToNetwork() {
-        WifiDialogActivity activity =
-                Robolectric.buildActivity(WifiDialogActivity.class,
-                        new Intent().putExtra(WifiDialogActivity.KEY_CHOSEN_WIFIENTRY_KEY,
-                                "StandardWifiEntry:OpenNetwork,0"))
-                        .setup().get();
-        WifiDialog2 dialog = (WifiDialog2) ShadowAlertDialogCompat.getLatestAlertDialog();
+        WifiDialogActivity activity = Robolectric.setupActivity(WifiDialogActivity.class);
+        WifiDialog dialog = (WifiDialog) ShadowAlertDialogCompat.getLatestAlertDialog();
         assertThat(dialog).isNotNull();
 
         ReflectionHelpers.setField(dialog, "mController", mController);
@@ -79,17 +75,34 @@
     }
 
     @Test
+    public void onSubmit_whenConnectForCallerIsFalse_shouldNotConnectToNetwork() {
+        WifiDialogActivity activity =
+                Robolectric.buildActivity(
+                        WifiDialogActivity.class,
+                        new Intent().putExtra(WifiDialogActivity.KEY_CONNECT_FOR_CALLER, false))
+                        .setup().get();
+        WifiDialog dialog = (WifiDialog) ShadowAlertDialogCompat.getLatestAlertDialog();
+
+        assertThat(dialog).isNotNull();
+
+        ReflectionHelpers.setField(dialog, "mController", mController);
+
+        activity.onSubmit(dialog);
+
+        assertThat(ShadowWifiManager.get().savedWifiConfig).isNull();
+    }
+
+    @Test
     public void onSubmit_whenLaunchInSetupFlow_shouldBeLightThemeForWifiDialog() {
         WifiDialogActivity activity =
                 Robolectric.buildActivity(
                         WifiDialogActivity.class,
                         new Intent()
+                                .putExtra(WifiDialogActivity.KEY_CONNECT_FOR_CALLER, false)
                                 .putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true)
-                                .putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true)
-                                .putExtra(WifiDialogActivity.KEY_CHOSEN_WIFIENTRY_KEY,
-                                        "StandardWifiEntry:OpenNetwork,0"))
+                                .putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true))
                         .setup().get();
-        WifiDialog2 dialog = (WifiDialog2) ShadowAlertDialogCompat.getLatestAlertDialog();
+        WifiDialog dialog = (WifiDialog) ShadowAlertDialogCompat.getLatestAlertDialog();
 
         assertThat(dialog).isNotNull();
 
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ConnectToWifiHandlerTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ConnectToWifiHandlerTest.java
index 829ec8f..ac1384f 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/ConnectToWifiHandlerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/ConnectToWifiHandlerTest.java
@@ -26,69 +26,95 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.Intent;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.net.wifi.WifiManager;
 
-import com.android.settings.wifi.WifiDialogActivity;
-import com.android.wifitrackerlib.WifiEntry;
-import com.android.wifitrackerlib.WifiEntry.ConnectCallback;
+import com.android.settings.testutils.shadow.ShadowWifiManager;
+import com.android.settingslib.wifi.AccessPoint;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowWifiManager.class)
 public class ConnectToWifiHandlerTest {
 
     private static final String AP_SSID = "\"ap\"";
     private Context mContext;
     private ConnectToWifiHandler mHandler;
+    private WifiConfiguration mWifiConfig;
     @Mock
-    private WifiScanWorker mWifiScanWorker;
+    private AccessPoint mAccessPoint;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mContext = spy(RuntimeEnvironment.application);
-        mHandler = spy(new ConnectToWifiHandler());
-        doReturn(mWifiScanWorker).when(mHandler).getWifiScanWorker(any());
+        mContext = RuntimeEnvironment.application;
+        mHandler = new ConnectToWifiHandler();
+        mWifiConfig = spy(new WifiConfiguration());
+        mWifiConfig.SSID = AP_SSID;
+        doReturn(mWifiConfig).when(mAccessPoint).getConfig();
     }
 
     @Test
-    public void onReceive_nonNullKeyAndUri_shouldConnectWifintry() {
-        final Intent intent = new Intent();
-        final String key = "key";
-        intent.putExtra(ConnectToWifiHandler.KEY_CHOSEN_WIFIENTRY_KEY, key);
-        intent.putExtra(ConnectToWifiHandler.KEY_WIFI_SLICE_URI,
-                com.android.settings.slices.CustomSliceRegistry.WIFI_SLICE_URI);
-        final WifiEntry wifiEntry = mock(WifiEntry.class);
-        when(mWifiScanWorker.getWifiEntry(key)).thenReturn(wifiEntry);
+    public void connect_shouldConnectToUnsavedOpenNetwork() {
+        when(mAccessPoint.isSaved()).thenReturn(false);
+        when(mAccessPoint.getSecurity()).thenReturn(AccessPoint.SECURITY_NONE);
 
-        mHandler.onReceive(mContext, intent);
+        mHandler.connect(mContext, mAccessPoint);
 
-        verify(wifiEntry).connect(any());
+        assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP_SSID);
     }
 
     @Test
-    public void onConnectResult_failNoConfig_shouldStartActivity() {
-        final String key = "key";
-        final WifiEntry wifiEntry = mock(WifiEntry.class);
-        when(wifiEntry.getKey()).thenReturn(key);
-        final ConnectToWifiHandler.WifiEntryConnectCallback callback =
-                spy(new ConnectToWifiHandler.WifiEntryConnectCallback(mContext, wifiEntry));
+    public void connect_shouldStartOsuProvisioning() {
+        when(mAccessPoint.isSaved()).thenReturn(false);
+        when(mAccessPoint.isOsuProvider()).thenReturn(true);
 
-        callback.onConnectResult(ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG);
+        mHandler.connect(mContext, mAccessPoint);
 
-        final ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext).startActivity(argument.capture());
-        assertThat(argument.getValue().getStringExtra(WifiDialogActivity.KEY_CHOSEN_WIFIENTRY_KEY))
-                .isEqualTo(key);
-        assertThat(argument.getValue().getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK)
-                .isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK);
+        verify(mAccessPoint).startOsuProvisioning(any(WifiManager.ActionListener.class));
+    }
+
+
+    @Test
+    public void connect_shouldConnectWithPasspointProvider() {
+        when(mAccessPoint.isSaved()).thenReturn(false);
+        when(mAccessPoint.isPasspoint()).thenReturn(true);
+
+        mHandler.connect(mContext, mAccessPoint);
+
+        assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP_SSID);
+    }
+
+    @Test
+    public void connect_shouldConnectToSavedSecuredNetwork() {
+        when(mAccessPoint.isSaved()).thenReturn(true);
+        when(mAccessPoint.getSecurity()).thenReturn(AccessPoint.SECURITY_PSK);
+        final NetworkSelectionStatus status = mock(NetworkSelectionStatus.class);
+        when(status.hasEverConnected()).thenReturn(true);
+        when(mWifiConfig.getNetworkSelectionStatus()).thenReturn(status);
+
+        mHandler.connect(mContext, mAccessPoint);
+
+        assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP_SSID);
+    }
+
+    @Test
+    public void connect_shouldNotConnectToUnsavedSecuredNetwork() {
+        when(mAccessPoint.isSaved()).thenReturn(false);
+        when(mAccessPoint.getSecurity()).thenReturn(AccessPoint.SECURITY_PSK);
+
+        mHandler.connect(mContext, mAccessPoint);
+
+        assertThat(ShadowWifiManager.get().savedWifiConfig).isNull();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiScanWorkerTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiScanWorkerTest.java
new file mode 100644
index 0000000..0e52520
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiScanWorkerTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settings.wifi.slice;
+
+import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.wifi.WifiManager;
+import android.os.UserHandle;
+
+import com.android.settings.testutils.shadow.ShadowWifiManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+        ShadowWifiManager.class,
+        WifiScanWorkerTest.ShadowWifiTracker.class,
+})
+public class ContextualWifiScanWorkerTest {
+
+    private Context mContext;
+    private WifiManager mWifiManager;
+    private ConnectivityManager mConnectivityManager;
+    private ContextualWifiScanWorker mWifiScanWorker;
+    private ConnectToWifiHandler mConnectToWifiHandler;
+
+    @Before
+    public void setUp() {
+        mContext = spy(RuntimeEnvironment.application);
+        mWifiManager = mContext.getSystemService(WifiManager.class);
+        mWifiManager.setWifiEnabled(true);
+
+        mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
+        mWifiScanWorker = new ContextualWifiScanWorker(mContext, CONTEXTUAL_WIFI_SLICE_URI);
+        mConnectToWifiHandler = new ConnectToWifiHandler();
+    }
+
+    @After
+    public void tearDown() {
+        mWifiScanWorker.clearClickedWifi();
+    }
+
+    @Test
+    public void NetworkCallback_onCapabilitiesChanged_sliceIsUnpinned_shouldSendBroadcast() {
+        final Intent intent = WifiScanWorkerTest.getIntentWithAccessPoint("ap1");
+        WifiScanWorkerTest.setConnectionInfoSSID("ap1");
+        final Network network = mConnectivityManager.getActiveNetwork();
+        mWifiScanWorker.registerNetworkCallback(network);
+        final NetworkCallback callback = mWifiScanWorker.mNetworkCallback;
+
+        mWifiScanWorker.onSlicePinned();
+        mConnectToWifiHandler.onReceive(mContext, intent);
+        mWifiScanWorker.onSliceUnpinned();
+        callback.onCapabilitiesChanged(network,
+                WifiSliceTest.makeCaptivePortalNetworkCapabilities());
+
+        verify(mContext).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.CURRENT));
+    }
+
+    @Test
+    public void NetworkCallback_onCapabilitiesChanged_newSession_shouldNotSendBroadcast() {
+        final Intent intent = WifiScanWorkerTest.getIntentWithAccessPoint("ap1");
+        WifiScanWorkerTest.setConnectionInfoSSID("ap1");
+        final Network network = mConnectivityManager.getActiveNetwork();
+        mWifiScanWorker.registerNetworkCallback(network);
+
+        mConnectToWifiHandler.onReceive(mContext, intent);
+        ContextualWifiScanWorker.newVisibleUiSession();
+        mWifiScanWorker.mNetworkCallback.onCapabilitiesChanged(network,
+                WifiSliceTest.makeCaptivePortalNetworkCapabilities());
+
+        verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.CURRENT));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
index 395048c..3319543 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
@@ -17,89 +17,216 @@
 package com.android.settings.wifi.slice;
 
 import static com.android.settings.slices.CustomSliceRegistry.WIFI_SLICE_URI;
+import static com.android.settings.wifi.WifiDialogActivity.KEY_ACCESS_POINT_STATE;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import androidx.lifecycle.Lifecycle;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.UserHandle;
 
-import com.android.wifitrackerlib.WifiEntry;
-import com.android.wifitrackerlib.WifiPickerTracker;
+import com.android.settings.slices.ShadowSliceBackgroundWorker;
+import com.android.settings.testutils.shadow.ShadowWifiManager;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowNetworkInfo;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowSliceBackgroundWorker.class, ShadowWifiManager.class,
+        WifiScanWorkerTest.ShadowWifiTracker.class})
 public class WifiScanWorkerTest {
 
+    private Context mContext;
+    private ContentResolver mResolver;
+    private WifiManager mWifiManager;
+    private ConnectivityManager mConnectivityManager;
     private WifiScanWorker mWifiScanWorker;
-    @Mock
-    WifiPickerTracker mWifiPickerTracker;
+    private ConnectToWifiHandler mConnectToWifiHandler;
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mResolver = mock(ContentResolver.class);
+        doReturn(mResolver).when(mContext).getContentResolver();
+        mWifiManager = mContext.getSystemService(WifiManager.class);
+        mWifiManager.setWifiEnabled(true);
 
-        mWifiScanWorker = new WifiScanWorker(RuntimeEnvironment.application, WIFI_SLICE_URI);
-        mWifiScanWorker.mWifiPickerTracker = mWifiPickerTracker;
+        mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
+        mWifiScanWorker = new WifiScanWorker(mContext, WIFI_SLICE_URI);
+        mConnectToWifiHandler = new ConnectToWifiHandler();
+    }
+
+    @After
+    public void tearDown() {
+        mWifiScanWorker.clearClickedWifi();
     }
 
     @Test
-    public void onConstructor_shouldBeInCreatedState() {
-        assertThat(mWifiScanWorker.getLifecycle().getCurrentState())
-                .isEqualTo(Lifecycle.State.CREATED);
+    public void onWifiStateChanged_shouldNotifyChange() {
+        mWifiScanWorker.onWifiStateChanged(WifiManager.WIFI_STATE_DISABLED);
+
+        verify(mResolver).notifyChange(WIFI_SLICE_URI, null);
     }
 
     @Test
-    public void onSlicePinned_shouldBeInResumedState() {
+    public void AccessPointList_sameState_shouldBeTheSame() {
+        final AccessPoint ap1 = createAccessPoint(DetailedState.CONNECTED);
+        final AccessPoint ap2 = createAccessPoint(DetailedState.CONNECTED);
+
+        assertThat(mWifiScanWorker.areListsTheSame(Arrays.asList(ap1), Arrays.asList(ap2)))
+                .isTrue();
+    }
+
+    @Test
+    public void AccessPointList_differentState_shouldBeDifferent() {
+        final AccessPoint ap1 = createAccessPoint(DetailedState.CONNECTING);
+        final AccessPoint ap2 = createAccessPoint(DetailedState.CONNECTED);
+
+        assertThat(mWifiScanWorker.areListsTheSame(Arrays.asList(ap1), Arrays.asList(ap2)))
+                .isFalse();
+    }
+
+    @Test
+    public void AccessPointList_differentListLength_shouldBeDifferent() {
+        final AccessPoint ap1 = createAccessPoint(DetailedState.CONNECTED);
+        final AccessPoint ap2 = createAccessPoint(DetailedState.CONNECTED);
+        final List<AccessPoint> list = new ArrayList<>();
+        list.add(ap1);
+        list.add(ap2);
+
+        assertThat(mWifiScanWorker.areListsTheSame(list, Arrays.asList(ap1))).isFalse();
+    }
+
+    @Test
+    public void NetworkCallback_onCapabilitiesChanged_shouldNotifyChange() {
+        final Network network = mConnectivityManager.getActiveNetwork();
+        mWifiScanWorker.registerNetworkCallback(network);
+
+        mWifiScanWorker.mNetworkCallback.onCapabilitiesChanged(network,
+                WifiSliceTest.makeCaptivePortalNetworkCapabilities());
+
+        verify(mResolver).notifyChange(WIFI_SLICE_URI, null);
+    }
+
+    @Test
+    public void NetworkCallback_onCapabilitiesChanged_isClickedWifi_shouldSendBroadcast() {
+        final Intent intent = getIntentWithAccessPoint("ap1");
+        setConnectionInfoSSID("ap1");
+        final Network network = mConnectivityManager.getActiveNetwork();
+        mWifiScanWorker.registerNetworkCallback(network);
+
+        mConnectToWifiHandler.onReceive(mContext, intent);
+        mWifiScanWorker.mNetworkCallback.onCapabilitiesChanged(network,
+                WifiSliceTest.makeCaptivePortalNetworkCapabilities());
+
+        verify(mContext).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.CURRENT));
+    }
+
+    @Test
+    public void NetworkCallback_onCapabilitiesChanged_isNotClickedWifi_shouldNotSendBroadcast() {
+        final Intent intent = getIntentWithAccessPoint("ap1");
+        setConnectionInfoSSID("ap2");
+        final Network network = mConnectivityManager.getActiveNetwork();
+        mWifiScanWorker.registerNetworkCallback(network);
+
+        mConnectToWifiHandler.onReceive(mContext, intent);
+        mWifiScanWorker.mNetworkCallback.onCapabilitiesChanged(network,
+                WifiSliceTest.makeCaptivePortalNetworkCapabilities());
+
+        verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.CURRENT));
+    }
+
+    @Test
+    public void NetworkCallback_onCapabilitiesChanged_neverClickWifi_shouldNotSendBroadcast() {
+        setConnectionInfoSSID("ap1");
+        final Network network = mConnectivityManager.getActiveNetwork();
+        mWifiScanWorker.registerNetworkCallback(network);
+
+        mWifiScanWorker.mNetworkCallback.onCapabilitiesChanged(network,
+                WifiSliceTest.makeCaptivePortalNetworkCapabilities());
+
+        verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.CURRENT));
+    }
+
+    @Test
+    public void NetworkCallback_onCapabilitiesChanged_sliceIsUnpinned_shouldNotSendBroadcast() {
+        final Intent intent = getIntentWithAccessPoint("ap1");
+        setConnectionInfoSSID("ap1");
+        final Network network = mConnectivityManager.getActiveNetwork();
+        mWifiScanWorker.registerNetworkCallback(network);
+        final NetworkCallback callback = mWifiScanWorker.mNetworkCallback;
+
         mWifiScanWorker.onSlicePinned();
-
-        assertThat(mWifiScanWorker.getLifecycle().getCurrentState())
-                .isEqualTo(Lifecycle.State.RESUMED);
-    }
-
-    @Test
-    public void onSliceUnpinned_shouldBeInCreatedState() {
+        mConnectToWifiHandler.onReceive(mContext, intent);
         mWifiScanWorker.onSliceUnpinned();
+        callback.onCapabilitiesChanged(network,
+                WifiSliceTest.makeCaptivePortalNetworkCapabilities());
 
-        assertThat(mWifiScanWorker.getLifecycle().getCurrentState())
-                .isEqualTo(Lifecycle.State.CREATED);
+        verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.CURRENT));
     }
 
-    @Test
-    public void close_shouldBeInDestroyedState() {
-        mWifiScanWorker.close();
-
-        assertThat(mWifiScanWorker.getLifecycle().getCurrentState())
-                .isEqualTo(Lifecycle.State.DESTROYED);
+    static Intent getIntentWithAccessPoint(String ssid) {
+        final Bundle savedState = new Bundle();
+        savedState.putString("key_ssid", ssid);
+        return new Intent().putExtra(KEY_ACCESS_POINT_STATE, savedState);
     }
 
-    @Test
-    public void getWifiEntry_connectedWifiKey_shouldGetConnectedWifi() {
-        final String key = "key";
-        final WifiEntry connectedWifiEntry = mock(WifiEntry.class);
-        when(connectedWifiEntry.getKey()).thenReturn(key);
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(connectedWifiEntry);
-
-        assertThat(mWifiScanWorker.getWifiEntry(key)).isEqualTo(connectedWifiEntry);
+    static void setConnectionInfoSSID(String ssid) {
+        final WifiInfo wifiInfo = mock(WifiInfo.class);
+        when(wifiInfo.getSSID()).thenReturn(ssid);
+        ShadowWifiManager.get().setConnectionInfo(wifiInfo);
     }
 
-    @Test
-    public void getWifiEntry_reachableWifiKey_shouldGetReachableWifi() {
-        final String key = "key";
-        final WifiEntry reachableWifiEntry = mock(WifiEntry.class);
-        when(reachableWifiEntry.getKey()).thenReturn(key);
-        when(mWifiPickerTracker.getWifiEntries()).thenReturn(Arrays.asList(reachableWifiEntry));
+    private AccessPoint createAccessPoint(String ssid, DetailedState detailedState) {
+        final NetworkInfo info = ShadowNetworkInfo.newInstance(detailedState, 1 /* type */,
+                0 /*subType */, true /* isAvailable */, true /* isConnected */);
+        final Bundle savedState = new Bundle();
+        savedState.putString("key_ssid", ssid);
+        savedState.putParcelable("key_networkinfo", info);
+        return new AccessPoint(mContext, savedState);
+    }
 
-        assertThat(mWifiScanWorker.getWifiEntry(key)).isEqualTo(reachableWifiEntry);
+    private AccessPoint createAccessPoint(DetailedState detailedState) {
+        return createAccessPoint("ap", detailedState);
+    }
+
+    @Implements(WifiTracker.class)
+    public static class ShadowWifiTracker {
+        @Implementation
+        public void onStart() {
+            // do nothing
+        }
     }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
index 5431540..ad74a32 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
@@ -23,14 +23,17 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 
@@ -46,8 +49,7 @@
 import com.android.settings.R;
 import com.android.settings.slices.SliceBackgroundWorker;
 import com.android.settings.testutils.SliceTester;
-import com.android.wifitrackerlib.WifiEntry;
-import com.android.wifitrackerlib.WifiEntry.ConnectedState;
+import com.android.settingslib.wifi.AccessPoint;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -72,6 +74,7 @@
     private Context mContext;
     private ContentResolver mResolver;
     private WifiManager mWifiManager;
+    private ConnectivityManager mConnectivityManager;
     private WifiSlice mWifiSlice;
 
     @Before
@@ -85,6 +88,9 @@
         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
         mWifiManager.setWifiEnabled(true);
 
+        mConnectivityManager = spy(mContext.getSystemService(ConnectivityManager.class));
+        doReturn(mConnectivityManager).when(mContext).getSystemService(ConnectivityManager.class);
+
         mWifiSlice = new WifiSlice(mContext);
     }
 
@@ -132,42 +138,33 @@
                 mContext.getString(R.string.wifi_empty_list_wifi_on));
     }
 
-    private WifiSliceItem createWifiSliceItem(String title, @ConnectedState int connectedState) {
-        final WifiEntry wifiEntry = mock(WifiEntry.class);
-        when(wifiEntry.getTitle()).thenReturn(title);
-        when(wifiEntry.getKey()).thenReturn("key");
-        when(wifiEntry.getConnectedState()).thenReturn(connectedState);
-        when(wifiEntry.getLevel()).thenReturn(WifiEntry.WIFI_LEVEL_MAX);
-        return new WifiSliceItem(mContext, wifiEntry);
+    private AccessPoint createAccessPoint(String name, boolean active, boolean reachable) {
+        final AccessPoint accessPoint = mock(AccessPoint.class);
+        doReturn(name).when(accessPoint).getTitle();
+        doReturn(active).when(accessPoint).isActive();
+        doReturn(reachable).when(accessPoint).isReachable();
+        if (active) {
+            final NetworkInfo networkInfo = mock(NetworkInfo.class);
+            doReturn(networkInfo).when(accessPoint).getNetworkInfo();
+            doReturn(NetworkInfo.State.CONNECTED).when(networkInfo).getState();
+        }
+        return accessPoint;
     }
 
-    private void setWorkerResults(WifiSliceItem... wifiSliceItems) {
-        final ArrayList<WifiSliceItem> results = new ArrayList<>();
-        for (WifiSliceItem wifiSliceItem : wifiSliceItems) {
-            results.add(wifiSliceItem);
+    private void setWorkerResults(AccessPoint... accessPoints) {
+        final ArrayList<AccessPoint> results = new ArrayList<>();
+        for (AccessPoint ap : accessPoints) {
+            results.add(ap);
         }
         final SliceBackgroundWorker worker = SliceBackgroundWorker.getInstance(mWifiSlice.getUri());
         doReturn(results).when(worker).getResults();
     }
 
     @Test
-    public void getWifiSlice_oneConnectedAp_shouldReturnLoadingRow() {
-        setWorkerResults(createWifiSliceItem(AP1_NAME, WifiEntry.CONNECTED_STATE_CONNECTED));
-
-        final Slice wifiSlice = mWifiSlice.getSlice();
-        final List<SliceItem> sliceItems = wifiSlice.getItems();
-
-        SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME);
-        // Has scanning text
-        SliceTester.assertAnySliceItemContainsSubtitle(sliceItems,
-                mContext.getString(R.string.wifi_empty_list_wifi_on));
-    }
-
-    @Test
-    public void getWifiSlice_oneConnectedApAndOneDisconnectedAp_shouldReturnLoadingRow() {
+    public void getWifiSlice_noReachableAp_shouldReturnLoadingRow() {
         setWorkerResults(
-                createWifiSliceItem(AP1_NAME, WifiEntry.CONNECTED_STATE_CONNECTED),
-                createWifiSliceItem(AP2_NAME, WifiEntry.CONNECTED_STATE_DISCONNECTED));
+                createAccessPoint(AP1_NAME, false, false),
+                createAccessPoint(AP2_NAME, false, false));
 
         final Slice wifiSlice = mWifiSlice.getSlice();
         final List<SliceItem> sliceItems = wifiSlice.getItems();
@@ -180,8 +177,8 @@
     }
 
     @Test
-    public void getWifiSlice_oneDisconnectedAp_shouldReturnLoadingRow() {
-        setWorkerResults(createWifiSliceItem(AP1_NAME, WifiEntry.CONNECTED_STATE_DISCONNECTED));
+    public void getWifiSlice_oneActiveAp_shouldReturnLoadingRow() {
+        setWorkerResults(createAccessPoint(AP1_NAME, true, true));
 
         final Slice wifiSlice = mWifiSlice.getSlice();
         final List<SliceItem> sliceItems = wifiSlice.getItems();
@@ -193,11 +190,40 @@
     }
 
     @Test
-    public void getWifiSlice_apReachExpandedCount_shouldNotReturnLoadingRow() {
+    public void getWifiSlice_oneActiveApAndOneUnreachableAp_shouldReturnLoadingRow() {
         setWorkerResults(
-                createWifiSliceItem(AP1_NAME, WifiEntry.CONNECTED_STATE_DISCONNECTED),
-                createWifiSliceItem(AP2_NAME, WifiEntry.CONNECTED_STATE_DISCONNECTED),
-                createWifiSliceItem(AP3_NAME, WifiEntry.CONNECTED_STATE_DISCONNECTED));
+                createAccessPoint(AP1_NAME, true, true),
+                createAccessPoint(AP2_NAME, false, false));
+
+        final Slice wifiSlice = mWifiSlice.getSlice();
+        final List<SliceItem> sliceItems = wifiSlice.getItems();
+
+        SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME);
+        SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP2_NAME);
+        // Has scanning text
+        SliceTester.assertAnySliceItemContainsSubtitle(sliceItems,
+                mContext.getString(R.string.wifi_empty_list_wifi_on));
+    }
+
+    @Test
+    public void getWifiSlice_oneReachableAp_shouldReturnLoadingRow() {
+        setWorkerResults(createAccessPoint(AP1_NAME, false, true));
+
+        final Slice wifiSlice = mWifiSlice.getSlice();
+        final List<SliceItem> sliceItems = wifiSlice.getItems();
+
+        SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME);
+        // Has scanning text
+        SliceTester.assertAnySliceItemContainsSubtitle(sliceItems,
+                mContext.getString(R.string.wifi_empty_list_wifi_on));
+    }
+
+    @Test
+    public void getWifiSlice_allReachableAps_shouldNotReturnLoadingRow() {
+        setWorkerResults(
+                createAccessPoint(AP1_NAME, false, true),
+                createAccessPoint(AP2_NAME, false, true),
+                createAccessPoint(AP3_NAME, false, true));
 
         final Slice wifiSlice = mWifiSlice.getSlice();
         final List<SliceItem> sliceItems = wifiSlice.getItems();
@@ -211,6 +237,29 @@
     }
 
     @Test
+    public void getWifiSlice_isCaptivePortal_shouldHaveCaptivePortalItems() {
+        setWorkerResults(createAccessPoint(AP1_NAME, true, true));
+        doReturn(makeCaptivePortalNetworkCapabilities()).when(mConnectivityManager)
+                .getNetworkCapabilities(any());
+        final IconCompat expectedIcon = IconCompat.createWithResource(mContext,
+                R.drawable.ic_settings_accent);
+
+        final Slice wifiSlice = mWifiSlice.getSlice();
+        final List<SliceItem> sliceItems = wifiSlice.getItems();
+
+        SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME);
+        SliceTester.assertAnySliceItemContainsIcon(sliceItems, expectedIcon);
+    }
+
+    static NetworkCapabilities makeCaptivePortalNetworkCapabilities() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        nc.clearAll();
+        nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+        nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+        return nc;
+    }
+
+    @Test
     public void handleUriChange_updatesWifi() {
         final Intent intent = mWifiSlice.getIntent();
         intent.putExtra(android.app.slice.Slice.EXTRA_TOGGLE_STATE, true);