Fix automatically directing the user to the captive portal in Wi-Fi Slice

The feature failed after the CL "Force the adapter to rebind cards with
a toggle".

Because toggle slices have been forced to rebind after starting another
activity and when any slice is updating. This unpins Wi-Fi slice and
stops WifiScanWorker and then clears the saved clicked network.

Solution:
1. Change ConnectToWifiHandler from activity to receiver and send
   broadcasts to it with FLAG_RECEIVER_FOREGROUND, so Wi-Fi slice won't
   be forced to rebind.
2. Seperate Wi-Fi scan worker and contextual Wi-Fi scan worker. Keep the
   original logic for the generic one, and then add the logic below to
   the contextual one.
3. Do not clear the saved clicked network when slice is unppined because
   it happens frequently in contextual homepage.
4. Introduce a static long in ContextualWifiScanWorker that updates once
   in every visible UI session. A session is when the screen is visible
   to user.
5. Use session token to determine whether auto-starting captive portal
   is needed.

Fixes: 128056349
Test: robotest, visual in homepage and network panel
Change-Id: I9e03c379806e124fa7253b2a635574b2433f6afc
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8e62c45..3b53d32 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2705,10 +2705,8 @@
             </intent-filter>
         </activity>
 
-        <activity
+        <receiver
             android:name=".wifi.slice.ConnectToWifiHandler"
-            android:theme="@android:style/Theme.NoDisplay"
-            android:excludeFromRecents="true"
             android:exported="false" />
 
         <activity
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
index 0704ed8..92892b3 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
@@ -33,12 +33,11 @@
 import com.android.settings.core.InstrumentedFragment;
 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 {
 
-    private static final String TAG = "ContextualCardsFragment";
-
     private FocusRecyclerView mCardsContainer;
     private GridLayoutManager mLayoutManager;
     private ContextualCardsAdapter mContextualCardsAdapter;
@@ -60,6 +59,7 @@
     @Override
     public void onStart() {
         super.onStart();
+        ContextualWifiScanWorker.newVisibleUiSession();
         mContextualCardManager.loadContextualCards(LoaderManager.getInstance(this));
     }
 
diff --git a/src/com/android/settings/slices/SlicesFeatureProvider.java b/src/com/android/settings/slices/SlicesFeatureProvider.java
index ae94f29..b649eb2 100644
--- a/src/com/android/settings/slices/SlicesFeatureProvider.java
+++ b/src/com/android/settings/slices/SlicesFeatureProvider.java
@@ -18,9 +18,9 @@
     /**
      * Starts a new UI session for the purpose of using Slices.
      *
-     * A UI session is defined as an duration of time when user stays in a UI screen. Screen
-     * rotation does not break the continuation of session, going to a sub-page and coming out does
-     * not break the continuation either. Leaving the page and coming back breaks it.
+     * A UI session is defined as a duration of time when user stays in a UI screen. Screen rotation
+     * does not break the continuation of session, going to a sub-page and coming out does not break
+     * the continuation either. Leaving the page and coming back breaks it.
      */
     void newUiSession();
 
diff --git a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
index 297f2c1..87d7401 100644
--- a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
+++ b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
@@ -24,8 +24,6 @@
 import com.android.settings.wifi.calling.WifiCallingSliceHelper;
 import com.android.settingslib.utils.ThreadUtils;
 
-import java.util.Random;
-
 /**
  * Manages Slices in Settings.
  */
diff --git a/src/com/android/settings/wifi/details/AddDevicePreferenceController.java b/src/com/android/settings/wifi/details/AddDevicePreferenceController.java
index eb7e226..f2b3d75 100644
--- a/src/com/android/settings/wifi/details/AddDevicePreferenceController.java
+++ b/src/com/android/settings/wifi/details/AddDevicePreferenceController.java
@@ -29,7 +29,7 @@
 import com.android.settingslib.wifi.AccessPoint;
 
 /**
- * {@link AbstractPreferenceController} that launches Wi-Fi Easy Connect configurator flow
+ * {@link BasePreferenceController} that launches Wi-Fi Easy Connect configurator flow
  */
 public class AddDevicePreferenceController extends BasePreferenceController {
 
diff --git a/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java b/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java
index f1b0b6f..5c92d81 100644
--- a/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java
+++ b/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java
@@ -16,7 +16,9 @@
 
 package com.android.settings.wifi.slice;
 
-import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.wifi.WifiManager;
@@ -30,35 +32,36 @@
 import com.android.settingslib.wifi.AccessPoint;
 
 /**
- * This activity helps connect to the Wi-Fi network which is open or saved
+ * This receiver helps connect to Wi-Fi network
  */
-public class ConnectToWifiHandler extends Activity {
+public class ConnectToWifiHandler extends BroadcastReceiver {
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+    public void onReceive(Context context, Intent intent) {
+        if (context == null || intent == null) {
+            return;
+        }
 
-        final Network network = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
-        final Bundle accessPointState = getIntent().getBundleExtra(
+        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 = getSystemService(ConnectivityManager.class);
+            final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
             // start captive portal app to sign in to network
             cm.startCaptivePortalApp(network);
         } else if (accessPointState != null) {
-            connect(new AccessPoint(this, accessPointState));
+            connect(context, new AccessPoint(context, accessPointState));
         }
-
-        finish();
     }
 
     @VisibleForTesting
-    void connect(AccessPoint accessPoint) {
+    void connect(Context context, AccessPoint accessPoint) {
+        ContextualWifiScanWorker.saveSession();
         WifiScanWorker.saveClickedWifi(accessPoint);
 
-        final WifiConnectListener connectListener = new WifiConnectListener(this);
+        final WifiConnectListener connectListener = new WifiConnectListener(context);
         switch (WifiUtils.getConnectingType(accessPoint)) {
             case WifiUtils.CONNECT_TYPE_OSU_PROVISION:
                 accessPoint.startOsuProvisioning(connectListener);
@@ -68,7 +71,7 @@
                 accessPoint.generateOpenNetworkConfig();
 
             case WifiUtils.CONNECT_TYPE_SAVED_NETWORK:
-                final WifiManager wifiManager = getSystemService(WifiManager.class);
+                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
new file mode 100644
index 0000000..5e69b8a
--- /dev/null
+++ b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.content.Context;
+import android.net.Uri;
+import android.os.SystemClock;
+
+import com.android.settings.slices.SliceBackgroundWorker;
+
+/**
+ * {@link SliceBackgroundWorker} for Wi-Fi, used by {@link ContextualWifiSlice}.
+ */
+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;
+    }
+}
\ 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 fefbf10..97b9241 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
@@ -90,4 +90,9 @@
                 && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)
                 && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
     }
+
+    @Override
+    public Class getBackgroundWorkerClass() {
+        return ContextualWifiScanWorker.class;
+    }
 }
diff --git a/src/com/android/settings/wifi/slice/WifiScanWorker.java b/src/com/android/settings/wifi/slice/WifiScanWorker.java
index 8697f00..a5bd74d 100644
--- a/src/com/android/settings/wifi/slice/WifiScanWorker.java
+++ b/src/com/android/settings/wifi/slice/WifiScanWorker.java
@@ -50,7 +50,7 @@
 import java.util.List;
 
 /**
- * {@link SliceBackgroundWorker} for Wi-Fi, used by WifiSlice.
+ * {@link SliceBackgroundWorker} for Wi-Fi, used by {@link WifiSlice}.
  */
 public class WifiScanWorker extends SliceBackgroundWorker<AccessPoint> implements
         WifiTracker.WifiListener {
@@ -84,7 +84,7 @@
     protected void onSliceUnpinned() {
         mWifiTracker.onStop();
         unregisterNetworkCallback();
-        clearClickedWifi();
+        clearClickedWifiOnSliceUnpinned();
     }
 
     @Override
@@ -157,6 +157,14 @@
         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;
@@ -224,12 +232,13 @@
 
             // Automatically start captive portal
             if (!prevIsCaptivePortal && mIsCaptivePortal
-                    && isWifiClicked(mWifiTracker.getManager().getConnectionInfo())) {
+                    && isWifiClicked(mWifiTracker.getManager().getConnectionInfo())
+                    && isSessionValid()) {
                 final Intent intent = new Intent(mContext, ConnectToWifiHandler.class)
                         .putExtra(ConnectivityManager.EXTRA_NETWORK, network)
-                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                // Starting activity in the system process needs to specify a user
-                mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                // Sending a broadcast in the system process needs to specify a user
+                mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
             }
         }
 
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index a687b93..9be9898 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -94,6 +94,7 @@
         final boolean isWifiEnabled = isWifiEnabled();
         ListBuilder listBuilder = getHeaderRow(isWifiEnabled);
         if (!isWifiEnabled) {
+            WifiScanWorker.clearClickedWifi();
             return listBuilder.build();
         }
 
@@ -133,6 +134,17 @@
         return listBuilder.build();
     }
 
+    private void handleNetworkCallback(WifiScanWorker worker, boolean isFirstApActive) {
+        if (worker == null) {
+            return;
+        }
+        if (isFirstApActive) {
+            worker.registerNetworkCallback(mWifiManager.getCurrentNetwork());
+        } else {
+            worker.unregisterNetworkCallback();
+        }
+    }
+
     private ListBuilder getHeaderRow(boolean isWifiEnabled) {
         final IconCompat icon = IconCompat.createWithResource(mContext,
                 R.drawable.ic_settings_wireless);
@@ -155,17 +167,6 @@
                         .setPrimaryAction(primarySliceAction));
     }
 
-    private void handleNetworkCallback(WifiScanWorker worker, boolean isFirstApActive) {
-        if (worker == null) {
-            return;
-        }
-        if (isFirstApActive) {
-            worker.registerNetworkCallback(mWifiManager.getCurrentNetwork());
-        } else {
-            worker.unregisterNetworkCallback();
-        }
-    }
-
     private ListBuilder.RowBuilder getAccessPointRow(AccessPoint accessPoint) {
         final boolean isCaptivePortal = accessPoint.isActive() && isCaptivePortal();
         final CharSequence title = accessPoint.getTitle();
@@ -175,9 +176,8 @@
                 .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE)
                 .setTitle(title)
                 .setSubtitle(summary)
-                .setPrimaryAction(SliceAction.createDeeplink(
-                        getAccessPointAction(accessPoint, isCaptivePortal), levelIcon,
-                        ListBuilder.ICON_IMAGE, title));
+                .setPrimaryAction(getAccessPointAction(accessPoint, isCaptivePortal, levelIcon,
+                        title));
 
         if (isCaptivePortal) {
             rowBuilder.addEndItem(getCaptivePortalEndAction(accessPoint, title));
@@ -203,7 +203,7 @@
         final Drawable d = mContext.getDrawable(
                 com.android.settingslib.Utils.getWifiIconResource(accessPoint.getLevel()));
 
-        @ColorInt int color;
+        final @ColorInt int color;
         if (accessPoint.isActive()) {
             final NetworkInfo.State state = accessPoint.getNetworkInfo().getState();
             if (state == NetworkInfo.State.CONNECTED) {
@@ -232,36 +232,54 @@
     }
 
     private SliceAction getCaptivePortalEndAction(AccessPoint accessPoint, CharSequence title) {
-        return SliceAction.createDeeplink(
-                getAccessPointAction(accessPoint, false /* isCaptivePortal */),
-                IconCompat.createWithResource(mContext, R.drawable.ic_settings_accent),
-                ListBuilder.ICON_IMAGE, title);
+        return getAccessPointAction(accessPoint, false /* isCaptivePortal */,
+                IconCompat.createWithResource(mContext, R.drawable.ic_settings_accent), title);
     }
 
-    private PendingIntent getAccessPointAction(AccessPoint accessPoint, boolean isCaptivePortal) {
+    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);
 
-        Intent intent;
-        if (isCaptivePortal) {
-            intent = new Intent(mContext, ConnectToWifiHandler.class);
-            intent.putExtra(ConnectivityManager.EXTRA_NETWORK, mWifiManager.getCurrentNetwork());
-        } else if (accessPoint.isActive()) {
-            intent = new SubSettingLauncher(mContext)
+        if (accessPoint.isActive()) {
+            final Intent intent = new SubSettingLauncher(mContext)
                     .setTitleRes(R.string.pref_title_network_details)
                     .setDestination(WifiNetworkDetailsFragment.class.getName())
                     .setArguments(extras)
                     .setSourceMetricsCategory(SettingsEnums.WIFI)
                     .toIntent();
+            return getActivityAction(requestCode, intent, icon, title);
         } else if (WifiUtils.getConnectingType(accessPoint) != WifiUtils.CONNECT_TYPE_OTHERS) {
-            intent = new Intent(mContext, ConnectToWifiHandler.class);
-            intent.putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras);
+            final Intent intent = new Intent(mContext, ConnectToWifiHandler.class)
+                    .putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras);
+            return getBroadcastAction(requestCode, intent, icon, title);
         } else {
-            intent = new Intent(mContext, WifiDialogActivity.class);
-            intent.putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras);
+            final Intent intent = new Intent(mContext, WifiDialogActivity.class)
+                    .putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras);
+            return getActivityAction(requestCode, intent, icon, title);
         }
-        return PendingIntent.getActivity(mContext, accessPoint.hashCode() /* requestCode */,
-                intent, 0 /* flags */);
+    }
+
+    private SliceAction getActivityAction(int requestCode, Intent intent, IconCompat icon,
+            CharSequence title) {
+        final PendingIntent pi = PendingIntent.getActivity(mContext, requestCode, intent,
+                0 /* flags */);
+        return SliceAction.createDeeplink(pi, icon, ListBuilder.ICON_IMAGE, title);
+    }
+
+    private SliceAction getBroadcastAction(int requestCode, Intent intent, IconCompat icon,
+            CharSequence title) {
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        final PendingIntent pi = PendingIntent.getBroadcast(mContext, requestCode, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        return SliceAction.create(pi, icon, ListBuilder.ICON_IMAGE, title);
     }
 
     private ListBuilder.RowBuilder getLoadingRow(CharSequence placeholder) {
@@ -277,7 +295,7 @@
                 .setSubtitle(title);
     }
 
-    protected boolean isCaptivePortal() {
+    private boolean isCaptivePortal() {
         final NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(
                 mWifiManager.getCurrentNetwork());
         return WifiUtils.canSignIntoNetwork(nc);
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 cea8365..1eb7818 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/ConnectToWifiHandlerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/ConnectToWifiHandlerTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.net.wifi.WifiManager;
@@ -35,8 +36,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
 @RunWith(RobolectricTestRunner.class)
@@ -44,6 +45,7 @@
 public class ConnectToWifiHandlerTest {
 
     private static final String AP_SSID = "\"ap\"";
+    private Context mContext;
     private ConnectToWifiHandler mHandler;
     private WifiConfiguration mWifiConfig;
     @Mock
@@ -53,7 +55,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mHandler = Robolectric.setupActivity(ConnectToWifiHandler.class);
+        mContext = RuntimeEnvironment.application;
+        mHandler = new ConnectToWifiHandler();
         mWifiConfig = new WifiConfiguration();
         mWifiConfig.SSID = AP_SSID;
         doReturn(mWifiConfig).when(mAccessPoint).getConfig();
@@ -64,7 +67,7 @@
         when(mAccessPoint.isSaved()).thenReturn(false);
         when(mAccessPoint.getSecurity()).thenReturn(AccessPoint.SECURITY_NONE);
 
-        mHandler.connect(mAccessPoint);
+        mHandler.connect(mContext, mAccessPoint);
 
         assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP_SSID);
     }
@@ -74,7 +77,7 @@
         when(mAccessPoint.isSaved()).thenReturn(false);
         when(mAccessPoint.isOsuProvider()).thenReturn(true);
 
-        mHandler.connect(mAccessPoint);
+        mHandler.connect(mContext, mAccessPoint);
 
         verify(mAccessPoint).startOsuProvisioning(any(WifiManager.ActionListener.class));
     }
@@ -85,7 +88,7 @@
         when(mAccessPoint.isSaved()).thenReturn(false);
         when(mAccessPoint.isPasspoint()).thenReturn(true);
 
-        mHandler.connect(mAccessPoint);
+        mHandler.connect(mContext, mAccessPoint);
 
         assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP_SSID);
     }
@@ -98,7 +101,7 @@
         status.setHasEverConnected(true);
         mWifiConfig.setNetworkSelectionStatus(status);
 
-        mHandler.connect(mAccessPoint);
+        mHandler.connect(mContext, mAccessPoint);
 
         assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP_SSID);
     }
@@ -108,7 +111,7 @@
         when(mAccessPoint.isSaved()).thenReturn(false);
         when(mAccessPoint.getSecurity()).thenReturn(AccessPoint.SECURITY_PSK);
 
-        mHandler.connect(mAccessPoint);
+        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 b246e9a..1c84eb6 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
@@ -17,6 +17,7 @@
 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;
 
@@ -42,9 +43,6 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 
-import androidx.slice.SliceProvider;
-import androidx.slice.widget.SliceLiveData;
-
 import com.android.settings.testutils.shadow.ShadowWifiManager;
 import com.android.settingslib.wifi.AccessPoint;
 import com.android.settingslib.wifi.WifiTracker;
@@ -57,7 +55,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
@@ -85,14 +82,11 @@
         mResolver = mock(ContentResolver.class);
         doReturn(mResolver).when(mContext).getContentResolver();
         mWifiManager = mContext.getSystemService(WifiManager.class);
-
-        // Set-up specs for SliceMetadata.
-        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
         mWifiManager.setWifiEnabled(true);
 
         mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
         mWifiScanWorker = new WifiScanWorker(mContext, WIFI_SLICE_URI);
-        mConnectToWifiHandler = Robolectric.setupActivity(ConnectToWifiHandler.class);
+        mConnectToWifiHandler = new ConnectToWifiHandler();
     }
 
     @After
@@ -147,42 +141,36 @@
         verify(mResolver).notifyChange(WIFI_SLICE_URI, null);
     }
 
-    private void setConnectionInfoSSID(String ssid) {
-        final WifiInfo wifiInfo = new WifiInfo();
-        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(ssid));
-        ShadowWifiManager.get().setConnectionInfo(wifiInfo);
-    }
-
     @Test
-    public void NetworkCallback_onCapabilitiesChanged_isClickedWifi_shouldStartActivity() {
-        final AccessPoint accessPoint = createAccessPoint("ap1");
+    public void NetworkCallback_onCapabilitiesChanged_isClickedWifi_shouldSendBroadcast() {
+        final Intent intent = getIntentWithAccessPoint("ap1");
         setConnectionInfoSSID("ap1");
         final Network network = mConnectivityManager.getActiveNetwork();
         mWifiScanWorker.registerNetworkCallback(network);
 
-        mConnectToWifiHandler.connect(accessPoint);
+        mConnectToWifiHandler.onReceive(mContext, intent);
         mWifiScanWorker.mNetworkCallback.onCapabilitiesChanged(network,
                 WifiSliceTest.makeCaptivePortalNetworkCapabilities());
 
-        verify(mContext).startActivityAsUser(any(Intent.class), eq(UserHandle.CURRENT));
+        verify(mContext).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.CURRENT));
     }
 
     @Test
-    public void NetworkCallback_onCapabilitiesChanged_isNotClickedWifi_shouldNotStartActivity() {
-        final AccessPoint accessPoint = createAccessPoint("ap1");
+    public void NetworkCallback_onCapabilitiesChanged_isNotClickedWifi_shouldNotSendBroadcast() {
+        final Intent intent = getIntentWithAccessPoint("ap1");
         setConnectionInfoSSID("ap2");
         final Network network = mConnectivityManager.getActiveNetwork();
         mWifiScanWorker.registerNetworkCallback(network);
 
-        mConnectToWifiHandler.connect(accessPoint);
+        mConnectToWifiHandler.onReceive(mContext, intent);
         mWifiScanWorker.mNetworkCallback.onCapabilitiesChanged(network,
                 WifiSliceTest.makeCaptivePortalNetworkCapabilities());
 
-        verify(mContext, never()).startActivityAsUser(any(Intent.class), eq(UserHandle.CURRENT));
+        verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.CURRENT));
     }
 
     @Test
-    public void NetworkCallback_onCapabilitiesChanged_neverClickWifi_shouldNotStartActivity() {
+    public void NetworkCallback_onCapabilitiesChanged_neverClickWifi_shouldNotSendBroadcast() {
         setConnectionInfoSSID("ap1");
         final Network network = mConnectivityManager.getActiveNetwork();
         mWifiScanWorker.registerNetworkCallback(network);
@@ -190,24 +178,36 @@
         mWifiScanWorker.mNetworkCallback.onCapabilitiesChanged(network,
                 WifiSliceTest.makeCaptivePortalNetworkCapabilities());
 
-        verify(mContext, never()).startActivityAsUser(any(Intent.class), eq(UserHandle.CURRENT));
+        verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.CURRENT));
     }
 
     @Test
-    public void NetworkCallback_onCapabilitiesChanged_sliceIsUnpinned_shouldNotStartActivity() {
-        final AccessPoint accessPoint = createAccessPoint("ap1");
+    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();
-        mConnectToWifiHandler.connect(accessPoint);
+        mConnectToWifiHandler.onReceive(mContext, intent);
         mWifiScanWorker.onSliceUnpinned();
         callback.onCapabilitiesChanged(network,
                 WifiSliceTest.makeCaptivePortalNetworkCapabilities());
 
-        verify(mContext, never()).startActivityAsUser(any(Intent.class), eq(UserHandle.CURRENT));
+        verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.CURRENT));
+    }
+
+    static Intent getIntentWithAccessPoint(String ssid) {
+        final Bundle savedState = new Bundle();
+        savedState.putString("key_ssid", ssid);
+        return new Intent().putExtra(KEY_ACCESS_POINT_STATE, savedState);
+    }
+
+    static void setConnectionInfoSSID(String ssid) {
+        final WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(ssid));
+        ShadowWifiManager.get().setConnectionInfo(wifiInfo);
     }
 
     private AccessPoint createAccessPoint(String ssid, DetailedState detailedState) {
@@ -223,10 +223,6 @@
         return createAccessPoint("ap", detailedState);
     }
 
-    private AccessPoint createAccessPoint(String ssid) {
-        return createAccessPoint(ssid, DetailedState.DISCONNECTED);
-    }
-
     @Implements(WifiTracker.class)
     public static class ShadowWifiTracker {
         @Implementation