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.cts.verifier.wifi;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.net.wifi.ScanResult;
27 import android.net.wifi.SupplicantState;
28 import android.net.wifi.WifiInfo;
29 import android.net.wifi.WifiManager;
30 import android.text.TextUtils;
31 import android.util.Log;
32 
33 import com.android.cts.verifier.R;
34 
35 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy;
37 import java.util.List;
38 import java.util.Random;
39 import java.util.concurrent.CountDownLatch;
40 import java.util.concurrent.TimeUnit;
41 
42 /**
43  * Test utility methods.
44  */
45 public class TestUtils {
46     private static final String TAG = "NetworkRequestTestCase";
47     private static final boolean DBG = true;
48     private static final int SCAN_TIMEOUT_MS = 30_000;
49 
50     private final Context mContext;
51     protected BaseTestCase.Listener mListener;
52     private final WifiManager mWifiManager;
53 
TestUtils(Context context, BaseTestCase.Listener listener)54     public TestUtils(Context context, BaseTestCase.Listener listener) {
55         mContext = context;
56         mListener = listener;
57         mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
58     }
59 
startScanAndWaitForResults()60     private boolean startScanAndWaitForResults() throws InterruptedException {
61         IntentFilter intentFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
62         final CountDownLatch countDownLatch = new CountDownLatch(1);
63         // Scan Results available broadcast receiver.
64         BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
65             @Override
66             public void onReceive(Context context, Intent intent) {
67                 if (DBG) Log.v(TAG, "Broadcast onReceive " + intent);
68                 if (!intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) return;
69                 if (!intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)) return;
70                 if (DBG) Log.v(TAG, "Scan results received");
71                 countDownLatch.countDown();
72             }
73         };
74         // Register the receiver for scan results broadcast.
75         mContext.registerReceiver(broadcastReceiver, intentFilter);
76 
77         // Start scan.
78         if (DBG) Log.v(TAG, "Starting scan");
79         mListener.onTestMsgReceived(mContext.getString(R.string.wifi_status_initiating_scan));
80         if (!mWifiManager.startScan()) {
81             Log.e(TAG, "Failed to start scan");
82             // Unregister the receiver for scan results broadcast.
83             mContext.unregisterReceiver(broadcastReceiver);
84             return false;
85         }
86         // Wait for scan results.
87         if (DBG) Log.v(TAG, "Wait for scan results");
88         if (!countDownLatch.await(SCAN_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
89             Log.e(TAG, "No new scan results available");
90             // Unregister the receiver for scan results broadcast.
91             mContext.unregisterReceiver(broadcastReceiver);
92             return false;
93         }
94 
95         // Unregister the receiver for scan results broadcast.
96         mContext.unregisterReceiver(broadcastReceiver);
97         return true;
98     }
99 
100     public static final int SCAN_RESULT_TYPE_OPEN = 0;
101     public static final int SCAN_RESULT_TYPE_PSK = 1;
102 
103     @IntDef(prefix = { "SCAN_RESULT_TYPE_" }, value = {
104             SCAN_RESULT_TYPE_OPEN,
105             SCAN_RESULT_TYPE_PSK,
106     })
107     @Retention(RetentionPolicy.SOURCE)
108     public @interface ScanResultType {}
109 
110     /**
111      * Helper to check if the scan result corresponds to an open network.
112      */
isScanResultForOpenNetwork(@onNull ScanResult scanResult)113     public static boolean isScanResultForOpenNetwork(@NonNull ScanResult scanResult) {
114         String capabilities = scanResult.capabilities;
115         return !capabilities.contains("PSK") && !capabilities.contains("EAP")
116                 && !capabilities.contains("WEP") && !capabilities.contains("SAE")
117                 && !capabilities.contains("SUITE-B-192") && !capabilities.contains("OWE");
118     }
119 
120     /**
121      * Helper to check if the scan result corresponds to a WPA2 PSK network.
122      */
isScanResultForWpa2Network(@onNull ScanResult scanResult)123     public static boolean isScanResultForWpa2Network(@NonNull ScanResult scanResult) {
124         String capabilities = scanResult.capabilities;
125         return capabilities.contains("PSK");
126     }
127 
128     /**
129      * Helper to check if the scan result corresponds to a WPA3 PSK network.
130      */
isScanResultForWpa3Network(@onNull ScanResult scanResult)131     public static boolean isScanResultForWpa3Network(@NonNull ScanResult scanResult) {
132         String capabilities = scanResult.capabilities;
133         return capabilities.contains("SAE");
134     }
135 
doesScanResultMatchType( ScanResult scanResult, @ScanResultType int type)136     private static boolean doesScanResultMatchType(
137             ScanResult scanResult, @ScanResultType int type) {
138         switch(type) {
139             case SCAN_RESULT_TYPE_OPEN:
140                 return isScanResultForOpenNetwork(scanResult);
141             case SCAN_RESULT_TYPE_PSK:
142                 return isScanResultForWpa2Network(scanResult)
143                         || isScanResultForWpa3Network(scanResult);
144             default:
145                 return false;
146         }
147     }
148 
149     /**
150      * Helper method to start a scan and find any type of networks in the scan results returned by
151      * the device.
152      * @return ScanResult instance corresponding to an network of type if one exists, null if the
153      * scan failed or if there are networks found.
154      */
startScanAndFindAnyMatchingNetworkInResults( String ssid, @ScanResultType int type)155     public @Nullable ScanResult startScanAndFindAnyMatchingNetworkInResults(
156             String ssid, @ScanResultType int type)
157             throws InterruptedException {
158         // Start scan and wait for new results.
159         if (!startScanAndWaitForResults()) {
160             Log.e(TAG,"Failed to initiate a new scan. Using cached results from device");
161         }
162         // Filter results to find an open network.
163         List<ScanResult> scanResults = mWifiManager.getScanResults();
164         for (ScanResult scanResult : scanResults) {
165             if (TextUtils.equals(ssid, scanResult.SSID)
166                     && !TextUtils.isEmpty(scanResult.BSSID)
167                     && doesScanResultMatchType(scanResult, type)) {
168                 if (DBG) Log.v(TAG, "Found network " + scanResult);
169                 return scanResult;
170             }
171         }
172         Log.e(TAG, "No matching network found in scan results");
173         return null;
174     }
175 
176     /**
177      * Helper method to check if a scan result with the specified SSID & BSSID matches the scan
178      * results returned by the device.
179      *
180      * @param ssid SSID of the network.
181      * @param bssid BSSID of network.
182      * @return true if there is a match, false otherwise.
183      */
findNetworkInScanResultsResults(@onNull String ssid, @NonNull String bssid)184     public boolean findNetworkInScanResultsResults(@NonNull String ssid, @NonNull String bssid) {
185         List<ScanResult> scanResults = mWifiManager.getScanResults();
186         for (ScanResult scanResult : scanResults) {
187             if (TextUtils.equals(scanResult.SSID, ssid)
188                     && TextUtils.equals(scanResult.BSSID, bssid)) {
189                 if (DBG) Log.v(TAG, "Found network " + scanResult);
190                 return true;
191             }
192         }
193         return false;
194     }
195 
196     /**
197      * Checks whether the device is connected.
198      *
199      * @param ssid If ssid is specified, then check where the device is connected to a network
200      *             with the specified SSID.
201      * @param bssid If bssid is specified, then check where the device is connected to a network
202      *             with the specified BSSID.
203      * @return true if the device is connected to a network with the specified params, false
204      * otherwise.
205      */
isConnected(@ullable String ssid, @Nullable String bssid)206     public boolean isConnected(@Nullable String ssid, @Nullable String bssid) {
207         WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
208         if (wifiInfo == null) {
209             Log.e(TAG, "Failed to get WifiInfo");
210             return false;
211         }
212         if (wifiInfo.getSupplicantState() != SupplicantState.COMPLETED) return false;
213         if (ssid != null && !wifiInfo.getSSID().equals(ssid)) return false;
214         if (bssid != null && !wifiInfo.getBSSID().equals(bssid)) return false;
215         return true;
216     }
217 
218     /**
219      * Generate random passphrase to use for tests.
220      * @return
221      */
generateRandomPassphrase()222     public String generateRandomPassphrase() {
223         return new Random().ints('a', 'z' + 1)
224                 .limit(45)
225                 .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
226                 .toString();
227     }
228 }
229