diff options
author | Quang Luong <qal@google.com> | 2020-02-25 17:52:33 -0800 |
---|---|---|
committer | Quang Luong <qal@google.com> | 2020-02-26 17:30:18 -0800 |
commit | 54a31806e0e074d061111c8457b1fff6e30f00b2 (patch) | |
tree | 341d28956822b2e616e069a790cbf301423aec36 | |
parent | b66edf6a39b0038c885b98134679d491ddfffd84 (diff) |
[WifiTrackerLib] Implement manual and auto captive portal opening
Allow the user to sign in to captive portals through the wifi picker.
The captive portal app will also auto-open on the wifi picker page and
the Network Details page following a manual user connection to the
network.
Bug: 70983952
Bug: 149354973
Test: manual verification with local captive portal AP, atest
WifiTrackerLibTests
Change-Id: I9311bf38b861715c74b6415204ef63eab70ab717
6 files changed, 132 insertions, 21 deletions
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/BaseWifiTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/BaseWifiTracker.java index 2fb162fc2..2c749ad2e 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/BaseWifiTracker.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/BaseWifiTracker.java @@ -25,6 +25,7 @@ import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.NetworkScoreManager; import android.net.wifi.WifiManager; @@ -124,7 +125,8 @@ public class BaseWifiTracker implements LifecycleObserver { protected final long mScanIntervalMillis; protected final ScanResultUpdater mScanResultUpdater; - // Network request for listening on changes to Wifi link properties. + // Network request for listening on changes to Wifi link properties and network capabilities + // such as captive portal availability. private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder() .clearCapabilities().addTransportType(TRANSPORT_WIFI).build(); @@ -134,6 +136,12 @@ public class BaseWifiTracker implements LifecycleObserver { public void onLinkPropertiesChanged(Network network, LinkProperties lp) { handleLinkPropertiesChanged(lp); } + + @Override + public void onCapabilitiesChanged(Network network, + NetworkCapabilities networkCapabilities) { + handleNetworkCapabilitiesChanged(networkCapabilities); + } }; /** @@ -236,7 +244,7 @@ public class BaseWifiTracker implements LifecycleObserver { @WorkerThread protected void handleOnStart() { // Do nothing. - }; + } /** * Handle receiving the WifiManager.WIFI_STATE_CHANGED_ACTION broadcast @@ -244,7 +252,7 @@ public class BaseWifiTracker implements LifecycleObserver { @WorkerThread protected void handleWifiStateChangedAction() { // Do nothing. - }; + } /** * Handle receiving the WifiManager.SCAN_RESULTS_AVAILABLE_ACTION broadcast @@ -252,7 +260,7 @@ public class BaseWifiTracker implements LifecycleObserver { @WorkerThread protected void handleScanResultsAvailableAction(@NonNull Intent intent) { // Do nothing. - }; + } /** * Handle receiving the WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION broadcast @@ -260,7 +268,7 @@ public class BaseWifiTracker implements LifecycleObserver { @WorkerThread protected void handleConfiguredNetworksChangedAction(@NonNull Intent intent) { // Do nothing. - }; + } /** * Handle receiving the WifiManager.NETWORK_STATE_CHANGED_ACTION broadcast @@ -268,12 +276,23 @@ public class BaseWifiTracker implements LifecycleObserver { @WorkerThread protected void handleNetworkStateChangedAction(@NonNull Intent intent) { // Do nothing. - }; + } + /** + * Handle link property changes for the current connected Wifi network. + */ @WorkerThread protected void handleLinkPropertiesChanged(@Nullable LinkProperties linkProperties) { // Do nothing. - }; + } + + /** + * Handle network capability changes for the current connected Wifi network. + */ + @WorkerThread + protected void handleNetworkCapabilitiesChanged(@Nullable NetworkCapabilities capabilities) { + // Do nothing. + } /** * Scanner to handle starting scans every SCAN_INTERVAL_MILLIS diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java index 5a95eb9b2..d30fcc407 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkScoreManager; import android.net.wifi.WifiConfiguration; @@ -38,6 +39,7 @@ import android.text.TextUtils; import androidx.annotation.AnyThread; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import androidx.lifecycle.Lifecycle; @@ -130,12 +132,20 @@ class StandardNetworkDetailsTracker extends NetworkDetailsTracker { @WorkerThread @Override - protected void handleLinkPropertiesChanged(@NonNull LinkProperties linkProperties) { + protected void handleLinkPropertiesChanged(@Nullable LinkProperties linkProperties) { if (mChosenEntry.getConnectedState() == CONNECTED_STATE_CONNECTED) { mChosenEntry.updateLinkProperties(linkProperties); } } + @WorkerThread + @Override + protected void handleNetworkCapabilitiesChanged(@Nullable NetworkCapabilities capabilities) { + if (mChosenEntry.getConnectedState() == CONNECTED_STATE_CONNECTED) { + mChosenEntry.updateNetworkCapabilities(capabilities); + } + } + /** * Updates the tracked entry's scan results up to the max scan age (or more, if the last scan * was unsuccessful). If Wifi is disabled, the tracked entry's level will be cleared. diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java index 299c0f589..eab826ed3 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java @@ -109,6 +109,8 @@ public class StandardWifiEntry extends WifiEntry { @Nullable private WifiConfiguration mWifiConfig; @Nullable private String mRecommendationServiceLabel; + private boolean mShouldAutoOpenCaptivePortal = false; + StandardWifiEntry(@NonNull Context context, @NonNull Handler callbackHandler, @NonNull String key, @NonNull List<ScanResult> scanResults, @@ -245,22 +247,21 @@ public class StandardWifiEntry extends WifiEntry { } // Check NetworkCapabilities. - final ConnectivityManager cm = - (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkCapabilities nc = - cm.getNetworkCapabilities(mWifiManager.getCurrentNetwork()); - if (nc != null) { - if (nc.hasCapability(nc.NET_CAPABILITY_CAPTIVE_PORTAL)) { + if (mNetworkCapabilities != null) { + if (mNetworkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)) { return mContext.getString(mContext.getResources() .getIdentifier("network_available_sign_in", "string", "android")); } - if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) { + if (mNetworkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) { return mContext.getString(R.string.wifi_limited_connection); } - if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { - if (nc.isPrivateDnsBroken()) { + if (!mNetworkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { + if (mNetworkCapabilities.isPrivateDnsBroken()) { return mContext.getString(R.string.private_dns_broken); } return mContext.getString(R.string.wifi_connected_no_internet); @@ -383,6 +384,9 @@ public class StandardWifiEntry extends WifiEntry { @Override public void connect(@Nullable ConnectCallback callback) { mConnectCallback = callback; + // We should flag this network to auto-open captive portal since this method represents + // the user manually connecting to a network (i.e. not auto-join). + mShouldAutoOpenCaptivePortal = true; if (mWifiConfig == null) { // Unsaved network if (mSecurity == SECURITY_NONE @@ -448,13 +452,19 @@ public class StandardWifiEntry extends WifiEntry { @Override public boolean canSignIn() { - // TODO(b/70983952): Fill this method in - return false; + return mNetworkCapabilities != null + && mNetworkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); } @Override public void signIn(@Nullable SignInCallback callback) { - // TODO(b/70983952): Fill this method in + if (canSignIn()) { + // canSignIn() implies that this WifiEntry is the currently connected network, so use + // getCurrentNetwork() to start the captive portal app. + ((ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE)) + .startCaptivePortalApp(mWifiManager.getCurrentNetwork()); + } } /** @@ -687,6 +697,18 @@ public class StandardWifiEntry extends WifiEntry { notifyOnUpdated(); } + @WorkerThread + @Override + void updateNetworkCapabilities(@Nullable NetworkCapabilities capabilities) { + super.updateNetworkCapabilities(capabilities); + + // Auto-open an available captive portal if the user manually connected to this network. + if (canSignIn() && mShouldAutoOpenCaptivePortal) { + mShouldAutoOpenCaptivePortal = false; + signIn(null /* callback */); + } + } + private void updateEapType(ScanResult result) { if (result.capabilities.contains("RSN-EAP")) { // WPA2-Enterprise and WPA3-Enterprise (non 192-bit) advertise RSN-EAP-CCMP diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java index 4f186ef26..34bac2ab5 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java @@ -22,6 +22,7 @@ import static androidx.core.util.Preconditions.checkNotNull; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkUtils; import android.net.RouteInfo; @@ -190,6 +191,7 @@ public abstract class WifiEntry implements Comparable<WifiEntry> { protected int mLevel = WIFI_LEVEL_UNREACHABLE; protected WifiInfo mWifiInfo; protected NetworkInfo mNetworkInfo; + protected NetworkCapabilities mNetworkCapabilities; protected ConnectedInfo mConnectedInfo; protected ConnectCallback mConnectCallback; @@ -563,6 +565,7 @@ public abstract class WifiEntry implements Comparable<WifiEntry> { } } else { // Connection info doesn't matched, so this network is disconnected mNetworkInfo = null; + mNetworkCapabilities = null; mConnectedInfo = null; if (mCalledDisconnect) { mCalledDisconnect = false; @@ -621,6 +624,12 @@ public abstract class WifiEntry implements Comparable<WifiEntry> { notifyOnUpdated(); } + // Method for WifiTracker to update a connected WifiEntry's network capabilities. + @WorkerThread + void updateNetworkCapabilities(@Nullable NetworkCapabilities capabilities) { + mNetworkCapabilities = capabilities; + } + String getWifiInfoDescription() { final StringJoiner sj = new StringJoiner(" "); if (getConnectedState() == CONNECTED_STATE_CONNECTED && mWifiInfo != null) { diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java index 21d3f2c27..8b97040a3 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java @@ -34,6 +34,7 @@ import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkScoreManager; import android.net.wifi.ScanResult; @@ -240,13 +241,22 @@ public class WifiPickerTracker extends BaseWifiTracker { @WorkerThread @Override - protected void handleLinkPropertiesChanged(@NonNull LinkProperties linkProperties) { + protected void handleLinkPropertiesChanged(@Nullable LinkProperties linkProperties) { if (mConnectedWifiEntry != null && mConnectedWifiEntry.getConnectedState() == CONNECTED_STATE_CONNECTED) { mConnectedWifiEntry.updateLinkProperties(linkProperties); } } + @WorkerThread + @Override + protected void handleNetworkCapabilitiesChanged(@Nullable NetworkCapabilities capabilities) { + if (mConnectedWifiEntry != null + && mConnectedWifiEntry.getConnectedState() == CONNECTED_STATE_CONNECTED) { + mConnectedWifiEntry.updateNetworkCapabilities(capabilities); + } + } + /** * Update the list returned by getWifiEntries() with the current states of the entry caches. */ diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/StandardWifiEntryTest.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/StandardWifiEntryTest.java index e4e6d49f6..e7d126097 100644 --- a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/StandardWifiEntryTest.java +++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/StandardWifiEntryTest.java @@ -43,6 +43,7 @@ import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.MacAddress; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkScoreManager; import android.net.wifi.ScanResult; @@ -72,6 +73,7 @@ public class StandardWifiEntryTest { @Mock private WifiEntry.WifiEntryCallback mMockListener; @Mock private WifiEntry.ConnectCallback mMockConnectCallback; @Mock private WifiManager mMockWifiManager; + @Mock private ConnectivityManager mMockConnectivityManager; @Mock private WifiInfo mMockWifiInfo; @Mock private NetworkInfo mMockNetworkInfo; @Mock private Context mMockContext; @@ -623,4 +625,43 @@ public class StandardWifiEntryTest { assertThat(entry.getMeteredChoice()).isEqualTo(WifiEntry.METERED_CHOICE_METERED); } + + @Test + public void testCanSignIn_captivePortalCapability_returnsTrue() { + final StandardWifiEntry entry = new StandardWifiEntry(mMockContext, mTestHandler, + ssidAndSecurityToStandardWifiEntryKey("ssid", SECURITY_NONE), + Arrays.asList( + buildScanResult("ssid", "bssid0", 0, GOOD_RSSI)), + mMockWifiManager, false /* forSavedNetworksPage */); + NetworkCapabilities captivePortalCapabilities = new NetworkCapabilities(); + captivePortalCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); + entry.updateNetworkCapabilities(captivePortalCapabilities); + + assertThat(entry.canSignIn()).isTrue(); + } + + @Test + public void testUpdateNetworkCapabilities_userConnect_autoOpenCaptivePortalOnce() { + when(mMockContext.getSystemService(Context.CONNECTIVITY_SERVICE)) + .thenReturn(mMockConnectivityManager); + final StandardWifiEntry entry = new StandardWifiEntry(mMockContext, mTestHandler, + ssidAndSecurityToStandardWifiEntryKey("ssid", SECURITY_NONE), + Arrays.asList( + buildScanResult("ssid", "bssid0", 0, GOOD_RSSI)), + mMockWifiManager, false /* forSavedNetworksPage */); + NetworkCapabilities captivePortalCapabilities = new NetworkCapabilities(); + captivePortalCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); + + // Simulate user tapping on the network and receiving captive portal capabilities. + // This should trigger the captive portal app. + entry.connect(null /* callback */); + entry.updateNetworkCapabilities(captivePortalCapabilities); + + verify(mMockConnectivityManager, times(1)).startCaptivePortalApp(any()); + + // Update network capabilities again. This should not trigger the captive portal app. + entry.updateNetworkCapabilities(captivePortalCapabilities); + + verify(mMockConnectivityManager, times(1)).startCaptivePortalApp(any()); + } } |