• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.wifitrackerlib;
18 
19 import static android.net.wifi.WifiInfo.SECURITY_TYPE_PSK;
20 
21 import static com.android.wifitrackerlib.StandardWifiEntry.StandardWifiEntryKey;
22 import static com.android.wifitrackerlib.StandardWifiEntry.ssidAndSecurityTypeToStandardWifiEntryKey;
23 import static com.android.wifitrackerlib.TestUtils.BAD_LEVEL;
24 import static com.android.wifitrackerlib.TestUtils.BAD_RSSI;
25 import static com.android.wifitrackerlib.TestUtils.GOOD_RSSI;
26 import static com.android.wifitrackerlib.TestUtils.buildScanResult;
27 import static com.android.wifitrackerlib.TestUtils.buildWifiConfiguration;
28 import static com.android.wifitrackerlib.WifiEntry.SECURITY_NONE;
29 import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE;
30 
31 import static com.google.common.truth.Truth.assertThat;
32 
33 import static org.mockito.ArgumentMatchers.any;
34 import static org.mockito.Mockito.mock;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
37 
38 import android.content.BroadcastReceiver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.res.Resources;
42 import android.net.ConnectivityDiagnosticsManager;
43 import android.net.ConnectivityManager;
44 import android.net.wifi.ScanResult;
45 import android.net.wifi.WifiConfiguration;
46 import android.net.wifi.WifiInfo;
47 import android.net.wifi.WifiManager;
48 import android.net.wifi.WifiScanner;
49 import android.os.Handler;
50 import android.os.test.TestLooper;
51 
52 import androidx.lifecycle.Lifecycle;
53 
54 import org.junit.Before;
55 import org.junit.Test;
56 import org.mockito.ArgumentCaptor;
57 import org.mockito.Mock;
58 import org.mockito.MockitoAnnotations;
59 
60 import java.time.Clock;
61 import java.util.ArrayList;
62 import java.util.Collections;
63 
64 public class StandardNetworkDetailsTrackerTest {
65 
66     private static final long START_MILLIS = 123_456_789;
67 
68     private static final long MAX_SCAN_AGE_MILLIS = 15_000;
69     private static final long SCAN_INTERVAL_MILLIS = 10_000;
70 
71     @Mock private WifiTrackerInjector mInjector;
72     @Mock private Lifecycle mMockLifecycle;
73     @Mock private Context mMockContext;
74     @Mock private Resources mResources;
75     @Mock private WifiManager mMockWifiManager;
76     @Mock private WifiScanner mWifiScanner;
77     @Mock private ConnectivityManager mMockConnectivityManager;
78     @Mock private ConnectivityDiagnosticsManager mMockConnectivityDiagnosticsManager;
79     @Mock private Clock mMockClock;
80 
81     private TestLooper mTestLooper;
82 
83     private final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor =
84             ArgumentCaptor.forClass(BroadcastReceiver.class);
85 
createTestStandardNetworkDetailsTracker( String key)86     private StandardNetworkDetailsTracker createTestStandardNetworkDetailsTracker(
87             String key) {
88         final Handler testHandler = new Handler(mTestLooper.getLooper());
89 
90         return new StandardNetworkDetailsTracker(
91                 mInjector,
92                 mMockLifecycle,
93                 mMockContext,
94                 mMockWifiManager,
95                 mMockConnectivityManager,
96                 testHandler,
97                 testHandler,
98                 mMockClock,
99                 MAX_SCAN_AGE_MILLIS,
100                 SCAN_INTERVAL_MILLIS,
101                 key);
102     }
103 
104     @Before
setUp()105     public void setUp() {
106         MockitoAnnotations.initMocks(this);
107 
108         mTestLooper = new TestLooper();
109 
110         when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true);
111         when(mMockWifiManager.isWpa3SuiteBSupported()).thenReturn(true);
112         when(mMockWifiManager.isEnhancedOpenSupported()).thenReturn(true);
113         when(mMockWifiManager.getScanResults()).thenReturn(new ArrayList<>());
114         when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED);
115         when(mMockWifiManager.calculateSignalLevel(TestUtils.GOOD_RSSI))
116                 .thenReturn(TestUtils.GOOD_LEVEL);
117         when(mMockWifiManager.calculateSignalLevel(TestUtils.OKAY_RSSI))
118                 .thenReturn(TestUtils.OKAY_LEVEL);
119         when(mMockWifiManager.calculateSignalLevel(TestUtils.BAD_RSSI))
120                 .thenReturn(TestUtils.BAD_LEVEL);
121         when(mMockContext.getResources()).thenReturn(mResources);
122         when(mMockContext.getSystemService(ConnectivityDiagnosticsManager.class))
123                 .thenReturn(mMockConnectivityDiagnosticsManager);
124         when(mMockContext.getSystemService(WifiScanner.class)).thenReturn(mWifiScanner);
125         when(mMockClock.millis()).thenReturn(START_MILLIS);
126     }
127 
128     /**
129      * Tests that the key of the created WifiEntry matches the key passed into the constructor.
130      */
131     @Test
testGetWifiEntry_HasCorrectKey()132     public void testGetWifiEntry_HasCorrectKey() throws Exception {
133         final StandardWifiEntryKey key =
134                 ssidAndSecurityTypeToStandardWifiEntryKey("ssid", SECURITY_NONE);
135 
136         final StandardNetworkDetailsTracker tracker =
137                 createTestStandardNetworkDetailsTracker(key.toString());
138 
139         assertThat(tracker.getWifiEntry().getKey()).isEqualTo(key.toString());
140     }
141 
142     /**
143      * Tests that SCAN_RESULTS_AVAILABLE_ACTION updates the level of the entry.
144      */
145     @Test
testScanResultsAvailableAction_updates_getLevel()146     public void testScanResultsAvailableAction_updates_getLevel() throws Exception {
147         // Starting without any scans available should make level WIFI_LEVEL_UNREACHABLE
148         final ScanResult scan = buildScanResult("ssid", "bssid", START_MILLIS, -50 /* rssi */);
149         final StandardWifiEntryKey key =
150                 ssidAndSecurityTypeToStandardWifiEntryKey("ssid", SECURITY_NONE);
151         final StandardNetworkDetailsTracker tracker =
152                 createTestStandardNetworkDetailsTracker(key.toString());
153 
154         tracker.onStart();
155         mTestLooper.dispatchAll();
156         verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
157                 any(), any(), any());
158         final WifiEntry wifiEntry = tracker.getWifiEntry();
159 
160         assertThat(wifiEntry.getLevel()).isEqualTo(WIFI_LEVEL_UNREACHABLE);
161 
162         // Received fresh scan. Level should not be WIFI_LEVEL_UNREACHABLE anymore
163         when(mMockWifiManager.getScanResults()).thenReturn(Collections.singletonList(scan));
164 
165         mBroadcastReceiverCaptor.getValue().onReceive(mMockContext,
166                 new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
167                         .putExtra(WifiManager.EXTRA_RESULTS_UPDATED, true));
168 
169         assertThat(wifiEntry.getLevel()).isNotEqualTo(WIFI_LEVEL_UNREACHABLE);
170 
171         // Scan returned with no scans, old scans timed out. Level should be WIFI_LEVEL_UNREACHABLE.
172         when(mMockWifiManager.getScanResults()).thenReturn(Collections.emptyList());
173         when(mMockClock.millis()).thenReturn(START_MILLIS + MAX_SCAN_AGE_MILLIS + 1);
174 
175         mBroadcastReceiverCaptor.getValue().onReceive(mMockContext,
176                 new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
177                         .putExtra(WifiManager.EXTRA_RESULTS_UPDATED, true));
178 
179         assertThat(wifiEntry.getLevel()).isEqualTo(WIFI_LEVEL_UNREACHABLE);
180     }
181 
182     /**
183      * Tests that CONFIGURED_NETWORKS_CHANGED_ACTION updates the isSaved() value of the entry.
184      */
185     @Test
testConfiguredNetworksChangedAction_updates_isSaved()186     public void testConfiguredNetworksChangedAction_updates_isSaved() throws Exception {
187         // Initialize with no config. isSaved() should return false.
188         final StandardWifiEntryKey key =
189                 ssidAndSecurityTypeToStandardWifiEntryKey("ssid", SECURITY_NONE);
190         final StandardNetworkDetailsTracker tracker =
191                 createTestStandardNetworkDetailsTracker(key.toString());
192 
193         tracker.onStart();
194         mTestLooper.dispatchAll();
195         verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
196                 any(), any(), any());
197         final WifiEntry wifiEntry = tracker.getWifiEntry();
198 
199         assertThat(wifiEntry.isSaved()).isFalse();
200 
201         // Add a config and send a broadcast. isSaved() should return true.
202         final WifiConfiguration config = new WifiConfiguration();
203         config.SSID = "\"" + "ssid" + "\"";
204         when(mMockWifiManager.getPrivilegedConfiguredNetworks())
205                 .thenReturn(Collections.singletonList(config));
206         mBroadcastReceiverCaptor.getValue().onReceive(mMockContext,
207                 new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION));
208 
209         assertThat(wifiEntry.isSaved()).isTrue();
210 
211         // Remove the config and send a broadcast. isSaved() should be false.
212         when(mMockWifiManager.getPrivilegedConfiguredNetworks())
213                 .thenReturn(Collections.emptyList());
214         mBroadcastReceiverCaptor.getValue().onReceive(mMockContext,
215                 new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION));
216 
217         assertThat(wifiEntry.isSaved()).isFalse();
218     }
219 
220     /**
221      * Tests that WIFI_STATE_DISABLED will clear the scan results of the chosen entry regardless if
222      * the scan results are still valid.
223      */
224     @Test
testWifiStateChanged_disabled_clearsLevel()225     public void testWifiStateChanged_disabled_clearsLevel() throws Exception {
226         // Start with scan result and wifi state enabled. Level should not be unreachable.
227         final ScanResult scan = buildScanResult("ssid", "bssid", START_MILLIS, -50 /* rssi */);
228         final StandardWifiEntryKey key =
229                 ssidAndSecurityTypeToStandardWifiEntryKey("ssid", SECURITY_NONE);
230         when(mMockWifiManager.getScanResults()).thenReturn(Collections.singletonList(scan));
231 
232         final StandardNetworkDetailsTracker tracker =
233                 createTestStandardNetworkDetailsTracker(key.toString());
234         tracker.onStart();
235         mTestLooper.dispatchAll();
236         verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
237                 any(), any(), any());
238         final WifiEntry wifiEntry = tracker.getWifiEntry();
239 
240         assertThat(wifiEntry.getLevel()).isNotEqualTo(WIFI_LEVEL_UNREACHABLE);
241 
242         // Disable wifi. Level should be unreachable.
243         when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_DISABLED);
244 
245         mBroadcastReceiverCaptor.getValue().onReceive(mMockContext,
246                 new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
247 
248         assertThat(wifiEntry.getLevel()).isEqualTo(WIFI_LEVEL_UNREACHABLE);
249     }
250 
251     @Test
testSecurityTargeting_pskScansWithSaeConfig_correspondsToNewNetworkTargeting()252     public void testSecurityTargeting_pskScansWithSaeConfig_correspondsToNewNetworkTargeting() {
253         final String ssid = "ssid";
254         final WifiConfiguration config = buildWifiConfiguration(ssid);
255         config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
256         when(mMockWifiManager.getPrivilegedConfiguredNetworks())
257                 .thenReturn(Collections.singletonList(config));
258         final ScanResult scan = buildScanResult(ssid, "bssid", START_MILLIS, -50 /* rssi */);
259         scan.capabilities = "[PSK]";
260         when(mMockWifiManager.getScanResults()).thenReturn(Collections.singletonList(scan));
261 
262         // Start without targeting new networks
263         StandardNetworkDetailsTracker tracker = createTestStandardNetworkDetailsTracker(
264                 ssidAndSecurityTypeToStandardWifiEntryKey(ssid, SECURITY_TYPE_PSK).toString());
265         tracker.onStart();
266         mTestLooper.dispatchAll();
267 
268         // WifiEntry should correspond to the saved config
269         WifiEntry wifiEntry = tracker.getWifiEntry();
270         assertThat(wifiEntry.getSecurityTypes().get(0)).isEqualTo(WifiInfo.SECURITY_TYPE_SAE);
271         assertThat(wifiEntry.getLevel()).isEqualTo(WIFI_LEVEL_UNREACHABLE);
272 
273         // Now target new networks as if we got the key from WifiPickerTracker
274         tracker = createTestStandardNetworkDetailsTracker(
275                 ssidAndSecurityTypeToStandardWifiEntryKey(ssid, SECURITY_TYPE_PSK,
276                         true /* isTargetingNewNetworks */).toString());
277         tracker.onStart();
278         mTestLooper.dispatchAll();
279 
280         // WifiEntry should correspond to the unsaved scan
281         wifiEntry = tracker.getWifiEntry();
282         assertThat(wifiEntry.getSecurityTypes().get(0)).isEqualTo(SECURITY_TYPE_PSK);
283         assertThat(wifiEntry.getLevel()).isNotEqualTo(WIFI_LEVEL_UNREACHABLE);
284     }
285 
286     /**
287      * Tests that we update the chosen entry's ScanResults correctly after a WifiScanner scan.
288      */
289     @Test
testScanner_wifiScannerResultReceived_scanResultsUpdated()290     public void testScanner_wifiScannerResultReceived_scanResultsUpdated() {
291         final String ssid = "ssid";
292         final WifiConfiguration config = buildWifiConfiguration(ssid);
293         when(mMockWifiManager.getPrivilegedConfiguredNetworks())
294                 .thenReturn(Collections.singletonList(config));
295         StandardNetworkDetailsTracker tracker = createTestStandardNetworkDetailsTracker(
296                 ssidAndSecurityTypeToStandardWifiEntryKey(ssid, SECURITY_NONE).toString());
297         tracker.onStart();
298         mTestLooper.dispatchAll();
299         verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
300                 any(), any(), any());
301         mBroadcastReceiverCaptor.getValue().onReceive(mMockContext,
302                 new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION).putExtra(
303                         WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_ENABLED));
304         ArgumentCaptor<WifiScanner.ScanListener> mScanListenerCaptor =
305                 ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
306         verify(mWifiScanner).startScan(any(), mScanListenerCaptor.capture());
307         ScanResult[] scanResults = new ScanResult[]{
308                 buildScanResult(ssid, "bssid", START_MILLIS, BAD_RSSI),
309                 buildScanResult("different ssid", "bssid", START_MILLIS, GOOD_RSSI),
310         };
311         WifiScanner.ScanData scanData = mock(WifiScanner.ScanData.class);
312         when(scanData.getResults()).thenReturn(scanResults);
313 
314         mScanListenerCaptor.getValue().onResults(new WifiScanner.ScanData[]{scanData});
315         mTestLooper.dispatchAll();
316 
317         // Updated with the correct SSID and ignored the different SSID.
318         assertThat(tracker.getWifiEntry().getLevel()).isEqualTo(BAD_LEVEL);
319     }
320 }
321