1 /*
2  * Copyright (C) 2021 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 
17 package com.android.systemui.statusbar.connectivity;
18 
19 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
20 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
21 
22 import static junit.framework.Assert.assertEquals;
23 
24 import static org.junit.Assert.assertTrue;
25 import static org.mockito.ArgumentMatchers.anyLong;
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.when;
28 
29 import android.content.Intent;
30 import android.net.ConnectivityManager;
31 import android.net.Network;
32 import android.net.NetworkCapabilities;
33 import android.net.NetworkInfo;
34 import android.net.vcn.VcnTransportInfo;
35 import android.net.wifi.WifiInfo;
36 import android.net.wifi.WifiManager;
37 import android.testing.TestableLooper.RunWithLooper;
38 
39 import androidx.test.ext.junit.runners.AndroidJUnit4;
40 import androidx.test.filters.SmallTest;
41 
42 import com.android.settingslib.mobile.TelephonyIcons;
43 
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.ArgumentCaptor;
48 import org.mockito.Mockito;
49 
50 import java.util.Collections;
51 
52 @SmallTest
53 @RunWith(AndroidJUnit4.class)
54 @RunWithLooper
55 public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
56     // These match the constants in WifiManager and need to be kept up to date.
57     private static final int MIN_RSSI = -100;
58     private static final int MAX_RSSI = -55;
59     private WifiInfo mWifiInfo = mock(WifiInfo.class);
60     private VcnTransportInfo mVcnTransportInfo = mock(VcnTransportInfo.class);
61 
62     @Before
setUp()63     public void setUp() throws Exception {
64         super.setUp();
65         allowTestableLooperAsMainThread();
66         when(mWifiInfo.makeCopy(anyLong())).thenReturn(mWifiInfo);
67         when(mWifiInfo.isPrimary()).thenReturn(true);
68     }
69 
70     @Test
testWifiIcon()71     public void testWifiIcon() {
72         String testSsid = "Test SSID";
73         setWifiEnabled(true);
74         verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
75 
76         setWifiState(true, testSsid);
77         setWifiLevel(0);
78 
79         // Connected, but still not validated - does not show
80         verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
81 
82         for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
83             setWifiLevel(testLevel);
84 
85             setConnectivityViaCallbackInNetworkController(
86                     NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
87             verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
88             setConnectivityViaCallbackInNetworkController(
89                     NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
90             // Icon does not show if not validated
91             verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
92         }
93     }
94 
95     @Test
testQsWifiIcon()96     public void testQsWifiIcon() {
97         String testSsid = "Test SSID";
98 
99         setWifiEnabled(false);
100         verifyLastQsWifiIcon(false, false, WifiIcons.QS_WIFI_NO_NETWORK, null);
101 
102         setWifiEnabled(true);
103         verifyLastQsWifiIcon(true, false, WifiIcons.QS_WIFI_NO_NETWORK, null);
104 
105         setWifiState(true, testSsid);
106         for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
107             setWifiLevel(testLevel);
108             setConnectivityViaCallbackInNetworkController(
109                     NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
110             setConnectivityViaDefaultCallbackInWifiTracker(
111                     NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
112             verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel],
113                     testSsid);
114             setConnectivityViaCallbackInNetworkController(
115                     NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
116             verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel],
117                     testSsid);
118         }
119     }
120 
121     @Test
testQsDataDirection()122     public void testQsDataDirection() {
123         // Setup normal connection
124         String testSsid = "Test SSID";
125         int testLevel = 2;
126         setWifiEnabled(true);
127         setWifiState(true, testSsid);
128         setWifiLevel(testLevel);
129         setConnectivityViaCallbackInNetworkController(
130                 NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
131         setConnectivityViaDefaultCallbackInWifiTracker(
132                 NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
133         verifyLastQsWifiIcon(true, true,
134                 WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel], testSsid);
135 
136         // Set to different activity state first to ensure a callback happens.
137         setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN);
138 
139         setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE);
140         verifyLastQsDataDirection(false, false);
141         setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN);
142         verifyLastQsDataDirection(true, false);
143         setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT);
144         verifyLastQsDataDirection(false, true);
145         setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT);
146         verifyLastQsDataDirection(true, true);
147     }
148 
149     @Test
testRoamingIconDuringWifi()150     public void testRoamingIconDuringWifi() {
151         // Setup normal connection
152         String testSsid = "Test SSID";
153         int testLevel = 2;
154         setWifiEnabled(true);
155         setWifiState(true, testSsid);
156         setWifiLevel(testLevel);
157         setConnectivityViaCallbackInNetworkController(
158                 NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
159         verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
160 
161         setupDefaultSignal();
162         setGsmRoaming(true);
163         // Still be on wifi though.
164         setConnectivityViaCallbackInNetworkController(
165                 NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
166         setConnectivityViaCallbackInNetworkController(
167                 NetworkCapabilities.TRANSPORT_CELLULAR, false, false, null);
168         verifyLastMobileDataIndicators(true, DEFAULT_LEVEL, 0, true);
169     }
170 
171     @Test
testWifiIconInvalidatedViaCallback()172     public void testWifiIconInvalidatedViaCallback() {
173         // Setup normal connection
174         String testSsid = "Test SSID";
175         int testLevel = 2;
176         setWifiEnabled(true);
177         setWifiState(true, testSsid);
178         setWifiLevel(testLevel);
179         setConnectivityViaCallbackInNetworkController(
180                 NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
181         verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
182 
183         setConnectivityViaCallbackInNetworkController(
184                 NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
185         verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
186     }
187 
188     @Test
testWifiIconDisconnectedViaCallback()189     public void testWifiIconDisconnectedViaCallback() {
190         // Setup normal connection
191         String testSsid = "Test SSID";
192         int testLevel = 2;
193         setWifiEnabled(true);
194         setWifiState(true, testSsid);
195         setWifiLevel(testLevel);
196         setConnectivityViaCallbackInNetworkController(
197                 NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
198         verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
199 
200         setWifiState(false, testSsid);
201         setConnectivityViaCallbackInNetworkController(
202                 NetworkCapabilities.TRANSPORT_WIFI, false, false, mWifiInfo);
203         verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
204     }
205 
206     @Test
testVpnWithUnderlyingWifi()207     public void testVpnWithUnderlyingWifi() {
208         String testSsid = "Test SSID";
209         int testLevel = 2;
210         setWifiEnabled(true);
211         verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
212 
213         setConnectivityViaCallbackInNetworkController(
214                 NetworkCapabilities.TRANSPORT_VPN, false, true, mWifiInfo);
215         setConnectivityViaCallbackInNetworkController(
216                 NetworkCapabilities.TRANSPORT_VPN, true, true, mWifiInfo);
217         verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
218 
219         // Mock calling setUnderlyingNetworks.
220         setWifiState(true, testSsid);
221         setWifiLevel(testLevel);
222         setConnectivityViaCallbackInNetworkController(
223                 NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
224         verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
225     }
226 
227     @Test
testFetchInitialData()228     public void testFetchInitialData() {
229         mNetworkController.mWifiSignalController.fetchInitialState();
230         Mockito.verify(mMockWm).getWifiState();
231         Mockito.verify(mMockCm).getNetworkInfo(ConnectivityManager.TYPE_WIFI);
232     }
233 
234     @Test
testFetchInitialData_correctValues()235     public void testFetchInitialData_correctValues() {
236         String testSsid = "TEST";
237 
238         when(mMockWm.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED);
239         NetworkInfo networkInfo = mock(NetworkInfo.class);
240         when(networkInfo.isConnected()).thenReturn(true);
241         when(mMockCm.getNetworkInfo(ConnectivityManager.TYPE_WIFI)).thenReturn(networkInfo);
242         WifiInfo wifiInfo = mock(WifiInfo.class);
243         when(wifiInfo.getSSID()).thenReturn(testSsid);
244         when(mMockWm.getConnectionInfo()).thenReturn(wifiInfo);
245 
246         mNetworkController.mWifiSignalController.fetchInitialState();
247 
248         assertTrue(mNetworkController.mWifiSignalController.mCurrentState.enabled);
249         assertTrue(mNetworkController.mWifiSignalController.mCurrentState.connected);
250         assertEquals(testSsid, mNetworkController.mWifiSignalController.mCurrentState.ssid);
251     }
252 
253     @Test
testVcnWithUnderlyingWifi()254     public void testVcnWithUnderlyingWifi() {
255         String testSsid = "Test VCN SSID";
256         setWifiEnabled(true);
257         verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
258 
259         mNetworkController.setNoNetworksAvailable(false);
260         setWifiStateForVcn(true, testSsid);
261         setWifiLevelForVcn(0);
262         verifyLastMobileDataIndicatorsForVcn(true, 0, TelephonyIcons.ICON_CWF, false);
263 
264         mNetworkController.setNoNetworksAvailable(true);
265         for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
266             setWifiLevelForVcn(testLevel);
267 
268             setConnectivityViaCallbackInNetworkControllerForVcn(
269                     NetworkCapabilities.TRANSPORT_CELLULAR, true, true, mVcnTransportInfo);
270             verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, true);
271 
272             setConnectivityViaCallbackInNetworkControllerForVcn(
273                     NetworkCapabilities.TRANSPORT_CELLULAR, false, true, mVcnTransportInfo);
274             verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, false);
275         }
276     }
277 
278     /** Test for b/225902574. */
279     @Test
vcnOnlyOnUnderlyingNetwork()280     public void vcnOnlyOnUnderlyingNetwork() {
281         setWifiEnabled(true);
282 
283         // Set up a carrier merged network...
284         WifiInfo underlyingCarrierMergedInfo = Mockito.mock(WifiInfo.class);
285         when(underlyingCarrierMergedInfo.isCarrierMerged()).thenReturn(true);
286         when(underlyingCarrierMergedInfo.isPrimary()).thenReturn(true);
287         int zeroLevel = 0;
288         when(underlyingCarrierMergedInfo.getRssi()).thenReturn(calculateRssiForLevel(zeroLevel));
289 
290         NetworkCapabilities underlyingNetworkCapabilities = Mockito.mock(NetworkCapabilities.class);
291         when(underlyingNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
292                 .thenReturn(true);
293         when(underlyingNetworkCapabilities.getTransportInfo())
294                 .thenReturn(underlyingCarrierMergedInfo);
295 
296         Network underlyingNetwork = Mockito.mock(Network.class);
297         when(mMockCm.getNetworkCapabilities(underlyingNetwork))
298                 .thenReturn(underlyingNetworkCapabilities);
299 
300         NetworkCapabilities.Builder mainCapabilitiesBuilder = new NetworkCapabilities.Builder();
301         mainCapabilitiesBuilder.addTransportType(TRANSPORT_CELLULAR);
302         mainCapabilitiesBuilder.setTransportInfo(null);
303         // And make the carrier merged network the underlying network, *not* the main network.
304         mainCapabilitiesBuilder.setUnderlyingNetworks(Collections.singletonList(underlyingNetwork));
305 
306         Network primaryNetwork = Mockito.mock(Network.class);
307         int primaryNetworkId = 1;
308         when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
309 
310         // WHEN this primary network with underlying carrier merged information is sent
311         setConnectivityViaDefaultAndNormalCallbackInWifiTracker(
312                 primaryNetwork, mainCapabilitiesBuilder.build());
313 
314         // THEN we see the mobile data indicators for carrier merged
315         verifyLastMobileDataIndicatorsForVcn(
316                 /* visible= */ true,
317                 /* level= */ zeroLevel,
318                 TelephonyIcons.ICON_CWF,
319                 /* inet= */ false);
320 
321         // For each level...
322         for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
323             int rssi = calculateRssiForLevel(testLevel);
324             when(underlyingCarrierMergedInfo.getRssi()).thenReturn(rssi);
325             // WHEN the new level is sent to the callbacks
326             setConnectivityViaDefaultAndNormalCallbackInWifiTracker(
327                     primaryNetwork, mainCapabilitiesBuilder.build());
328 
329             // WHEN the network is validated
330             mainCapabilitiesBuilder.addCapability(NET_CAPABILITY_VALIDATED);
331             setConnectivityViaCallbackInNetworkController(
332                     primaryNetwork, mainCapabilitiesBuilder.build());
333 
334             // THEN we see the mobile data indicators with inet=true (no exclamation mark)
335             verifyLastMobileDataIndicatorsForVcn(
336                     /* visible= */ true,
337                     testLevel,
338                     TelephonyIcons.ICON_CWF,
339                     /* inet= */ true);
340 
341             // WHEN the network is not validated
342             mainCapabilitiesBuilder.removeCapability(NET_CAPABILITY_VALIDATED);
343             setConnectivityViaCallbackInNetworkController(
344                     primaryNetwork, mainCapabilitiesBuilder.build());
345 
346             // THEN we see the mobile data indicators with inet=false (exclamation mark)
347             verifyLastMobileDataIndicatorsForVcn(
348                     /* visible= */ true,
349                     testLevel,
350                     TelephonyIcons.ICON_CWF,
351                     /* inet= */ false);
352         }
353     }
354 
355     @Test
testDisableWiFiWithVcnWithUnderlyingWifi()356     public void testDisableWiFiWithVcnWithUnderlyingWifi() {
357         String testSsid = "Test VCN SSID";
358         setWifiEnabled(true);
359         verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
360 
361         mNetworkController.setNoNetworksAvailable(false);
362         setWifiStateForVcn(true, testSsid);
363         setWifiLevelForVcn(1);
364         verifyLastMobileDataIndicatorsForVcn(true, 1, TelephonyIcons.ICON_CWF, false);
365 
366         setWifiEnabled(false);
367         verifyLastMobileDataIndicatorsForVcn(false, 1, 0, false);
368     }
369 
setWifiActivity(int activity)370     protected void setWifiActivity(int activity) {
371         // TODO: Not this, because this variable probably isn't sticking around.
372         mNetworkController.mWifiSignalController.setActivity(activity);
373     }
374 
setWifiLevel(int level)375     protected void setWifiLevel(int level) {
376         when(mWifiInfo.getRssi()).thenReturn(calculateRssiForLevel(level));
377         setConnectivityViaCallbackInWifiTracker(
378                 NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
379     }
380 
setWifiEnabled(boolean enabled)381     protected void setWifiEnabled(boolean enabled) {
382         when(mMockWm.getWifiState()).thenReturn(
383                 enabled ? WifiManager.WIFI_STATE_ENABLED : WifiManager.WIFI_STATE_DISABLED);
384         mNetworkController.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
385     }
386 
setWifiState(boolean connected, String ssid)387     protected void setWifiState(boolean connected, String ssid) {
388         when(mWifiInfo.getSSID()).thenReturn(ssid);
389         setConnectivityViaCallbackInWifiTracker(
390                 NetworkCapabilities.TRANSPORT_WIFI, false, connected, mWifiInfo);
391     }
392 
setWifiLevelForVcn(int level)393     protected void setWifiLevelForVcn(int level) {
394         when(mVcnTransportInfo.getWifiInfo()).thenReturn(mWifiInfo);
395         when(mVcnTransportInfo.makeCopy(anyLong())).thenReturn(mVcnTransportInfo);
396         when(mWifiInfo.getRssi()).thenReturn(calculateRssiForLevel(level));
397         when(mWifiInfo.isCarrierMerged()).thenReturn(true);
398         when(mWifiInfo.getSubscriptionId()).thenReturn(1);
399         setConnectivityViaCallbackInWifiTrackerForVcn(
400                 NetworkCapabilities.TRANSPORT_CELLULAR, false, true, mVcnTransportInfo);
401     }
402 
calculateRssiForLevel(int level)403     private int calculateRssiForLevel(int level) {
404         float amountPerLevel = (MAX_RSSI - MIN_RSSI) / (WifiIcons.WIFI_LEVEL_COUNT - 1);
405         int rssi = (int) (MIN_RSSI + level * amountPerLevel);
406         // Put RSSI in the middle of the range.
407         rssi += amountPerLevel / 2;
408         return rssi;
409     }
410 
setWifiStateForVcn(boolean connected, String ssid)411     protected void setWifiStateForVcn(boolean connected, String ssid) {
412         when(mVcnTransportInfo.getWifiInfo()).thenReturn(mWifiInfo);
413         when(mVcnTransportInfo.makeCopy(anyLong())).thenReturn(mVcnTransportInfo);
414         when(mWifiInfo.getSSID()).thenReturn(ssid);
415         when(mWifiInfo.isCarrierMerged()).thenReturn(true);
416         when(mWifiInfo.getSubscriptionId()).thenReturn(1);
417         setConnectivityViaCallbackInWifiTrackerForVcn(
418                 NetworkCapabilities.TRANSPORT_CELLULAR, false, connected, mVcnTransportInfo);
419     }
420 
verifyLastQsDataDirection(boolean in, boolean out)421     protected void verifyLastQsDataDirection(boolean in, boolean out) {
422         ArgumentCaptor<WifiIndicators> indicatorsArg =
423                 ArgumentCaptor.forClass(WifiIndicators.class);
424 
425         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
426                 indicatorsArg.capture());
427         WifiIndicators expected = indicatorsArg.getValue();
428         assertEquals("WiFi data in, in quick settings", in, expected.activityIn);
429         assertEquals("WiFi data out, in quick settings", out, expected.activityOut);
430     }
431 
verifyLastQsWifiIcon(boolean enabled, boolean connected, int icon, String description)432     protected void verifyLastQsWifiIcon(boolean enabled, boolean connected, int icon,
433             String description) {
434         ArgumentCaptor<WifiIndicators> indicatorsArg =
435                 ArgumentCaptor.forClass(WifiIndicators.class);
436 
437         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
438                 indicatorsArg.capture());
439         WifiIndicators expected = indicatorsArg.getValue();
440         assertEquals("WiFi enabled, in quick settings", enabled, expected.enabled);
441         assertEquals("WiFI desc (ssid), in quick settings", description, expected.description);
442         if (enabled && connected) {
443             assertEquals("WiFi connected, in quick settings", connected, expected.qsIcon.visible);
444             assertEquals("WiFi signal, in quick settings", icon, expected.qsIcon.icon);
445         } else {
446             assertEquals("WiFi is not default", null, expected.qsIcon);
447         }
448     }
449 
verifyLastWifiIcon(boolean visible, int icon)450     protected void verifyLastWifiIcon(boolean visible, int icon) {
451         ArgumentCaptor<WifiIndicators> indicatorsArg =
452                 ArgumentCaptor.forClass(WifiIndicators.class);
453 
454         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
455                 indicatorsArg.capture());
456         WifiIndicators expected = indicatorsArg.getValue();
457         assertEquals("WiFi visible, in status bar", visible, expected.statusIcon.visible);
458         assertEquals("WiFi signal, in status bar", icon, expected.statusIcon.icon);
459     }
460 }
461