1 /*
2  * Copyright (C) 2008 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 android.net.wifi.cts;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 
22 import java.nio.ByteBuffer;
23 import java.util.List;
24 
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.net.wifi.ScanResult;
30 import android.net.wifi.ScanResult.InformationElement;
31 import android.net.wifi.WifiInfo;
32 import android.net.wifi.WifiManager;
33 import android.net.wifi.WifiManager.WifiLock;
34 import android.platform.test.annotations.AppModeFull;
35 import android.test.AndroidTestCase;
36 
37 import com.android.compatibility.common.util.PollingCheck;
38 import com.android.compatibility.common.util.ShellIdentityUtils;
39 import com.android.compatibility.common.util.SystemUtil;
40 
41 @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
42 public class ScanResultTest extends WifiJUnit3TestBase {
43     private static class MySync {
44         int expectedState = STATE_NULL;
45     }
46 
47     private WifiManager mWifiManager;
48     private WifiLock mWifiLock;
49     private static MySync mMySync;
50     private boolean mWasVerboseLoggingEnabled;
51     private boolean mWasScanThrottleEnabled;
52 
53     private static final int STATE_NULL = 0;
54     private static final int STATE_WIFI_CHANGING = 1;
55     private static final int STATE_WIFI_CHANGED = 2;
56     private static final int STATE_START_SCAN = 3;
57     private static final int STATE_SCAN_RESULTS_AVAILABLE = 4;
58     private static final int STATE_SCAN_FAILURE = 5;
59 
60     private static final String TAG = "WifiInfoTest";
61     private static final int TIMEOUT_MSEC = 6000;
62     private static final int WAIT_MSEC = 60;
63     private static final int ENABLE_WAIT_MSEC = 10000;
64     private static final int SCAN_WAIT_MSEC = 10000;
65     private static final int SCAN_MAX_RETRY_COUNT = 6;
66     private static final int SCAN_FIND_BSSID_MAX_RETRY_COUNT = 5;
67     private static final long SCAN_FIND_BSSID_WAIT_MSEC = 5_000L;
68     private static final int WIFI_CONNECT_TIMEOUT_MILLIS = 30_000;
69 
70     private static final String TEST_SSID = "TEST_SSID";
71     public static final String TEST_BSSID = "04:ac:fe:45:34:10";
72     public static final String TEST_CAPS = "CCMP";
73     public static final int TEST_LEVEL = -56;
74     public static final int TEST_FREQUENCY = 2412;
75     public static final long TEST_TIMESTAMP = 4660L;
76 
77     private IntentFilter mIntentFilter;
78     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
79         @Override
80         public void onReceive(Context context, Intent intent) {
81             final String action = intent.getAction();
82             if (action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
83                 synchronized (mMySync) {
84                     mMySync.expectedState = STATE_WIFI_CHANGED;
85                     mMySync.notify();
86                 }
87             } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
88                 synchronized (mMySync) {
89                     if (intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)) {
90                         mMySync.expectedState = STATE_SCAN_RESULTS_AVAILABLE;
91                     } else {
92                         mMySync.expectedState = STATE_SCAN_FAILURE;
93                     }
94                     mMySync.notify();
95                 }
96             }
97         }
98     };
99 
100     @Override
setUp()101     protected void setUp() throws Exception {
102         super.setUp();
103         if (!WifiFeature.isWifiSupported(getContext())) {
104             // skip the test if WiFi is not supported
105             return;
106         }
107         mMySync = new MySync();
108         mIntentFilter = new IntentFilter();
109         mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
110         mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
111         mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
112         mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
113         mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
114         mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
115         mIntentFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
116         mIntentFilter.addAction(WifiManager.ACTION_PICK_WIFI_NETWORK);
117 
118         mContext.registerReceiver(mReceiver, mIntentFilter);
119         mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
120         assertThat(mWifiManager).isNotNull();
121 
122         // turn on verbose logging for tests
123         mWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions(
124                 () -> mWifiManager.isVerboseLoggingEnabled());
125         ShellIdentityUtils.invokeWithShellPermissions(
126                 () -> mWifiManager.setVerboseLoggingEnabled(true));
127         // Disable scan throttling for tests.
128         mWasScanThrottleEnabled = ShellIdentityUtils.invokeWithShellPermissions(
129                 () -> mWifiManager.isScanThrottleEnabled());
130         ShellIdentityUtils.invokeWithShellPermissions(
131                 () -> mWifiManager.setScanThrottleEnabled(false));
132 
133         mWifiLock = mWifiManager.createWifiLock(TAG);
134         mWifiLock.acquire();
135 
136         // enable Wifi
137         if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
138         PollingCheck.check("Wifi not enabled", ENABLE_WAIT_MSEC,
139                 () -> mWifiManager.isWifiEnabled());
140 
141         mMySync.expectedState = STATE_NULL;
142     }
143 
144     @Override
tearDown()145     protected void tearDown() throws Exception {
146         if (!WifiFeature.isWifiSupported(getContext())) {
147             // skip the test if WiFi is not supported
148             super.tearDown();
149             return;
150         }
151         mWifiLock.release();
152         mContext.unregisterReceiver(mReceiver);
153         if (!mWifiManager.isWifiEnabled())
154             setWifiEnabled(true);
155         ShellIdentityUtils.invokeWithShellPermissions(
156                 () -> mWifiManager.setScanThrottleEnabled(mWasScanThrottleEnabled));
157         ShellIdentityUtils.invokeWithShellPermissions(
158                 () -> mWifiManager.setVerboseLoggingEnabled(mWasVerboseLoggingEnabled));
159         Thread.sleep(ENABLE_WAIT_MSEC);
160         super.tearDown();
161     }
162 
setWifiEnabled(boolean enable)163     private void setWifiEnabled(boolean enable) throws Exception {
164         synchronized (mMySync) {
165             mMySync.expectedState = STATE_WIFI_CHANGING;
166             if (enable) {
167                 SystemUtil.runShellCommand("svc wifi enable");
168             } else {
169                 SystemUtil.runShellCommand("svc wifi disable");
170             }
171             waitForBroadcast(TIMEOUT_MSEC, STATE_WIFI_CHANGED);
172        }
173     }
174 
waitForBroadcast(long timeout, int expectedState)175     private boolean waitForBroadcast(long timeout, int expectedState) throws Exception {
176         long waitTime = System.currentTimeMillis() + timeout;
177         while (System.currentTimeMillis() < waitTime
178                 && mMySync.expectedState != expectedState)
179             mMySync.wait(WAIT_MSEC);
180         return mMySync.expectedState == expectedState;
181     }
182 
testScanResultProperties()183     public void testScanResultProperties() {
184         if (!WifiFeature.isWifiSupported(getContext())) {
185             // skip the test if WiFi is not supported
186             return;
187         }
188         // this test case should in Wifi environment
189         for (ScanResult scanResult : mWifiManager.getScanResults()) {
190             assertThat(scanResult.toString()).isNotNull();
191 
192             for (InformationElement ie : scanResult.getInformationElements()) {
193                 testInformationElementCopyConstructor(ie);
194                 testInformationElementFields(ie);
195             }
196 
197             assertThat(scanResult.getWifiStandard()).isAnyOf(
198                     ScanResult.WIFI_STANDARD_UNKNOWN,
199                     ScanResult.WIFI_STANDARD_LEGACY,
200                     ScanResult.WIFI_STANDARD_11N,
201                     ScanResult.WIFI_STANDARD_11AC,
202                     ScanResult.WIFI_STANDARD_11AX
203             );
204 
205             scanResult.isPasspointNetwork();
206         }
207     }
208 
testInformationElementCopyConstructor(InformationElement ie)209     private void testInformationElementCopyConstructor(InformationElement ie) {
210         InformationElement copy = new InformationElement(ie);
211 
212         assertThat(copy.getId()).isEqualTo(ie.getId());
213         assertThat(copy.getIdExt()).isEqualTo(ie.getIdExt());
214         assertThat(copy.getBytes()).isEqualTo(ie.getBytes());
215     }
216 
testInformationElementFields(InformationElement ie)217     private void testInformationElementFields(InformationElement ie) {
218         // id is 1 octet
219         int id = ie.getId();
220         assertThat(id).isAtLeast(0);
221         assertThat(id).isAtMost(255);
222 
223         // idExt is 0 or 1 octet
224         int idExt = ie.getIdExt();
225         assertThat(idExt).isAtLeast(0);
226         assertThat(idExt).isAtMost(255);
227 
228         ByteBuffer bytes = ie.getBytes();
229         assertThat(bytes).isNotNull();
230     }
231 
232     /* Multiple scans to ensure bssid is updated */
scanAndWait()233     private void scanAndWait() throws Exception {
234         synchronized (mMySync) {
235             for (int retry  = 0; retry < SCAN_MAX_RETRY_COUNT; retry++) {
236                 mMySync.expectedState = STATE_START_SCAN;
237                 mWifiManager.startScan();
238                 if (waitForBroadcast(SCAN_WAIT_MSEC, STATE_SCAN_RESULTS_AVAILABLE)) {
239                     break;
240                 }
241             }
242         }
243    }
244 
245     @VirtualDeviceNotSupported
testScanResultTimeStamp()246     public void testScanResultTimeStamp() throws Exception {
247         if (!WifiFeature.isWifiSupported(getContext())) {
248             // skip the test if WiFi is not supported
249             return;
250         }
251 
252         long timestamp = 0;
253         String BSSID = null;
254 
255         scanAndWait();
256 
257         List<ScanResult> scanResults = mWifiManager.getScanResults();
258         for (ScanResult result : scanResults) {
259             BSSID = result.BSSID;
260             timestamp = result.timestamp;
261             assertThat(timestamp).isNotEqualTo(0);
262             break;
263         }
264 
265         scanAndWait();
266 
267         scanResults = mWifiManager.getScanResults();
268         for (ScanResult result : scanResults) {
269             if (result.BSSID.equals(BSSID)) {
270                 long timeDiff = (result.timestamp - timestamp) / 1000;
271                 assertThat(timeDiff).isGreaterThan(0L);
272                 assertThat(timeDiff).isLessThan(6L * SCAN_WAIT_MSEC);
273             }
274         }
275     }
276 
277     /** Test that the copy constructor copies fields correctly. */
testScanResultConstructors()278     public void testScanResultConstructors() throws Exception {
279         if (!WifiFeature.isWifiSupported(getContext())) {
280             // skip the test if WiFi is not supported
281             return;
282         }
283 
284         ScanResult scanResult = new ScanResult();
285         scanResult.SSID = TEST_SSID;
286         scanResult.BSSID = TEST_BSSID;
287         scanResult.capabilities = TEST_CAPS;
288         scanResult.level = TEST_LEVEL;
289         scanResult.frequency = TEST_FREQUENCY;
290         scanResult.timestamp = TEST_TIMESTAMP;
291 
292         ScanResult scanResult2 = new ScanResult(scanResult);
293         assertThat(scanResult2.SSID).isEqualTo(TEST_SSID);
294         assertThat(scanResult2.BSSID).isEqualTo(TEST_BSSID);
295         assertThat(scanResult2.capabilities).isEqualTo(TEST_CAPS);
296         assertThat(scanResult2.level).isEqualTo(TEST_LEVEL);
297         assertThat(scanResult2.frequency).isEqualTo(TEST_FREQUENCY);
298         assertThat(scanResult2.timestamp).isEqualTo(TEST_TIMESTAMP);
299     }
300 
testScanResultMatchesWifiInfo()301     public void testScanResultMatchesWifiInfo() throws Exception {
302         if (!WifiFeature.isWifiSupported(getContext())) {
303             // skip the test if WiFi is not supported
304             return;
305         }
306 
307         // ensure Wifi is connected
308         ShellIdentityUtils.invokeWithShellPermissions(() -> mWifiManager.reconnect());
309         PollingCheck.check(
310                 "Wifi not connected",
311                 WIFI_CONNECT_TIMEOUT_MILLIS,
312                 () -> mWifiManager.getConnectionInfo().getNetworkId() != -1);
313 
314         final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
315         assertThat(wifiInfo).isNotNull();
316 
317         ScanResult currentNetwork = null;
318         for (int i = 0; i < SCAN_FIND_BSSID_MAX_RETRY_COUNT; i++) {
319             scanAndWait();
320             final List<ScanResult> scanResults = mWifiManager.getScanResults();
321             currentNetwork = scanResults.stream().filter(r -> r.BSSID.equals(wifiInfo.getBSSID()))
322                     .findAny().orElse(null);
323 
324             if (currentNetwork != null) {
325                 break;
326             }
327             Thread.sleep(SCAN_FIND_BSSID_WAIT_MSEC);
328         }
329         assertWithMessage("Current network not found in scan results")
330                 .that(currentNetwork).isNotNull();
331 
332         String wifiInfoSsidQuoted = wifiInfo.getSSID();
333         String scanResultSsidUnquoted = currentNetwork.SSID;
334 
335         assertWithMessage(
336                 "SSID mismatch: make sure this isn't a hidden network or an SSID containing "
337                         + "non-UTF-8 characters - neither is supported by this CTS test.")
338                 .that("\"" + scanResultSsidUnquoted + "\"")
339                 .isEqualTo(wifiInfoSsidQuoted);
340         assertThat(currentNetwork.frequency).isEqualTo(wifiInfo.getFrequency());
341     }
342 }
343