1 /*
2  * Copyright (C) 2016 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.server.wifi.util;
18 
19 import android.net.wifi.ScanResult;
20 import android.net.wifi.WifiConfiguration;
21 
22 import com.android.internal.annotations.VisibleForTesting;
23 import com.android.server.wifi.ScanDetail;
24 import com.android.server.wifi.hotspot2.NetworkDetail;
25 
26 import java.io.PrintWriter;
27 import java.util.List;
28 /**
29  * Scan result utility for any {@link ScanResult} related operations.
30  * Currently contains:
31  *   > Helper method for converting a ScanResult to a ScanDetail.
32  *     Only fields that are supported in ScanResult are copied.
33  *   > Helper methods to identify the encryption of a ScanResult.
34  */
35 public class ScanResultUtil {
ScanResultUtil()36     private ScanResultUtil() { /* not constructable */ }
37 
38     /**
39      * This method should only be used when the informationElements field in the provided scan
40      * result is filled in with the IEs from the beacon.
41      */
toScanDetail(ScanResult scanResult)42     public static ScanDetail toScanDetail(ScanResult scanResult) {
43         NetworkDetail networkDetail = new NetworkDetail(scanResult.BSSID,
44                 scanResult.informationElements, scanResult.anqpLines, scanResult.frequency);
45         return new ScanDetail(scanResult, networkDetail);
46     }
47 
48     /**
49      * Helper method to check if the provided |scanResult| corresponds to a PSK network or not.
50      * This checks if the provided capabilities string contains PSK encryption type or not.
51      */
isScanResultForPskNetwork(ScanResult scanResult)52     public static boolean isScanResultForPskNetwork(ScanResult scanResult) {
53         return scanResult.capabilities.contains("PSK");
54     }
55 
56     /**
57      * Helper method to check if the provided |scanResult| corresponds to a WAPI-PSK network or not.
58      * This checks if the provided capabilities string contains PSK encryption type or not.
59      */
isScanResultForWapiPskNetwork(ScanResult scanResult)60     public static boolean isScanResultForWapiPskNetwork(ScanResult scanResult) {
61         return scanResult.capabilities.contains("WAPI-PSK");
62     }
63 
64     /**
65      * Helper method to check if the provided |scanResult| corresponds to a WAPI-CERT
66      * network or not.
67      * This checks if the provided capabilities string contains PSK encryption type or not.
68      */
isScanResultForWapiCertNetwork(ScanResult scanResult)69     public static boolean isScanResultForWapiCertNetwork(ScanResult scanResult) {
70         return scanResult.capabilities.contains("WAPI-CERT");
71     }
72 
73     /**
74      * Helper method to check if the provided |scanResult| corresponds to a EAP network or not.
75      * This checks if the provided capabilities string contains EAP encryption type or not.
76      */
isScanResultForEapNetwork(ScanResult scanResult)77     public static boolean isScanResultForEapNetwork(ScanResult scanResult) {
78         return scanResult.capabilities.contains("EAP");
79     }
80 
81     /**
82      * Helper method to check if the provided |scanResult| corresponds to a EAP network or not.
83      * This checks if the provided capabilities string contains EAP encryption type or not.
84      */
isScanResultForEapSuiteBNetwork(ScanResult scanResult)85     public static boolean isScanResultForEapSuiteBNetwork(ScanResult scanResult) {
86         return scanResult.capabilities.contains("SUITE-B-192");
87     }
88 
89     /**
90      * Helper method to check if the provided |scanResult| corresponds to a WEP network or not.
91      * This checks if the provided capabilities string contains WEP encryption type or not.
92      */
isScanResultForWepNetwork(ScanResult scanResult)93     public static boolean isScanResultForWepNetwork(ScanResult scanResult) {
94         return scanResult.capabilities.contains("WEP");
95     }
96 
97     /**
98      * Helper method to check if the provided |scanResult| corresponds to OWE network.
99      * This checks if the provided capabilities string contains OWE or not.
100      */
isScanResultForOweNetwork(ScanResult scanResult)101     public static boolean isScanResultForOweNetwork(ScanResult scanResult) {
102         return scanResult.capabilities.contains("OWE");
103     }
104 
105     /**
106      * Helper method to check if the provided |scanResult| corresponds to OWE transition network.
107      * This checks if the provided capabilities string contains OWE_TRANSITION or not.
108      */
isScanResultForOweTransitionNetwork(ScanResult scanResult)109     public static boolean isScanResultForOweTransitionNetwork(ScanResult scanResult) {
110         return scanResult.capabilities.contains("OWE_TRANSITION");
111     }
112 
113     /**
114      * Helper method to check if the provided |scanResult| corresponds to SAE network.
115      * This checks if the provided capabilities string contains SAE or not.
116      */
isScanResultForSaeNetwork(ScanResult scanResult)117     public static boolean isScanResultForSaeNetwork(ScanResult scanResult) {
118         return scanResult.capabilities.contains("SAE");
119     }
120 
121     /**
122      * Helper method to check if the provided |scanResult| corresponds to PSK-SAE transition
123      * network. This checks if the provided capabilities string contains both PSK and SAE or not.
124      */
isScanResultForPskSaeTransitionNetwork(ScanResult scanResult)125     public static boolean isScanResultForPskSaeTransitionNetwork(ScanResult scanResult) {
126         return scanResult.capabilities.contains("PSK") && scanResult.capabilities.contains("SAE");
127     }
128 
129     /**
130      * Helper method to check if the provided |scanResult| corresponds to FILS SHA256 network.
131      * This checks if the provided capabilities string contains FILS-SHA256 or not.
132      */
isScanResultForFilsSha256Network(ScanResult scanResult)133     public static boolean isScanResultForFilsSha256Network(ScanResult scanResult) {
134         return scanResult.capabilities.contains("FILS-SHA256");
135     }
136 
137     /**
138      * Helper method to check if the provided |scanResult| corresponds to FILS SHA384 network.
139      * This checks if the provided capabilities string contains FILS-SHA384 or not.
140      */
isScanResultForFilsSha384Network(ScanResult scanResult)141     public static boolean isScanResultForFilsSha384Network(ScanResult scanResult) {
142         return scanResult.capabilities.contains("FILS-SHA384");
143     }
144 
145     /**
146      * Helper method to check if the provided |scanResult| corresponds to an open network or not.
147      * This checks if the provided capabilities string does not contain either of WEP, PSK, SAE
148      * or EAP encryption types or not.
149      */
isScanResultForOpenNetwork(ScanResult scanResult)150     public static boolean isScanResultForOpenNetwork(ScanResult scanResult) {
151         return (!(isScanResultForWepNetwork(scanResult) || isScanResultForPskNetwork(scanResult)
152                 || isScanResultForEapNetwork(scanResult) || isScanResultForSaeNetwork(scanResult)
153                 || isScanResultForWapiPskNetwork(scanResult)
154                 || isScanResultForWapiCertNetwork(scanResult)
155                 || isScanResultForEapSuiteBNetwork(scanResult)));
156     }
157 
158     /**
159      * Helper method to quote the SSID in Scan result to use for comparing/filling SSID stored in
160      * WifiConfiguration object.
161      */
162     @VisibleForTesting
createQuotedSSID(String ssid)163     public static String createQuotedSSID(String ssid) {
164         return "\"" + ssid + "\"";
165     }
166 
167     /**
168      * Creates a network configuration object using the provided |scanResult|.
169      * This is used to create ephemeral network configurations.
170      */
createNetworkFromScanResult(ScanResult scanResult)171     public static WifiConfiguration createNetworkFromScanResult(ScanResult scanResult) {
172         WifiConfiguration config = new WifiConfiguration();
173         config.SSID = createQuotedSSID(scanResult.SSID);
174         setAllowedKeyManagementFromScanResult(scanResult, config);
175         return config;
176     }
177 
178     /**
179      * Sets the {@link WifiConfiguration#allowedKeyManagement} field on the given
180      * {@link WifiConfiguration} based on its corresponding {@link ScanResult}.
181      */
setAllowedKeyManagementFromScanResult(ScanResult scanResult, WifiConfiguration config)182     public static void setAllowedKeyManagementFromScanResult(ScanResult scanResult,
183             WifiConfiguration config) {
184         if (isScanResultForSaeNetwork(scanResult)) {
185             config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
186         } else if (isScanResultForWapiPskNetwork(scanResult)) {
187             config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_PSK);
188         } else if (isScanResultForWapiCertNetwork(scanResult)) {
189             config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_CERT);
190         } else if (isScanResultForPskNetwork(scanResult)) {
191             config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
192         } else if (isScanResultForEapSuiteBNetwork(scanResult)) {
193             config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
194         } else if (isScanResultForEapNetwork(scanResult)) {
195             config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
196         } else if (isScanResultForWepNetwork(scanResult)) {
197             config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
198         } else if (isScanResultForOweNetwork(scanResult)) {
199             config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
200         } else {
201             config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
202         }
203     }
204 
205     /**
206      * Dump the provided scan results list to |pw|.
207      */
dumpScanResults(PrintWriter pw, List<ScanResult> scanResults, long nowMs)208     public static void dumpScanResults(PrintWriter pw, List<ScanResult> scanResults, long nowMs) {
209         if (scanResults != null && scanResults.size() != 0) {
210             pw.println("    BSSID              Frequency      RSSI           Age(sec)     SSID "
211                     + "                                Flags");
212             for (ScanResult r : scanResults) {
213                 long timeStampMs = r.timestamp / 1000;
214                 String age;
215                 if (timeStampMs <= 0) {
216                     age = "___?___";
217                 } else if (nowMs < timeStampMs) {
218                     age = "  0.000";
219                 } else if (timeStampMs < nowMs - 1000000) {
220                     age = ">1000.0";
221                 } else {
222                     age = String.format("%3.3f", (nowMs - timeStampMs) / 1000.0);
223                 }
224                 String ssid = r.SSID == null ? "" : r.SSID;
225                 String rssiInfo = "";
226                 if (ArrayUtils.size(r.radioChainInfos) == 1) {
227                     rssiInfo = String.format("%5d(%1d:%3d)       ", r.level,
228                             r.radioChainInfos[0].id, r.radioChainInfos[0].level);
229                 } else if (ArrayUtils.size(r.radioChainInfos) == 2) {
230                     rssiInfo = String.format("%5d(%1d:%3d/%1d:%3d)", r.level,
231                             r.radioChainInfos[0].id, r.radioChainInfos[0].level,
232                             r.radioChainInfos[1].id, r.radioChainInfos[1].level);
233                 } else {
234                     rssiInfo = String.format("%9d         ", r.level);
235                 }
236                 pw.printf("  %17s  %9d  %18s   %7s    %-32s  %s\n",
237                         r.BSSID,
238                         r.frequency,
239                         rssiInfo,
240                         age,
241                         String.format("%1.32s", ssid),
242                         r.capabilities);
243             }
244         }
245     }
246 
247     /**
248      * Check if ScarResult list is valid.
249      */
validateScanResultList(List<ScanResult> scanResults)250     public static boolean validateScanResultList(List<ScanResult> scanResults) {
251         if (scanResults == null || scanResults.isEmpty()) {
252             return false;
253         }
254         for (ScanResult scanResult : scanResults) {
255             if (!validate(scanResult)) {
256                 return false;
257             }
258         }
259         return true;
260     }
261 
validate(ScanResult scanResult)262     private static boolean validate(ScanResult scanResult) {
263         return scanResult != null && scanResult.SSID != null
264                 && scanResult.capabilities != null && scanResult.BSSID != null;
265     }
266 }
267