From b6245156f647e4e41198026ef6a3bfc54790e384 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 27 Apr 2017 00:56:39 +0900 Subject: [PATCH] Add a "sign into network" button to the wifi details page. Bug: 36203355 Test: manually signed into captive portal Test: make -j64 RunSettingsRoboTests Change-Id: I3242ef30125ddfdaaac9b80ead4f8ac14ea6d364 --- res/xml/wifi_network_details_fragment.xml | 6 +- .../vpn2/ConnectivityManagerWrapper.java | 33 ++++++ .../vpn2/ConnectivityManagerWrapperImpl.java | 20 ++++ .../WifiDetailPreferenceController.java | 108 ++++++++++++++--- .../details/WifiNetworkDetailsFragment.java | 20 ++-- .../WifiDetailPreferenceControllerTest.java | 111 +++++++++++++++--- 6 files changed, 255 insertions(+), 43 deletions(-) diff --git a/res/xml/wifi_network_details_fragment.xml b/res/xml/wifi_network_details_fragment.xml index 5b20d595daf..5d5958d534e 100644 --- a/res/xml/wifi_network_details_fragment.xml +++ b/res/xml/wifi_network_details_fragment.xml @@ -28,9 +28,9 @@ android:key="general_details_category" > + android:key="buttons" + android:layout="@layout/two_buttons_panel" + android:selectable="false" /> mConnectivityManagerWrapper.startCaptivePortalApp(mNetwork)); + mSignalStrengthPref = (WifiDetailPreference) screen.findPreference(KEY_SIGNAL_STRENGTH_PREF); mLinkSpeedPref = (WifiDetailPreference) screen.findPreference(KEY_LINK_SPEED); @@ -191,6 +249,12 @@ public class WifiDetailPreferenceController extends PreferenceController impleme @Override public void onResume() { + mConnectivityManagerWrapper.registerNetworkCallback(mNetworkRequest, mNetworkCallback, + mHandler); + mNetwork = mWifiManager.getCurrentNetwork(); + mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork); + mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork); + updateInfo(); mContext.registerReceiver(mReceiver, mFilter); @@ -198,13 +262,17 @@ public class WifiDetailPreferenceController extends PreferenceController impleme @Override public void onPause() { + mNetwork = null; + mLinkProperties = null; + mNetworkCapabilities = null; mContext.unregisterReceiver(mReceiver); + mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); } private void updateInfo() { - mNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); + mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork); mWifiInfo = mWifiManager.getConnectionInfo(); - if (mNetworkInfo == null || mWifiInfo == null) { + if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) { exitActivity(); return; } @@ -238,7 +306,7 @@ public class WifiDetailPreferenceController extends PreferenceController impleme } mFrequencyPref.setDetailText(band); - setIpText(); + updateIpLayerInfo(); } private void exitActivity() { @@ -270,7 +338,9 @@ public class WifiDetailPreferenceController extends PreferenceController impleme mSignalStrengthPref.setDetailText(mSignalStr[summarySignalLevel]); } - private void setIpText() { + private void updateIpLayerInfo() { + mSignInButton.setVisibility(canSignIntoNetwork() ? View.VISIBLE : View.INVISIBLE); + // Reset all fields mIpv6AddressCategory.removeAll(); mIpv6AddressCategory.setVisible(false); @@ -279,18 +349,12 @@ public class WifiDetailPreferenceController extends PreferenceController impleme mGatewayPref.setVisible(false); mDnsPref.setVisible(false); - Network currentNetwork = mWifiManager.getCurrentNetwork(); - if (currentNetwork == null) { + if (mNetwork == null || mLinkProperties == null) { return; } + List addresses = mLinkProperties.getAddresses(); - LinkProperties linkProperties = mConnectivityManager.getLinkProperties(currentNetwork); - if (linkProperties == null) { - return; - } - List addresses = linkProperties.getAddresses(); - - // Set IPv4 and Ipv6 addresses + // Set IPv4 and IPv6 addresses for (int i = 0; i < addresses.size(); i++) { InetAddress addr = addresses.get(i); if (addr instanceof Inet4Address) { @@ -310,7 +374,7 @@ public class WifiDetailPreferenceController extends PreferenceController impleme // Set up IPv4 gateway and subnet mask String gateway = null; String subnet = null; - for (RouteInfo routeInfo : linkProperties.getRoutes()) { + for (RouteInfo routeInfo : mLinkProperties.getRoutes()) { if (routeInfo.hasGateway() && routeInfo.getGateway() instanceof Inet4Address) { gateway = routeInfo.getGateway().getHostAddress(); } @@ -333,7 +397,7 @@ public class WifiDetailPreferenceController extends PreferenceController impleme // Set IPv4 DNS addresses StringJoiner stringJoiner = new StringJoiner(","); - for (InetAddress dnsServer : linkProperties.getDnsServers()) { + for (InetAddress dnsServer : mLinkProperties.getDnsServers()) { if (dnsServer instanceof Inet4Address) { stringJoiner.add(dnsServer.getHostAddress()); } @@ -362,6 +426,14 @@ public class WifiDetailPreferenceController extends PreferenceController impleme return mWifiInfo != null && mWifiInfo.isEphemeral() || mWifiConfig != null; } + /** + * Returns whether the user can sign into the network represented by this preference. + */ + private boolean canSignIntoNetwork() { + return mNetworkCapabilities != null && mNetworkCapabilities.hasCapability( + NET_CAPABILITY_CAPTIVE_PORTAL); + } + /** * Forgets the wifi network associated with this preference. */ diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java index adcfcc0b3ab..fe16cdb0306 100644 --- a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java +++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java @@ -17,8 +17,12 @@ package com.android.settings.wifi.details; import android.content.Context; import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.NetworkRequest; import android.net.wifi.WifiManager; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.widget.Button; import com.android.internal.logging.nano.MetricsProto; @@ -26,6 +30,7 @@ import com.android.settings.R; import com.android.settings.applications.LayoutPreference; import com.android.settings.core.PreferenceController; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.vpn2.ConnectivityManagerWrapperImpl; import com.android.settingslib.wifi.AccessPoint; import java.util.ArrayList; @@ -40,9 +45,6 @@ import java.util.List; public class WifiNetworkDetailsFragment extends DashboardFragment { private static final String TAG = "WifiNetworkDetailsFrg"; - // XML KEYS - private static final String KEY_FORGET_BUTTON = "forget_button"; - private AccessPoint mAccessPoint; private Button mForgetButton; private WifiDetailPreferenceController mWifiDetailPreferenceController; @@ -59,9 +61,11 @@ public class WifiNetworkDetailsFragment extends DashboardFragment { // Header Title set automatically from launching Preference - LayoutPreference forgetPreference = ((LayoutPreference) findPreference(KEY_FORGET_BUTTON)); - forgetPreference.setVisible(mWifiDetailPreferenceController.canForgetNetwork()); - mForgetButton = (Button) forgetPreference.findViewById(R.id.button); + LayoutPreference buttonsPreference = ((LayoutPreference) findPreference( + WifiDetailPreferenceController.KEY_BUTTONS_PREF)); + buttonsPreference.setVisible(mWifiDetailPreferenceController.canForgetNetwork()); + + mForgetButton = (Button) buttonsPreference.findViewById(R.id.left_button); mForgetButton.setText(R.string.forget); mForgetButton.setOnClickListener(view -> forgetNetwork()); } @@ -88,11 +92,13 @@ public class WifiNetworkDetailsFragment extends DashboardFragment { @Override protected List getPreferenceControllers(Context context) { + ConnectivityManager cm = context.getSystemService(ConnectivityManager.class); mWifiDetailPreferenceController = new WifiDetailPreferenceController( mAccessPoint, - context.getSystemService(ConnectivityManager.class), + new ConnectivityManagerWrapperImpl(cm), context, this, + new Handler(Looper.getMainLooper()), // UI thread. getLifecycle(), context.getSystemService(WifiManager.class)); diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java index 8d28dee82b3..b9dde37f3fc 100644 --- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java @@ -18,6 +18,8 @@ package com.android.settings.wifi.details; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; @@ -29,25 +31,35 @@ import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkBadging; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; +import android.net.NetworkRequest; import android.net.RouteInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.os.Handler; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceCategory; import android.support.v7.preference.PreferenceScreen; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; import com.android.settings.R; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; +import com.android.settings.applications.LayoutPreference; import com.android.settings.core.lifecycle.Lifecycle; import com.android.settings.wifi.WifiDetailPreference; +import com.android.settings.vpn2.ConnectivityManagerWrapper; +import com.android.settings.vpn2.ConnectivityManagerWrapperImpl; import com.android.settingslib.wifi.AccessPoint; import org.junit.Before; @@ -55,6 +67,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; @@ -83,6 +96,7 @@ public class WifiDetailPreferenceControllerTest { @Mock private AccessPoint mockAccessPoint; @Mock private Activity mockActivity; @Mock private ConnectivityManager mockConnectivityManager; + @Mock private ConnectivityManagerWrapperImpl mockConnectivityManagerWrapper; @Mock private Network mockNetwork; @Mock private NetworkInfo mockNetworkInfo; @Mock private WifiConfiguration mockWifiConfig; @@ -91,6 +105,8 @@ public class WifiDetailPreferenceControllerTest { @Mock private WifiManager mockWifiManager; @Mock private Preference mockConnectionDetailPref; + @Mock private LayoutPreference mockButtonsPref; + @Mock private Button mockSignInButton; @Mock private WifiDetailPreference mockSignalStrengthPref; @Mock private WifiDetailPreference mockLinkSpeedPref; @Mock private WifiDetailPreference mockFrequencyPref; @@ -102,9 +118,10 @@ public class WifiDetailPreferenceControllerTest { @Mock private WifiDetailPreference mockDnsPref; @Mock private PreferenceCategory mockIpv6AddressCategory; - private LinkProperties mLinkProperties; + private ArgumentCaptor mCallbackCaptor; private Context mContext = RuntimeEnvironment.application; private Lifecycle mLifecycle; + private LinkProperties mLinkProperties; private WifiDetailPreferenceController mController; @Before @@ -126,14 +143,20 @@ public class WifiDetailPreferenceControllerTest { throw new RuntimeException(e); } + mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class); + when(mockAccessPoint.getConfig()).thenReturn(mockWifiConfig); when(mockAccessPoint.getLevel()).thenReturn(LEVEL); when(mockAccessPoint.getNetworkInfo()).thenReturn(mockNetworkInfo); when(mockAccessPoint.getRssi()).thenReturn(RSSI); when(mockAccessPoint.getSecurityString(false)).thenReturn(SECURITY); + when(mockConnectivityManagerWrapper.getConnectivityManager()) + .thenReturn(mockConnectivityManager); when(mockConnectivityManager.getNetworkInfo(any(Network.class))) .thenReturn(mockNetworkInfo); + doNothing().when(mockConnectivityManagerWrapper).registerNetworkCallback( + any(NetworkRequest.class), mCallbackCaptor.capture(), any(Handler.class)); when(mockWifiInfo.getLinkSpeed()).thenReturn(LINK_SPEED); when(mockWifiInfo.getRssi()).thenReturn(RSSI); @@ -154,9 +177,10 @@ public class WifiDetailPreferenceControllerTest { private WifiDetailPreferenceController newWifiDetailPreferenceController() { return new WifiDetailPreferenceController( mockAccessPoint, - mockConnectivityManager, + mockConnectivityManagerWrapper, mContext, mockFragment, + null, // Handler mLifecycle, mockWifiManager); } @@ -166,6 +190,10 @@ public class WifiDetailPreferenceControllerTest { when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_CONNECTION_DETAIL_PREF)) .thenReturn(mockConnectionDetailPref); + when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_BUTTONS_PREF)) + .thenReturn(mockButtonsPref); + when(mockButtonsPref.findViewById(R.id.right_button)) + .thenReturn(mockSignInButton); when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_SIGNAL_STRENGTH_PREF)) .thenReturn(mockSignalStrengthPref); when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_LINK_SPEED)) @@ -214,6 +242,23 @@ public class WifiDetailPreferenceControllerTest { verify(mockConnectivityManager, times(1)).getNetworkInfo(any(Network.class)); } + @Test + public void networkCallback_shouldBeRegisteredOnResume() { + mController.onResume(); + + verify(mockConnectivityManagerWrapper, times(1)).registerNetworkCallback( + any(NetworkRequest.class), mCallbackCaptor.capture(), any(Handler.class)); + } + + @Test + public void networkCallback_shouldBeUnregisteredOnPause() { + mController.onResume(); + mController.onPause(); + + verify(mockConnectivityManager, times(1)).unregisterNetworkCallback( + mCallbackCaptor.getValue()); + } + @Test public void connectionDetailPref_shouldHaveIconSet() { Drawable expectedIcon = @@ -311,23 +356,14 @@ public class WifiDetailPreferenceControllerTest { } @Test - public void noCurrentNetwork_allIpDetailsHidden() { + public void noCurrentNetwork_shouldFinishActivity() { + // If WifiManager#getCurrentNetwork() returns null, then the network is neither connected + // nor connecting and WifiStateMachine has not reached L2ConnectedState. when(mockWifiManager.getCurrentNetwork()).thenReturn(null); - reset(mockIpv6AddressCategory, mockIpAddressPref, mockSubnetPref, mockGatewayPref, - mockDnsPref); mController.onResume(); - verify(mockIpv6AddressCategory).setVisible(false); - verify(mockIpAddressPref).setVisible(false); - verify(mockSubnetPref).setVisible(false); - verify(mockGatewayPref).setVisible(false); - verify(mockDnsPref).setVisible(false); - verify(mockIpv6AddressCategory, never()).setVisible(true); - verify(mockIpAddressPref, never()).setVisible(true); - verify(mockSubnetPref, never()).setVisible(true); - verify(mockGatewayPref, never()).setVisible(true); - verify(mockDnsPref, never()).setVisible(true); + verify(mockActivity).finish(); } @Test @@ -432,6 +468,15 @@ public class WifiDetailPreferenceControllerTest { verify(mockActivity).finish(); } + @Test + public void networkOnLost_shouldFinishActivity() { + mController.onResume(); + + mCallbackCaptor.getValue().onLost(mockNetwork); + + verify(mockActivity).finish(); + } + @Test public void ipv6AddressPref_shouldHaveHostAddressTextSet() { LinkAddress ipv6Address = new LinkAddress(mIpv6Address, 128); @@ -457,4 +502,40 @@ public class WifiDetailPreferenceControllerTest { verify(mockIpv6AddressCategory).addPreference(preferenceCaptor.capture()); assertThat(preferenceCaptor.getValue().isSelectable()).isFalse(); } + + @Test + public void captivePortal_shouldShowSignInButton() { + reset(mockSignInButton); + InOrder inOrder = inOrder(mockSignInButton); + mController.onResume(); + inOrder.verify(mockSignInButton).setVisibility(View.INVISIBLE); + + NetworkCapabilities nc = new NetworkCapabilities(); + nc.clearAll(); + nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + + NetworkCallback callback = mCallbackCaptor.getValue(); + callback.onCapabilitiesChanged(mockNetwork, nc); + inOrder.verify(mockSignInButton).setVisibility(View.INVISIBLE); + + nc = new NetworkCapabilities(nc); + nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); + callback.onCapabilitiesChanged(mockNetwork, nc); + inOrder.verify(mockSignInButton).setVisibility(View.VISIBLE); + + nc = new NetworkCapabilities(nc); + nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); + callback.onCapabilitiesChanged(mockNetwork, nc); + inOrder.verify(mockSignInButton).setVisibility(View.INVISIBLE); + } + + @Test + public void testSignInButton_shouldStartCaptivePortalApp() { + mController.onResume(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(OnClickListener.class); + verify(mockSignInButton).setOnClickListener(captor.capture()); + captor.getValue().onClick(mockSignInButton); + verify(mockConnectivityManagerWrapper).startCaptivePortalApp(mockNetwork); + } }