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