diff --git a/src/com/android/settings/slices/SliceBackgroundWorker.java b/src/com/android/settings/slices/SliceBackgroundWorker.java index f19b1df79d3..6bafc00f9fc 100644 --- a/src/com/android/settings/slices/SliceBackgroundWorker.java +++ b/src/com/android/settings/slices/SliceBackgroundWorker.java @@ -68,16 +68,17 @@ public abstract class SliceBackgroundWorker implements Closeable { } /** - * Returns the singleton instance of the {@link SliceBackgroundWorker} for specified {@link Uri} - * if exists + * Returns the singleton instance of {@link SliceBackgroundWorker} for specified {@link Uri} if + * exists */ @Nullable - public static SliceBackgroundWorker getInstance(Uri uri) { - return LIVE_WORKERS.get(uri); + @SuppressWarnings("TypeParameterUnusedInFormals") + public static T getInstance(Uri uri) { + return (T) LIVE_WORKERS.get(uri); } /** - * Returns the singleton instance of the {@link SliceBackgroundWorker} for specified {@link + * Returns the singleton instance of {@link SliceBackgroundWorker} for specified {@link * CustomSliceable} */ static SliceBackgroundWorker getInstance(Context context, Sliceable sliceable, Uri uri) { @@ -165,4 +166,4 @@ public abstract class SliceBackgroundWorker implements Closeable { protected final void notifySliceChange() { mContext.getContentResolver().notifyChange(mUri, null); } -} \ No newline at end of file +} diff --git a/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java b/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java index 6cf55d27b7e..ee158202095 100644 --- a/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java +++ b/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java @@ -17,6 +17,8 @@ package com.android.settings.wifi.slice; import android.app.Activity; +import android.net.ConnectivityManager; +import android.net.Network; import android.net.wifi.WifiManager; import android.os.Bundle; @@ -36,10 +38,15 @@ public class ConnectToWifiHandler extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + final Network network = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK); final Bundle accessPointState = getIntent().getBundleExtra( WifiDialogActivity.KEY_ACCESS_POINT_STATE); - if (accessPointState != null) { + if (network != null) { + final ConnectivityManager cm = getSystemService(ConnectivityManager.class); + // start captive portal app to sign in to network + cm.startCaptivePortalApp(network); + } else if (accessPointState != null) { connect(new AccessPoint(this, accessPointState)); } finish(); diff --git a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java index 4a799d15f6a..7dbba4188ab 100644 --- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java +++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java @@ -57,7 +57,7 @@ public class ContextualWifiSlice extends WifiSlice { sActiveUiSession = currentUiSession; sPreviouslyDisplayed = false; } - if (!sPreviouslyDisplayed && !TextUtils.equals(getActiveSSID(), WifiSsid.NONE)) { + if (!sPreviouslyDisplayed && hasWorkingNetwork()) { Log.d(TAG, "Wifi is connected, no point showing any suggestion."); return null; } @@ -67,4 +67,8 @@ public class ContextualWifiSlice extends WifiSlice { return super.getSlice(); } + + private boolean hasWorkingNetwork() { + return !TextUtils.equals(getActiveSSID(), WifiSsid.NONE) && !isCaptivePortal(); + } } diff --git a/src/com/android/settings/wifi/slice/WifiScanWorker.java b/src/com/android/settings/wifi/slice/WifiScanWorker.java index cf45d082f35..b8462287568 100644 --- a/src/com/android/settings/wifi/slice/WifiScanWorker.java +++ b/src/com/android/settings/wifi/slice/WifiScanWorker.java @@ -16,14 +16,27 @@ package com.android.settings.wifi.slice; +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.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; +import android.net.NetworkRequest; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import androidx.annotation.VisibleForTesting; + +import com.android.internal.util.Preconditions; import com.android.settings.slices.SliceBackgroundWorker; +import com.android.settings.wifi.WifiUtils; import com.android.settingslib.wifi.AccessPoint; import com.android.settingslib.wifi.WifiTracker; @@ -33,16 +46,23 @@ import java.util.List; /** * {@link SliceBackgroundWorker} for Wi-Fi, used by WifiSlice. */ -public class WifiScanWorker extends SliceBackgroundWorker - implements WifiTracker.WifiListener { +public class WifiScanWorker extends SliceBackgroundWorker implements + WifiTracker.WifiListener { + + private static final String TAG = "WifiScanWorker"; + + @VisibleForTesting + CaptivePortalNetworkCallback mCaptivePortalNetworkCallback; private final Context mContext; private WifiTracker mWifiTracker; + private ConnectivityManager mConnectivityManager; public WifiScanWorker(Context context, Uri uri) { super(context, uri); mContext = context; + mConnectivityManager = context.getSystemService(ConnectivityManager.class); } @Override @@ -58,6 +78,7 @@ public class WifiScanWorker extends SliceBackgroundWorker @Override protected void onSliceUnpinned() { mWifiTracker.onStop(); + unregisterCaptivePortalNetworkCallback(); } @Override @@ -124,4 +145,71 @@ public class WifiScanWorker extends SliceBackgroundWorker } return null; } -} \ No newline at end of file + + public void registerCaptivePortalNetworkCallback(Network wifiNetwork) { + if (wifiNetwork == null) { + return; + } + + if (mCaptivePortalNetworkCallback != null + && mCaptivePortalNetworkCallback.isSameNetwork(wifiNetwork)) { + return; + } + + unregisterCaptivePortalNetworkCallback(); + + mCaptivePortalNetworkCallback = new CaptivePortalNetworkCallback(wifiNetwork); + mConnectivityManager.registerNetworkCallback( + new NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(TRANSPORT_WIFI) + .build(), + mCaptivePortalNetworkCallback, + new Handler(Looper.getMainLooper())); + } + + public void unregisterCaptivePortalNetworkCallback() { + if (mCaptivePortalNetworkCallback != null) { + try { + mConnectivityManager.unregisterNetworkCallback(mCaptivePortalNetworkCallback); + } catch (RuntimeException e) { + Log.e(TAG, "Unregistering CaptivePortalNetworkCallback failed.", e); + } + mCaptivePortalNetworkCallback = null; + } + } + + class CaptivePortalNetworkCallback extends NetworkCallback { + + private final Network mNetwork; + private boolean mIsCaptivePortal; + + CaptivePortalNetworkCallback(Network network) { + mNetwork = Preconditions.checkNotNull(network); + } + + @Override + public void onCapabilitiesChanged(Network network, + NetworkCapabilities networkCapabilities) { + if (!mNetwork.equals(network)) { + return; + } + + final boolean isCaptivePortal = WifiUtils.canSignIntoNetwork(networkCapabilities); + if (mIsCaptivePortal == isCaptivePortal) { + return; + } + + mIsCaptivePortal = isCaptivePortal; + notifySliceChange(); + } + + /** + * 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 d3df5fccb53..3fe29501f7e 100644 --- a/src/com/android/settings/wifi/slice/WifiSlice.java +++ b/src/com/android/settings/wifi/slice/WifiSlice.java @@ -31,6 +31,8 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; 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.NetworkInfo.State; import android.net.NetworkInfo.DetailedState; @@ -78,10 +80,12 @@ public class WifiSlice implements CustomSliceable { 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 @@ -100,13 +104,16 @@ public class WifiSlice implements CustomSliceable { return listBuilder.build(); } - final SliceBackgroundWorker worker = SliceBackgroundWorker.getInstance(getUri()); + final WifiScanWorker worker = SliceBackgroundWorker.getInstance(getUri()); final List results = worker != null ? worker.getResults() : null; final int apCount = results == null ? 0 : results.size(); + final boolean isFirstApActive = apCount > 0 && results.get(0).isActive(); + handleCaptivePortalCallback(worker, isFirstApActive); // Need a loading text when results are not ready or out of date. boolean needLoadingRow = true; - int index = apCount > 0 && results.get(0).isActive() ? 1 : 0; + // Skip checking the existence of the first access point if it's active + int index = isFirstApActive ? 1 : 0; // This loop checks the existence of reachable APs to determine the validity of the current // AP list. for (; index < apCount; index++) { @@ -159,19 +166,35 @@ public class WifiSlice implements CustomSliceable { .setPrimaryAction(primarySliceAction)); } + private void handleCaptivePortalCallback(WifiScanWorker worker, boolean isFirstApActive) { + if (worker == null) { + return; + } + if (isFirstApActive) { + worker.registerCaptivePortalNetworkCallback(mWifiManager.getCurrentNetwork()); + } else { + worker.unregisterCaptivePortalNetworkCallback(); + } + } + private ListBuilder.RowBuilder getAccessPointRow(AccessPoint accessPoint) { final CharSequence title = getAccessPointName(accessPoint); final IconCompat levelIcon = getAccessPointLevelIcon(accessPoint); + final boolean isCaptivePortal = accessPoint.isActive() && isCaptivePortal(); final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder() .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE) .setSubtitle(title) - .setPrimaryAction(SliceAction.create( - getAccessPointAction(accessPoint), levelIcon, ListBuilder.ICON_IMAGE, - title)); + .setPrimaryAction(SliceAction.createDeeplink( + getAccessPointAction(accessPoint, isCaptivePortal), levelIcon, + ListBuilder.ICON_IMAGE, title)); - final IconCompat endIcon = getEndIcon(accessPoint); - 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; } @@ -218,12 +241,22 @@ public class WifiSlice implements CustomSliceable { return null; } - private PendingIntent getAccessPointAction(AccessPoint accessPoint) { + 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); + } + + private PendingIntent getAccessPointAction(AccessPoint accessPoint, boolean isCaptivePortal) { final Bundle extras = new Bundle(); accessPoint.saveWifiState(extras); Intent intent; - if (accessPoint.isActive()) { + if (isCaptivePortal) { + intent = new Intent(mContext, ConnectToWifiHandler.class); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK, mWifiManager.getCurrentNetwork()); + } else if (accessPoint.isActive()) { intent = new SubSettingLauncher(mContext) .setTitleRes(R.string.pref_title_network_details) .setDestination(WifiNetworkDetailsFragment.class.getName()) @@ -253,6 +286,12 @@ public class WifiSlice implements CustomSliceable { .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}. @@ -317,6 +356,12 @@ public class WifiSlice implements CustomSliceable { } private CharSequence getSummary(AccessPoint accessPoint) { + if (isCaptivePortal()) { + final int id = mContext.getResources() + .getIdentifier("network_available_sign_in", "string", "android"); + return mContext.getText(id); + } + if (accessPoint == null) { return getSummary(); } diff --git a/tests/robotests/src/com/android/settings/testutils/SliceTester.java b/tests/robotests/src/com/android/settings/testutils/SliceTester.java index fdd447545a9..6fb2c49ba4c 100644 --- a/tests/robotests/src/com/android/settings/testutils/SliceTester.java +++ b/tests/robotests/src/com/android/settings/testutils/SliceTester.java @@ -18,6 +18,7 @@ package com.android.settings.testutils; import static android.app.slice.Slice.HINT_TITLE; import static android.app.slice.Slice.SUBTYPE_COLOR; +import static android.app.slice.SliceItem.FORMAT_IMAGE; import static android.app.slice.SliceItem.FORMAT_INT; import static android.app.slice.SliceItem.FORMAT_TEXT; @@ -284,6 +285,31 @@ public class SliceTester { return hasText; } + /** + * Assert any slice item contains icon. + * + * @param sliceItems All slice items of a Slice. + * @param icon Icon for asserting. + */ + public static void assertAnySliceItemContainsIcon(List sliceItems, IconCompat icon) { + boolean hasIcon = false; + for (SliceItem item : sliceItems) { + List iconItems = SliceQuery.findAll(item, FORMAT_IMAGE, + (String) null /* hints */, null /* non-hints */); + if (iconItems == null) { + continue; + } + + for (SliceItem iconItem : iconItems) { + if (icon.toString().equals(iconItem.getIcon().toString())) { + hasIcon = true; + break; + } + } + } + assertThat(hasIcon).isTrue(); + } + private static void assertKeywords(SliceMetadata metadata, SliceData data) { final List keywords = metadata.getSliceKeywords(); final Set expectedKeywords = Arrays.stream(data.getKeywords().split(",")) diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java index 520d98897fc..55bf2d30d7d 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java @@ -16,6 +16,7 @@ package com.android.settings.wifi.slice; +import static org.mockito.ArgumentMatchers.any; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; @@ -24,6 +25,7 @@ import static org.mockito.Mockito.spy; import android.content.ContentResolver; import android.content.Context; +import android.net.ConnectivityManager; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; @@ -53,6 +55,7 @@ public class ContextualWifiSliceTest { private Context mContext; private ContentResolver mResolver; private WifiManager mWifiManager; + private ConnectivityManager mConnectivityManager; private ContextualWifiSlice mWifiSlice; private FakeFeatureFactory mFeatureFactory; @@ -70,6 +73,9 @@ public class ContextualWifiSliceTest { SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); mWifiManager.setWifiEnabled(true); + mConnectivityManager = spy(mContext.getSystemService(ConnectivityManager.class)); + doReturn(mConnectivityManager).when(mContext).getSystemService(ConnectivityManager.class); + mWifiSlice = new ContextualWifiSlice(mContext); mWifiSlice.sPreviouslyDisplayed = false; } @@ -124,6 +130,29 @@ public class ContextualWifiSliceTest { assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString()); } + @Test + public void getWifiSlice_isCaptivePortal_shouldHaveTitleAndToggle() { + mWifiSlice.sPreviouslyDisplayed = false; + final WifiConfiguration config = new WifiConfiguration(); + config.SSID = "123"; + mWifiManager.connect(config, null /* listener */); + doReturn(WifiSliceTest.makeCaptivePortalNetworkCapabilities()).when(mConnectivityManager) + .getNetworkCapabilities(any()); + + final Slice wifiSlice = mWifiSlice.getSlice(); + + final SliceMetadata metadata = SliceMetadata.from(mContext, wifiSlice); + assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings)); + + final List toggles = metadata.getToggles(); + assertThat(toggles).hasSize(1); + + final SliceAction primaryAction = metadata.getPrimaryAction(); + final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext, + R.drawable.ic_settings_wireless); + assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString()); + } + @Test public void getWifiSlice_contextualWifiSlice_shouldReturnContextualWifiSliceUri() { mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken(); 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 7ddbce48a9f..30e289b0550 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java @@ -27,6 +27,8 @@ import static org.mockito.Mockito.verify; import android.content.ContentResolver; import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; import android.net.NetworkInfo; import android.net.NetworkInfo.State; import android.net.wifi.WifiManager; @@ -55,6 +57,7 @@ public class WifiScanWorkerTest { private Context mContext; private ContentResolver mResolver; private WifiManager mWifiManager; + private ConnectivityManager mConnectivityManager; private WifiScanWorker mWifiScanWorker; @Before @@ -68,6 +71,7 @@ public class WifiScanWorkerTest { SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); mWifiManager.setWifiEnabled(true); + mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); mWifiScanWorker = new WifiScanWorker(mContext, WIFI_SLICE_URI); } @@ -89,7 +93,7 @@ public class WifiScanWorkerTest { } @Test - public void SliceAccessPoint_sameState_shouldBeTheSame() { + public void AccessPointList_sameState_shouldBeTheSame() { final AccessPoint ap1 = createAccessPoint(AP_NAME, State.CONNECTED); final AccessPoint ap2 = createAccessPoint(AP_NAME, State.CONNECTED); @@ -98,7 +102,7 @@ public class WifiScanWorkerTest { } @Test - public void SliceAccessPoint_differentState_shouldBeDifferent() { + public void AccessPointList_differentState_shouldBeDifferent() { final AccessPoint ap1 = createAccessPoint(AP_NAME, State.CONNECTING); final AccessPoint ap2 = createAccessPoint(AP_NAME, State.CONNECTED); @@ -107,7 +111,7 @@ public class WifiScanWorkerTest { } @Test - public void SliceAccessPoint_differentLength_shouldBeDifferent() { + public void AccessPointList_differentLength_shouldBeDifferent() { final AccessPoint ap1 = createAccessPoint(AP_NAME, State.CONNECTED); final AccessPoint ap2 = createAccessPoint(AP_NAME, State.CONNECTED); final List list = new ArrayList<>(); @@ -116,4 +120,15 @@ public class WifiScanWorkerTest { assertThat(mWifiScanWorker.areListsTheSame(list, Arrays.asList(ap1))).isFalse(); } + + @Test + public void NetworkCallback_onCapabilitiesChanged_shouldNotifyChange() { + final Network network = mConnectivityManager.getActiveNetwork(); + mWifiScanWorker.registerCaptivePortalNetworkCallback(network); + + mWifiScanWorker.mCaptivePortalNetworkCallback.onCapabilitiesChanged(network, + WifiSliceTest.makeCaptivePortalNetworkCapabilities()); + + verify(mResolver).notifyChange(WIFI_SLICE_URI, null); + } } 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 b2718fc0631..b8c7a8c5d32 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java @@ -23,6 +23,7 @@ import static com.android.settings.wifi.slice.WifiSlice.DEFAULT_EXPANDED_ROW_COU 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; @@ -30,6 +31,8 @@ import static org.mockito.Mockito.spy; 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; @@ -70,6 +73,7 @@ public class WifiSliceTest { private Context mContext; private ContentResolver mResolver; private WifiManager mWifiManager; + private ConnectivityManager mConnectivityManager; private WifiSlice mWifiSlice; @Before @@ -83,6 +87,9 @@ public class WifiSliceTest { 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); } @@ -226,6 +233,37 @@ public class WifiSliceTest { mContext.getString(R.string.wifi_empty_list_wifi_on)); } + @Test + public void getWifiSlice_isCaptivePortal_shouldHaveCaptivePortalItems() { + setWorkerResults(createAccessPoint(AP1_NAME, true, true)); + doReturn(makeCaptivePortalNetworkCapabilities()).when(mConnectivityManager) + .getNetworkCapabilities(any()); + + final Slice wifiSlice = mWifiSlice.getSlice(); + final List sliceItems = wifiSlice.getItems(); + + SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); + assertCaptivePortalItems(sliceItems); + } + + private void assertCaptivePortalItems(List sliceItems) { + final String expectedSummary = mContext.getString(mContext.getResources() + .getIdentifier("network_available_sign_in", "string", "android")); + SliceTester.assertAnySliceItemContainsSubtitle(sliceItems, expectedSummary); + + final IconCompat expectedIcon = IconCompat.createWithResource(mContext, + R.drawable.ic_settings_accent); + 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();