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