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