1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.settings.wifi.details;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import static org.mockito.Matchers.any;
21 import static org.mockito.Mockito.doNothing;
22 import static org.mockito.Mockito.inOrder;
23 import static org.mockito.Mockito.never;
24 import static org.mockito.Mockito.reset;
25 import static org.mockito.Mockito.times;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.app.Activity;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.graphics.drawable.Drawable;
33 import android.net.ConnectivityManager;
34 import android.net.ConnectivityManager.NetworkCallback;
35 import android.net.IpPrefix;
36 import android.net.LinkAddress;
37 import android.net.LinkProperties;
38 import android.net.Network;
39 import android.net.NetworkBadging;
40 import android.net.NetworkCapabilities;
41 import android.net.NetworkInfo;
42 import android.net.NetworkRequest;
43 import android.net.RouteInfo;
44 import android.net.wifi.WifiConfiguration;
45 import android.net.wifi.WifiInfo;
46 import android.net.wifi.WifiManager;
47 import android.os.Handler;
48 import android.support.v7.preference.Preference;
49 import android.support.v7.preference.PreferenceCategory;
50 import android.support.v7.preference.PreferenceScreen;
51 import android.view.View;
52 import android.view.View.OnClickListener;
53 import android.widget.Button;
54 
55 import com.android.internal.logging.nano.MetricsProto;
56 import com.android.settings.R;
57 import com.android.settings.SettingsRobolectricTestRunner;
58 import com.android.settings.TestConfig;
59 import com.android.settings.applications.LayoutPreference;
60 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
61 import com.android.settings.core.lifecycle.Lifecycle;
62 import com.android.settings.vpn2.ConnectivityManagerWrapperImpl;
63 import com.android.settings.wifi.WifiDetailPreference;
64 import com.android.settingslib.wifi.AccessPoint;
65 
66 import org.junit.Before;
67 import org.junit.Test;
68 import org.junit.runner.RunWith;
69 import org.mockito.Answers;
70 import org.mockito.ArgumentCaptor;
71 import org.mockito.Captor;
72 import org.mockito.InOrder;
73 import org.mockito.Mock;
74 import org.mockito.MockitoAnnotations;
75 import org.robolectric.RuntimeEnvironment;
76 import org.robolectric.annotation.Config;
77 
78 import java.net.Inet4Address;
79 import java.net.Inet6Address;
80 import java.net.InetAddress;
81 import java.net.UnknownHostException;
82 import java.util.Arrays;
83 import java.util.List;
84 import java.util.stream.Collectors;
85 
86 @RunWith(SettingsRobolectricTestRunner.class)
87 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
88 public class WifiDetailPreferenceControllerTest {
89 
90     private static final int LEVEL = 1;
91     private static final int RSSI = -55;
92     private static final int LINK_SPEED = 123;
93     private static final String MAC_ADDRESS = WifiInfo.DEFAULT_MAC_ADDRESS;
94     private static final String SECURITY = "None";
95 
96     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
97     private PreferenceScreen mockScreen;
98 
99     @Mock private AccessPoint mockAccessPoint;
100     @Mock private Activity mockActivity;
101     @Mock private ConnectivityManager mockConnectivityManager;
102     @Mock private ConnectivityManagerWrapperImpl mockConnectivityManagerWrapper;
103     @Mock private Network mockNetwork;
104     @Mock private NetworkInfo mockNetworkInfo;
105     @Mock private WifiConfiguration mockWifiConfig;
106     @Mock private WifiInfo mockWifiInfo;
107     @Mock private WifiNetworkDetailsFragment mockFragment;
108     @Mock private WifiManager mockWifiManager;
109     @Mock private MetricsFeatureProvider mockMetricsFeatureProvider;
110 
111     @Mock private Preference mockConnectionDetailPref;
112     @Mock private LayoutPreference mockButtonsPref;
113     @Mock private Button mockSignInButton;
114     @Mock private WifiDetailPreference mockSignalStrengthPref;
115     @Mock private WifiDetailPreference mockLinkSpeedPref;
116     @Mock private WifiDetailPreference mockFrequencyPref;
117     @Mock private WifiDetailPreference mockSecurityPref;
118     @Mock private WifiDetailPreference mockMacAddressPref;
119     @Mock private WifiDetailPreference mockIpAddressPref;
120     @Mock private WifiDetailPreference mockGatewayPref;
121     @Mock private WifiDetailPreference mockSubnetPref;
122     @Mock private WifiDetailPreference mockDnsPref;
123     @Mock private Button mockForgetButton;
124     @Mock private PreferenceCategory mockIpv6Category;
125     @Mock private WifiDetailPreference mockIpv6AddressesPref;
126 
127     @Captor private ArgumentCaptor<NetworkCallback> mCallbackCaptor;
128     @Captor private ArgumentCaptor<View.OnClickListener> mForgetClickListener;
129     @Captor private ArgumentCaptor<Preference> mIpv6AddressCaptor;
130 
131     private Context mContext = RuntimeEnvironment.application;
132     private Lifecycle mLifecycle;
133     private LinkProperties mLinkProperties;
134     private WifiDetailPreferenceController mController;
135 
136     // This class exists so that these values can be made static final. They can't be static final
137     // members of the test class, because any attempt to call IpPrefix or RouteInfo constructors
138     // during static initialization of the test class results in NoSuchMethorError being thrown
139     // when the test is run.
140     private static class Constants {
141         static final int IPV4_PREFIXLEN = 25;
142         static final LinkAddress IPV4_ADDR;
143         static final Inet4Address IPV4_GATEWAY;
144         static final RouteInfo IPV4_DEFAULT;
145         static final RouteInfo IPV4_SUBNET;
146         static final LinkAddress IPV6_LINKLOCAL;
147         static final LinkAddress IPV6_GLOBAL1;
148         static final LinkAddress IPV6_GLOBAL2;
149         static final InetAddress IPV4_DNS1;
150         static final InetAddress IPV4_DNS2;
151         static final InetAddress IPV6_DNS;
152 
ipv6LinkAddress(String addr)153         private static LinkAddress ipv6LinkAddress(String addr) throws UnknownHostException {
154             return new LinkAddress(InetAddress.getByName(addr), 64);
155         }
156 
ipv4LinkAddress(String addr, int prefixlen)157         private static LinkAddress ipv4LinkAddress(String addr, int prefixlen)
158                 throws UnknownHostException {
159             return new LinkAddress(InetAddress.getByName(addr), prefixlen);
160         }
161 
162         static {
163             try {
164                 // We create our test constants in these roundabout ways because the robolectric
165                 // shadows don't contain NetworkUtils.parseNumericAddress and other utility methods,
166                 // so the easy ways to do things fail with NoSuchMethodError.
167                 IPV4_ADDR = ipv4LinkAddress("192.0.2.2", IPV4_PREFIXLEN);
168                 IPV4_GATEWAY = (Inet4Address) InetAddress.getByName("192.0.2.127");
169 
170                 final Inet4Address any4 = (Inet4Address) InetAddress.getByName("0.0.0.0");
171                 IpPrefix subnet = new IpPrefix(IPV4_ADDR.getAddress(), IPV4_PREFIXLEN);
172                 IPV4_SUBNET = new RouteInfo(subnet, any4);
173                 IPV4_DEFAULT = new RouteInfo(new IpPrefix(any4, 0), IPV4_GATEWAY);
174 
175                 IPV6_LINKLOCAL = ipv6LinkAddress("fe80::211:25ff:fef8:7cb2%1");
176                 IPV6_GLOBAL1 = ipv6LinkAddress("2001:db8:1::211:25ff:fef8:7cb2");
177                 IPV6_GLOBAL2 = ipv6LinkAddress("2001:db8:1::3dfe:8902:f98f:739d");
178 
179                 IPV4_DNS1 = InetAddress.getByName("8.8.8.8");
180                 IPV4_DNS2 = InetAddress.getByName("8.8.4.4");
181                 IPV6_DNS = InetAddress.getByName("2001:4860:4860::64");
182             } catch (UnknownHostException e) {
183                 throw new RuntimeException("Invalid hardcoded IP addresss: " + e);
184             }
185         }
186     }
187 
188     @Before
setUp()189     public void setUp() {
190         MockitoAnnotations.initMocks(this);
191 
192         mLifecycle = new Lifecycle();
193 
194         when(mockAccessPoint.getConfig()).thenReturn(mockWifiConfig);
195         when(mockAccessPoint.getLevel()).thenReturn(LEVEL);
196         when(mockAccessPoint.getSecurityString(false)).thenReturn(SECURITY);
197 
198         when(mockConnectivityManagerWrapper.getConnectivityManager())
199                 .thenReturn(mockConnectivityManager);
200         when(mockConnectivityManager.getNetworkInfo(any(Network.class)))
201                 .thenReturn(mockNetworkInfo);
202         doNothing().when(mockConnectivityManagerWrapper).registerNetworkCallback(
203                 any(NetworkRequest.class), mCallbackCaptor.capture(), any(Handler.class));
204         doNothing().when(mockForgetButton).setOnClickListener(mForgetClickListener.capture());
205 
206         when(mockWifiInfo.getLinkSpeed()).thenReturn(LINK_SPEED);
207         when(mockWifiInfo.getRssi()).thenReturn(RSSI);
208         when(mockWifiInfo.getMacAddress()).thenReturn(MAC_ADDRESS);
209         when(mockWifiManager.getConnectionInfo()).thenReturn(mockWifiInfo);
210 
211         when(mockWifiManager.getCurrentNetwork()).thenReturn(mockNetwork);
212         mLinkProperties = new LinkProperties();
213         when(mockConnectivityManager.getLinkProperties(mockNetwork)).thenReturn(mLinkProperties);
214 
215         when(mockFragment.getActivity()).thenReturn(mockActivity);
216 
217         setupMockedPreferenceScreen();
218         mController = newWifiDetailPreferenceController();
219     }
220 
newWifiDetailPreferenceController()221     private WifiDetailPreferenceController newWifiDetailPreferenceController() {
222         return new WifiDetailPreferenceController(
223                 mockAccessPoint,
224                 mockConnectivityManagerWrapper,
225                 mContext,
226                 mockFragment,
227                 null,  // Handler
228                 mLifecycle,
229                 mockWifiManager,
230                 mockMetricsFeatureProvider);
231     }
232 
setupMockedPreferenceScreen()233     private void setupMockedPreferenceScreen() {
234         when(mockScreen.getPreferenceManager().getContext()).thenReturn(mContext);
235 
236         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_CONNECTION_DETAIL_PREF))
237                 .thenReturn(mockConnectionDetailPref);
238         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_BUTTONS_PREF))
239                 .thenReturn(mockButtonsPref);
240         when(mockButtonsPref.findViewById(R.id.forget_button))
241                 .thenReturn(mockForgetButton);
242         when(mockButtonsPref.findViewById(R.id.signin_button))
243                 .thenReturn(mockSignInButton);
244         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_SIGNAL_STRENGTH_PREF))
245                 .thenReturn(mockSignalStrengthPref);
246         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_LINK_SPEED))
247                 .thenReturn(mockLinkSpeedPref);
248         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_FREQUENCY_PREF))
249                 .thenReturn(mockFrequencyPref);
250         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_SECURITY_PREF))
251                 .thenReturn(mockSecurityPref);
252         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_MAC_ADDRESS_PREF))
253                 .thenReturn(mockMacAddressPref);
254         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IP_ADDRESS_PREF))
255                 .thenReturn(mockIpAddressPref);
256         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_GATEWAY_PREF))
257                 .thenReturn(mockGatewayPref);
258         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_SUBNET_MASK_PREF))
259                 .thenReturn(mockSubnetPref);
260         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_DNS_PREF))
261                 .thenReturn(mockDnsPref);
262         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IPV6_CATEGORY))
263                 .thenReturn(mockIpv6Category);
264         when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IPV6_ADDRESSES_PREF))
265                 .thenReturn(mockIpv6AddressesPref);
266     }
267 
displayAndResume()268     private void displayAndResume() {
269         mController.displayPreference(mockScreen);
270         mController.onResume();
271     }
272 
273     @Test
isAvailable_shouldAlwaysReturnTrue()274     public void isAvailable_shouldAlwaysReturnTrue() {
275         mController.displayPreference(mockScreen);
276 
277         assertThat(mController.isAvailable()).isTrue();
278     }
279 
280     @Test
securityPreference_stringShouldBeSet()281     public void securityPreference_stringShouldBeSet() {
282         displayAndResume();
283 
284         verify(mockSecurityPref).setDetailText(SECURITY);
285     }
286 
287     @Test
latestWifiInfo_shouldBeFetchedInDisplayPreference()288     public void latestWifiInfo_shouldBeFetchedInDisplayPreference() {
289         displayAndResume();
290 
291         verify(mockWifiManager, times(1)).getConnectionInfo();
292     }
293 
294     @Test
latestNetworkInfo_shouldBeFetchedInDisplayPreference()295     public void latestNetworkInfo_shouldBeFetchedInDisplayPreference() {
296         displayAndResume();
297 
298         verify(mockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
299     }
300 
301     @Test
networkCallback_shouldBeRegisteredOnResume()302     public void networkCallback_shouldBeRegisteredOnResume() {
303         displayAndResume();
304 
305         verify(mockConnectivityManagerWrapper, times(1)).registerNetworkCallback(
306                 any(NetworkRequest.class), mCallbackCaptor.capture(), any(Handler.class));
307     }
308 
309     @Test
networkCallback_shouldBeUnregisteredOnPause()310     public void networkCallback_shouldBeUnregisteredOnPause() {
311         displayAndResume();
312         mController.onPause();
313 
314         verify(mockConnectivityManager, times(1)).unregisterNetworkCallback(
315                 mCallbackCaptor.getValue());
316     }
317 
318     @Test
connectionDetailPref_shouldHaveIconSet()319     public void connectionDetailPref_shouldHaveIconSet() {
320         Drawable expectedIcon =
321                 NetworkBadging.getWifiIcon(LEVEL, NetworkBadging.BADGING_NONE, mContext.getTheme());
322 
323         displayAndResume();
324 
325         verify(mockConnectionDetailPref).setIcon(expectedIcon);
326     }
327 
328     @Test
connectionDetailPref_shouldHaveTitleSet()329     public void connectionDetailPref_shouldHaveTitleSet() {
330         String summary = "summary";
331         when(mockAccessPoint.getSettingsSummary()).thenReturn(summary);
332 
333         displayAndResume();
334 
335         verify(mockConnectionDetailPref).setTitle(summary);
336     }
337 
338     @Test
signalStrengthPref_shouldHaveIconSet()339     public void signalStrengthPref_shouldHaveIconSet() {
340         displayAndResume();
341 
342         verify(mockSignalStrengthPref).setIcon(any(Drawable.class));
343     }
344 
345     @Test
signalStrengthPref_shouldHaveDetailTextSet()346     public void signalStrengthPref_shouldHaveDetailTextSet() {
347         String expectedStrength =
348                 mContext.getResources().getStringArray(R.array.wifi_signal)[LEVEL];
349 
350         displayAndResume();
351 
352         verify(mockSignalStrengthPref).setDetailText(expectedStrength);
353     }
354 
355     @Test
linkSpeedPref_shouldHaveDetailTextSet()356     public void linkSpeedPref_shouldHaveDetailTextSet() {
357         String expectedLinkSpeed = mContext.getString(R.string.link_speed, LINK_SPEED);
358 
359         displayAndResume();
360 
361         verify(mockLinkSpeedPref).setDetailText(expectedLinkSpeed);
362     }
363 
364     @Test
linkSpeedPref_shouldNotShowIfNotSet()365     public void linkSpeedPref_shouldNotShowIfNotSet() {
366         when(mockWifiInfo.getLinkSpeed()).thenReturn(-1);
367 
368         displayAndResume();
369 
370         verify(mockLinkSpeedPref).setVisible(false);
371     }
372 
373     @Test
macAddressPref_shouldHaveDetailTextSet()374     public void macAddressPref_shouldHaveDetailTextSet() {
375         displayAndResume();
376 
377         verify(mockMacAddressPref).setDetailText(MAC_ADDRESS);
378     }
379 
380     @Test
ipAddressPref_shouldHaveDetailTextSet()381     public void ipAddressPref_shouldHaveDetailTextSet() {
382         mLinkProperties.addLinkAddress(Constants.IPV4_ADDR);
383 
384         displayAndResume();
385 
386         verify(mockIpAddressPref).setDetailText(Constants.IPV4_ADDR.getAddress().getHostAddress());
387     }
388 
389     @Test
gatewayAndSubnet_shouldHaveDetailTextSet()390     public void gatewayAndSubnet_shouldHaveDetailTextSet() {
391         mLinkProperties.addLinkAddress(Constants.IPV4_ADDR);
392         mLinkProperties.addRoute(Constants.IPV4_DEFAULT);
393         mLinkProperties.addRoute(Constants.IPV4_SUBNET);
394 
395         displayAndResume();
396 
397         verify(mockSubnetPref).setDetailText("255.255.255.128");
398         verify(mockGatewayPref).setDetailText("192.0.2.127");
399     }
400 
401     @Test
dnsServersPref_shouldHaveDetailTextSet()402     public void dnsServersPref_shouldHaveDetailTextSet() throws UnknownHostException {
403         mLinkProperties.addDnsServer(InetAddress.getByAddress(new byte[]{8,8,4,4}));
404         mLinkProperties.addDnsServer(InetAddress.getByAddress(new byte[]{8,8,8,8}));
405 
406         displayAndResume();
407 
408         verify(mockDnsPref).setDetailText("8.8.4.4,8.8.8.8");
409     }
410 
411     @Test
noCurrentNetwork_shouldFinishActivity()412     public void noCurrentNetwork_shouldFinishActivity() {
413         // If WifiManager#getCurrentNetwork() returns null, then the network is neither connected
414         // nor connecting and WifiStateMachine has not reached L2ConnectedState.
415         when(mockWifiManager.getCurrentNetwork()).thenReturn(null);
416 
417         displayAndResume();
418 
419         verify(mockActivity).finish();
420     }
421 
422     @Test
noLinkProperties_allIpDetailsHidden()423     public void noLinkProperties_allIpDetailsHidden() {
424         when(mockConnectivityManager.getLinkProperties(mockNetwork)).thenReturn(null);
425         reset(mockIpv6Category, mockIpAddressPref, mockSubnetPref, mockGatewayPref,
426                 mockDnsPref);
427 
428         displayAndResume();
429 
430         verify(mockIpv6Category).setVisible(false);
431         verify(mockIpAddressPref).setVisible(false);
432         verify(mockSubnetPref).setVisible(false);
433         verify(mockGatewayPref).setVisible(false);
434         verify(mockDnsPref).setVisible(false);
435         verify(mockIpv6Category, never()).setVisible(true);
436         verify(mockIpAddressPref, never()).setVisible(true);
437         verify(mockSubnetPref, never()).setVisible(true);
438         verify(mockGatewayPref, never()).setVisible(true);
439         verify(mockDnsPref, never()).setVisible(true);
440     }
441 
442     // Convenience method to convert a LinkAddress to a string without a prefix length.
asString(LinkAddress l)443     private String asString(LinkAddress l) {
444         return l.getAddress().getHostAddress();
445     }
446 
447     // Pretend that the NetworkCallback was triggered with a new copy of lp. We need to create a
448     // new copy because the code only updates if !mLinkProperties.equals(lp).
updateLinkProperties(LinkProperties lp)449     private void updateLinkProperties(LinkProperties lp) {
450         mCallbackCaptor.getValue().onLinkPropertiesChanged(mockNetwork, new LinkProperties(lp));
451     }
452 
updateNetworkCapabilities(NetworkCapabilities nc)453     private void updateNetworkCapabilities(NetworkCapabilities nc) {
454         mCallbackCaptor.getValue().onCapabilitiesChanged(mockNetwork, new NetworkCapabilities(nc));
455     }
456 
makeNetworkCapabilities()457     private NetworkCapabilities makeNetworkCapabilities() {
458         NetworkCapabilities nc = new NetworkCapabilities();
459         nc.clearAll();
460         nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
461         return nc;
462     }
463 
verifyDisplayedIpv6Addresses(InOrder inOrder, LinkAddress... addresses)464     private void verifyDisplayedIpv6Addresses(InOrder inOrder, LinkAddress... addresses) {
465         String text = Arrays.stream(addresses)
466                 .map(address -> asString(address))
467                 .collect(Collectors.joining("\n"));
468         inOrder.verify(mockIpv6AddressesPref).setSummary(text);
469     }
470 
471     @Test
onLinkPropertiesChanged_updatesFields()472     public void onLinkPropertiesChanged_updatesFields() {
473         displayAndResume();
474 
475         InOrder inOrder = inOrder(mockIpAddressPref, mockGatewayPref, mockSubnetPref,
476                 mockDnsPref, mockIpv6Category, mockIpv6AddressesPref);
477 
478         LinkProperties lp = new LinkProperties();
479 
480         lp.addLinkAddress(Constants.IPV6_LINKLOCAL);
481         updateLinkProperties(lp);
482         verifyDisplayedIpv6Addresses(inOrder, Constants.IPV6_LINKLOCAL);
483         inOrder.verify(mockIpv6Category).setVisible(true);
484 
485         lp.addRoute(Constants.IPV4_DEFAULT);
486         updateLinkProperties(lp);
487         inOrder.verify(mockGatewayPref).setDetailText(Constants.IPV4_GATEWAY.getHostAddress());
488         inOrder.verify(mockGatewayPref).setVisible(true);
489 
490         lp.addLinkAddress(Constants.IPV4_ADDR);
491         lp.addRoute(Constants.IPV4_SUBNET);
492         updateLinkProperties(lp);
493         inOrder.verify(mockIpAddressPref).setDetailText(asString(Constants.IPV4_ADDR));
494         inOrder.verify(mockIpAddressPref).setVisible(true);
495         inOrder.verify(mockSubnetPref).setDetailText("255.255.255.128");
496         inOrder.verify(mockSubnetPref).setVisible(true);
497 
498         lp.addLinkAddress(Constants.IPV6_GLOBAL1);
499         lp.addLinkAddress(Constants.IPV6_GLOBAL2);
500         updateLinkProperties(lp);
501         verifyDisplayedIpv6Addresses(inOrder,
502                 Constants.IPV6_LINKLOCAL,
503                 Constants.IPV6_GLOBAL1,
504                 Constants.IPV6_GLOBAL2);
505 
506         lp.removeLinkAddress(Constants.IPV6_GLOBAL1);
507         updateLinkProperties(lp);
508         verifyDisplayedIpv6Addresses(inOrder,
509                 Constants.IPV6_LINKLOCAL,
510                 Constants.IPV6_GLOBAL2);
511 
512         lp.addDnsServer(Constants.IPV6_DNS);
513         updateLinkProperties(lp);
514         inOrder.verify(mockDnsPref, never()).setVisible(true);
515 
516         lp.addDnsServer(Constants.IPV4_DNS1);
517         lp.addDnsServer(Constants.IPV4_DNS2);
518         updateLinkProperties(lp);
519         inOrder.verify(mockDnsPref).setDetailText(
520                 Constants.IPV4_DNS1.getHostAddress() + "," +
521                 Constants.IPV4_DNS2.getHostAddress());
522         inOrder.verify(mockDnsPref).setVisible(true);
523     }
524 
525     @Test
onCapabilitiesChanged_callsRefreshIfNecessary()526     public void onCapabilitiesChanged_callsRefreshIfNecessary() {
527         NetworkCapabilities nc = makeNetworkCapabilities();
528         when(mockConnectivityManager.getNetworkCapabilities(mockNetwork))
529                 .thenReturn(new NetworkCapabilities(nc));
530 
531         String summary = "Connected, no Internet";
532         when(mockAccessPoint.getSettingsSummary()).thenReturn(summary);
533 
534         InOrder inOrder = inOrder(mockConnectionDetailPref);
535         displayAndResume();
536         inOrder.verify(mockConnectionDetailPref).setTitle(summary);
537 
538         // Check that an irrelevant capability update does not update the access point summary, as
539         // doing so could cause unnecessary jank...
540         summary = "Connected";
541         when(mockAccessPoint.getSettingsSummary()).thenReturn(summary);
542         updateNetworkCapabilities(nc);
543         inOrder.verify(mockConnectionDetailPref, never()).setTitle(any());
544 
545         // ... but that if the network validates, then we do refresh.
546         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
547         updateNetworkCapabilities(nc);
548         inOrder.verify(mockConnectionDetailPref).setTitle(summary);
549 
550         summary = "Connected, no Internet";
551         when(mockAccessPoint.getSettingsSummary()).thenReturn(summary);
552 
553         // Another irrelevant update won't cause the UI to refresh...
554         updateNetworkCapabilities(nc);
555         inOrder.verify(mockConnectionDetailPref, never()).setTitle(any());
556 
557         // ... but if the network is no longer validated, then we display "connected, no Internet".
558         nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
559         updateNetworkCapabilities(nc);
560         inOrder.verify(mockConnectionDetailPref).setTitle(summary);
561     }
562 
563     @Test
canForgetNetwork_noNetwork()564     public void canForgetNetwork_noNetwork() {
565         when(mockAccessPoint.getConfig()).thenReturn(null);
566 
567         mController = newWifiDetailPreferenceController();
568         displayAndResume();
569 
570         verify(mockForgetButton).setVisibility(View.INVISIBLE);
571     }
572 
573     @Test
canForgetNetwork_ephemeral()574     public void canForgetNetwork_ephemeral() {
575         when(mockWifiInfo.isEphemeral()).thenReturn(true);
576         when(mockAccessPoint.getConfig()).thenReturn(null);
577 
578         displayAndResume();
579 
580         verify(mockForgetButton).setVisibility(View.VISIBLE);
581     }
582 
583     @Test
canForgetNetwork_saved()584     public void canForgetNetwork_saved() {
585         displayAndResume();
586 
587         verify(mockForgetButton).setVisibility(View.VISIBLE);
588     }
589 
590     @Test
forgetNetwork_ephemeral()591     public void forgetNetwork_ephemeral() {
592         String ssid = "ssid";
593         when(mockWifiInfo.isEphemeral()).thenReturn(true);
594         when(mockWifiInfo.getSSID()).thenReturn(ssid);
595 
596         displayAndResume();
597         mForgetClickListener.getValue().onClick(null);
598 
599         verify(mockWifiManager).disableEphemeralNetwork(ssid);
600         verify(mockMetricsFeatureProvider)
601                 .action(mockActivity, MetricsProto.MetricsEvent.ACTION_WIFI_FORGET);
602     }
603 
604     @Test
forgetNetwork_saved()605     public void forgetNetwork_saved() {
606         mockWifiConfig.networkId = 5;
607 
608         mController.displayPreference(mockScreen);
609         mForgetClickListener.getValue().onClick(null);
610 
611         verify(mockWifiManager).forget(mockWifiConfig.networkId, null);
612         verify(mockMetricsFeatureProvider)
613                 .action(mockActivity, MetricsProto.MetricsEvent.ACTION_WIFI_FORGET);
614     }
615 
616     @Test
networkStateChangedIntent_shouldRefetchInfo()617     public void networkStateChangedIntent_shouldRefetchInfo() {
618         displayAndResume();
619 
620         verify(mockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
621         verify(mockWifiManager, times(1)).getConnectionInfo();
622 
623         mContext.sendBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
624 
625         verify(mockConnectivityManager, times(2)).getNetworkInfo(any(Network.class));
626         verify(mockWifiManager, times(2)).getConnectionInfo();
627     }
628 
629     @Test
rssiChangedIntent_shouldRefetchInfo()630     public void rssiChangedIntent_shouldRefetchInfo() {
631         displayAndResume();
632 
633         verify(mockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
634         verify(mockWifiManager, times(1)).getConnectionInfo();
635 
636         mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
637 
638         verify(mockConnectivityManager, times(2)).getNetworkInfo(any(Network.class));
639         verify(mockWifiManager, times(2)).getConnectionInfo();
640     }
641 
642     @Test
networkDisconnectedState_shouldFinishActivity()643     public void networkDisconnectedState_shouldFinishActivity() {
644         displayAndResume();
645 
646         when(mockConnectivityManager.getNetworkInfo(any(Network.class))).thenReturn(null);
647         mContext.sendBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
648 
649         verify(mockActivity).finish();
650     }
651 
652     @Test
networkOnLost_shouldFinishActivity()653     public void networkOnLost_shouldFinishActivity() {
654         displayAndResume();
655 
656         mCallbackCaptor.getValue().onLost(mockNetwork);
657 
658         verify(mockActivity).finish();
659     }
660 
661     @Test
ipv6AddressPref_shouldHaveHostAddressTextSet()662     public void ipv6AddressPref_shouldHaveHostAddressTextSet() {
663         mLinkProperties.addLinkAddress(Constants.IPV6_LINKLOCAL);
664         mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL1);
665         mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL2);
666 
667         displayAndResume();
668 
669         List <Preference> addrs = mIpv6AddressCaptor.getAllValues();
670 
671         String expectedAddresses = String.join("\n",
672                 asString(Constants.IPV6_LINKLOCAL),
673                 asString(Constants.IPV6_GLOBAL1),
674                 asString(Constants.IPV6_GLOBAL2));
675 
676         verify(mockIpv6AddressesPref).setSummary(expectedAddresses);
677     }
678 
679     @Test
ipv6AddressPref_shouldNotBeSelectable()680     public void ipv6AddressPref_shouldNotBeSelectable() {
681         mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL2);
682 
683         displayAndResume();
684 
685         assertThat(mockIpv6AddressesPref.isSelectable()).isFalse();
686     }
687 
688     @Test
captivePortal_shouldShowSignInButton()689     public void captivePortal_shouldShowSignInButton() {
690         InOrder inOrder = inOrder(mockSignInButton);
691 
692         displayAndResume();
693 
694         inOrder.verify(mockSignInButton).setVisibility(View.INVISIBLE);
695 
696         NetworkCapabilities nc = makeNetworkCapabilities();
697         updateNetworkCapabilities(nc);
698         inOrder.verify(mockSignInButton).setVisibility(View.INVISIBLE);
699 
700         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
701         updateNetworkCapabilities(nc);
702         inOrder.verify(mockSignInButton).setVisibility(View.VISIBLE);
703 
704         nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
705         updateNetworkCapabilities(nc);
706         inOrder.verify(mockSignInButton).setVisibility(View.INVISIBLE);
707     }
708 
709     @Test
testSignInButton_shouldStartCaptivePortalApp()710     public void testSignInButton_shouldStartCaptivePortalApp() {
711         displayAndResume();
712 
713         ArgumentCaptor<OnClickListener> captor = ArgumentCaptor.forClass(OnClickListener.class);
714         verify(mockSignInButton).setOnClickListener(captor.capture());
715         captor.getValue().onClick(mockSignInButton);
716         verify(mockConnectivityManagerWrapper).startCaptivePortalApp(mockNetwork);
717     }
718 
719     @Test
signInButtonVisible_buttonPanelShouldBeVisible()720     public void signInButtonVisible_buttonPanelShouldBeVisible() {
721         when(mockSignInButton.getVisibility()).thenReturn(View.VISIBLE);
722         when(mockForgetButton.getVisibility()).thenReturn(View.INVISIBLE);
723 
724         displayAndResume();
725 
726         verify(mockButtonsPref).setVisible(true);
727     }
728 
729     @Test
forgetButtonVisible_buttonPanelShouldBeVisible()730     public void forgetButtonVisible_buttonPanelShouldBeVisible() {
731         when(mockSignInButton.getVisibility()).thenReturn(View.INVISIBLE);
732         when(mockForgetButton.getVisibility()).thenReturn(View.VISIBLE);
733 
734         displayAndResume();
735 
736         verify(mockButtonsPref).setVisible(true);
737     }
738 
739     @Test
neitherButtonVisible_buttonPanelShouldBeInvisible()740     public void neitherButtonVisible_buttonPanelShouldBeInvisible() {
741         when(mockSignInButton.getVisibility()).thenReturn(View.INVISIBLE);
742         when(mockForgetButton.getVisibility()).thenReturn(View.INVISIBLE);
743 
744         displayAndResume();
745 
746         verify(mockButtonsPref).setVisible(false);
747     }
748 }
749