1 /* 2 * Copyright (C) 2021 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.util; 18 19 import static android.net.wifi.ScanResult.FLAG_PASSPOINT_NETWORK; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.net.MacAddress; 24 import android.net.wifi.ScanResult; 25 import android.net.wifi.SecurityParams; 26 import android.net.wifi.WifiConfiguration; 27 import android.util.Log; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 31 import java.io.PrintWriter; 32 import java.util.ArrayList; 33 import java.util.List; 34 /** 35 * Scan result utility for any {@link ScanResult} related operations. 36 * Currently contains: 37 * > Helper methods to identify the encryption of a ScanResult. 38 * @hide 39 */ 40 public class ScanResultUtil { 41 private static final String TAG = "ScanResultUtil"; ScanResultUtil()42 private ScanResultUtil() { /* not constructable */ } 43 44 /** 45 * Helper method to check if the provided |scanResult| corresponds to a PSK network or not. 46 * This checks if the provided capabilities string contains PSK encryption type or not. 47 */ isScanResultForPskNetwork(@onNull ScanResult scanResult)48 public static boolean isScanResultForPskNetwork(@NonNull ScanResult scanResult) { 49 return scanResult.capabilities.contains("PSK"); 50 } 51 52 /** 53 * Helper method to check if the provided |scanResult| corresponds to a WAPI-PSK network or not. 54 * This checks if the provided capabilities string contains PSK encryption type or not. 55 */ isScanResultForWapiPskNetwork(@onNull ScanResult scanResult)56 public static boolean isScanResultForWapiPskNetwork(@NonNull ScanResult scanResult) { 57 return scanResult.capabilities.contains("WAPI-PSK"); 58 } 59 60 /** 61 * Helper method to check if the provided |scanResult| corresponds to a WAPI-CERT 62 * network or not. 63 * This checks if the provided capabilities string contains PSK encryption type or not. 64 */ isScanResultForWapiCertNetwork(@onNull ScanResult scanResult)65 public static boolean isScanResultForWapiCertNetwork(@NonNull ScanResult scanResult) { 66 return scanResult.capabilities.contains("WAPI-CERT"); 67 } 68 69 /** 70 * Helper method to check if the provided |scanResult| corresponds to a EAP network or not. 71 * This checks these conditions: 72 * - Enable EAP/SHA1, EAP/SHA256 AKM, FT/EAP, or EAP-FILS. 73 * - Not a WPA3 Enterprise only network. 74 * - Not a WPA3 Enterprise transition network. 75 */ isScanResultForEapNetwork(@onNull ScanResult scanResult)76 public static boolean isScanResultForEapNetwork(@NonNull ScanResult scanResult) { 77 return (scanResult.capabilities.contains("EAP/SHA1") 78 || scanResult.capabilities.contains("EAP/SHA256") 79 || scanResult.capabilities.contains("FT/EAP") 80 || scanResult.capabilities.contains("EAP-FILS")) 81 && !isScanResultForWpa3EnterpriseOnlyNetwork(scanResult) 82 && !isScanResultForWpa3EnterpriseTransitionNetwork(scanResult); 83 } 84 isScanResultForPmfMandatoryNetwork(@onNull ScanResult scanResult)85 private static boolean isScanResultForPmfMandatoryNetwork(@NonNull ScanResult scanResult) { 86 return scanResult.capabilities.contains("[MFPR]"); 87 } 88 isScanResultForPmfCapableNetwork(@onNull ScanResult scanResult)89 private static boolean isScanResultForPmfCapableNetwork(@NonNull ScanResult scanResult) { 90 return scanResult.capabilities.contains("[MFPC]"); 91 } 92 93 /** 94 * Helper method to check if the provided |scanResult| corresponds to a Passpoint R1/R2 95 * network or not. 96 * Passpoint R1/R2 requirements: 97 * - WPA2 Enterprise network. 98 * - interworking bit is set. 99 * - HotSpot Release presents. 100 */ isScanResultForPasspointR1R2Network(@onNull ScanResult scanResult)101 public static boolean isScanResultForPasspointR1R2Network(@NonNull ScanResult scanResult) { 102 if (!isScanResultForEapNetwork(scanResult)) return false; 103 104 return scanResult.isPasspointNetwork(); 105 } 106 107 /** 108 * Helper method to check if the provided |scanResult| corresponds to a Passpoint R3 109 * network or not. 110 * Passpoint R3 requirements: 111 * - Must be WPA2 Enterprise network, WPA3 Enterprise network, 112 * or WPA3 Enterprise 192-bit mode network. 113 * - interworking bit is set. 114 * - HotSpot Release presents. 115 * - PMF is mandatory. 116 */ isScanResultForPasspointR3Network(@onNull ScanResult scanResult)117 public static boolean isScanResultForPasspointR3Network(@NonNull ScanResult scanResult) { 118 if (!isScanResultForEapNetwork(scanResult) 119 && !isScanResultForWpa3EnterpriseOnlyNetwork(scanResult) 120 && !isScanResultForEapSuiteBNetwork(scanResult)) { 121 return false; 122 } 123 if (!isScanResultForPmfMandatoryNetwork(scanResult)) return false; 124 125 return scanResult.isPasspointNetwork(); 126 } 127 128 /** 129 * Helper method to check if the provided |scanResult| corresponds to 130 * a WPA3 Enterprise transition network or not. 131 * 132 * See Section 3.3 WPA3-Enterprise transition mode in WPA3 Specification 133 * - Enable at least EAP/SHA1 and EAP/SHA256 AKM suites. 134 * - Not enable WPA1 version 1, WEP, and TKIP. 135 * - Management Frame Protection Capable is set. 136 * - Management Frame Protection Required is not set. 137 */ isScanResultForWpa3EnterpriseTransitionNetwork( @onNull ScanResult scanResult)138 public static boolean isScanResultForWpa3EnterpriseTransitionNetwork( 139 @NonNull ScanResult scanResult) { 140 return scanResult.capabilities.contains("EAP/SHA1") 141 && scanResult.capabilities.contains("EAP/SHA256") 142 && scanResult.capabilities.contains("RSN") 143 && !scanResult.capabilities.contains("WEP") 144 && !scanResult.capabilities.contains("TKIP") 145 && !isScanResultForPmfMandatoryNetwork(scanResult) 146 && isScanResultForPmfCapableNetwork(scanResult); 147 } 148 149 /** 150 * Helper method to check if the provided |scanResult| corresponds to 151 * a WPA3 Enterprise only network or not. 152 * 153 * See Section 3.2 WPA3-Enterprise only mode in WPA3 Specification 154 * - Enable at least EAP/SHA256 AKM suite. 155 * - Not enable EAP/SHA1 AKM suite. 156 * - Not enable WPA1 version 1, WEP, and TKIP. 157 * - Management Frame Protection Capable is set. 158 * - Management Frame Protection Required is set. 159 */ isScanResultForWpa3EnterpriseOnlyNetwork(@onNull ScanResult scanResult)160 public static boolean isScanResultForWpa3EnterpriseOnlyNetwork(@NonNull ScanResult scanResult) { 161 return scanResult.capabilities.contains("EAP/SHA256") 162 && !scanResult.capabilities.contains("EAP/SHA1") 163 && scanResult.capabilities.contains("RSN") 164 && !scanResult.capabilities.contains("WEP") 165 && !scanResult.capabilities.contains("TKIP") 166 && isScanResultForPmfMandatoryNetwork(scanResult) 167 && isScanResultForPmfCapableNetwork(scanResult); 168 } 169 170 /** 171 * Helper method to check if the provided |scanResult| corresponds to a WPA3-Enterprise 192-bit 172 * mode network or not. 173 * This checks if the provided capabilities comply these conditions: 174 * - Enable SUITE-B-192 AKM. 175 * - Not enable EAP/SHA1 AKM suite. 176 * - Not enable WPA1 version 1, WEP, and TKIP. 177 * - Management Frame Protection Required is set. 178 */ isScanResultForEapSuiteBNetwork(@onNull ScanResult scanResult)179 public static boolean isScanResultForEapSuiteBNetwork(@NonNull ScanResult scanResult) { 180 return scanResult.capabilities.contains("SUITE_B_192") 181 && scanResult.capabilities.contains("RSN") 182 && !scanResult.capabilities.contains("WEP") 183 && !scanResult.capabilities.contains("TKIP") 184 && isScanResultForPmfMandatoryNetwork(scanResult); 185 } 186 187 /** 188 * Helper method to check if the provided |scanResult| corresponds to a WEP network or not. 189 * This checks if the provided capabilities string contains WEP encryption type or not. 190 */ isScanResultForWepNetwork(@onNull ScanResult scanResult)191 public static boolean isScanResultForWepNetwork(@NonNull ScanResult scanResult) { 192 return scanResult.capabilities.contains("WEP"); 193 } 194 195 /** 196 * Helper method to check if the provided |scanResult| corresponds to OWE network. 197 * This checks if the provided capabilities string contains OWE or not. 198 */ isScanResultForOweNetwork(@onNull ScanResult scanResult)199 public static boolean isScanResultForOweNetwork(@NonNull ScanResult scanResult) { 200 return scanResult.capabilities.contains("OWE"); 201 } 202 203 /** 204 * Helper method to check if the provided |scanResult| corresponds to OWE transition network. 205 * This checks if the provided capabilities string contains OWE_TRANSITION or not. 206 */ isScanResultForOweTransitionNetwork(@onNull ScanResult scanResult)207 public static boolean isScanResultForOweTransitionNetwork(@NonNull ScanResult scanResult) { 208 return scanResult.capabilities.contains("OWE_TRANSITION"); 209 } 210 211 /** 212 * Helper method to check if the provided |scanResult| corresponds to SAE network. 213 * This checks if the provided capabilities string contains SAE or not. 214 */ isScanResultForSaeNetwork(@onNull ScanResult scanResult)215 public static boolean isScanResultForSaeNetwork(@NonNull ScanResult scanResult) { 216 return scanResult.capabilities.contains("SAE"); 217 } 218 219 /** 220 * Helper method to check if the provided |scanResult| corresponds to PSK-SAE transition 221 * network. This checks if the provided capabilities string contains both PSK and SAE or not. 222 */ isScanResultForPskSaeTransitionNetwork(@onNull ScanResult scanResult)223 public static boolean isScanResultForPskSaeTransitionNetwork(@NonNull ScanResult scanResult) { 224 return scanResult.capabilities.contains("PSK") && scanResult.capabilities.contains("SAE"); 225 } 226 227 /** 228 * Helper method to check if the provided |scanResult| corresponds to FILS SHA256 network. 229 * This checks if the provided capabilities string contains FILS-SHA256 or not. 230 */ isScanResultForFilsSha256Network(@onNull ScanResult scanResult)231 public static boolean isScanResultForFilsSha256Network(@NonNull ScanResult scanResult) { 232 return scanResult.capabilities.contains("FILS-SHA256"); 233 } 234 235 /** 236 * Helper method to check if the provided |scanResult| corresponds to FILS SHA384 network. 237 * This checks if the provided capabilities string contains FILS-SHA384 or not. 238 */ isScanResultForFilsSha384Network(@onNull ScanResult scanResult)239 public static boolean isScanResultForFilsSha384Network(@NonNull ScanResult scanResult) { 240 return scanResult.capabilities.contains("FILS-SHA384"); 241 } 242 243 /** 244 * Helper method to check if the provided |scanResult| corresponds to DPP network. 245 * This checks if the provided capabilities string contains DPP or not. 246 */ isScanResultForDppNetwork(@onNull ScanResult scanResult)247 public static boolean isScanResultForDppNetwork(@NonNull ScanResult scanResult) { 248 return scanResult.capabilities.contains("DPP"); 249 } 250 251 /** 252 * Helper method to check if the provided |scanResult| corresponds to only WPA-Personal network. 253 * This checks if the provided capabilities string contains WPA and not RSN. 254 */ isScanResultForWpaPersonalOnlyNetwork(@onNull ScanResult scanResult)255 public static boolean isScanResultForWpaPersonalOnlyNetwork(@NonNull ScanResult scanResult) { 256 return isScanResultForPskNetwork(scanResult) && !scanResult.capabilities.contains("RSN"); 257 } 258 259 /** 260 * Helper method to check if the provided |scanResult| corresponds to an unknown amk network. 261 * This checks if the provided capabilities string contains ? or not. 262 */ isScanResultForUnknownAkmNetwork(@onNull ScanResult scanResult)263 public static boolean isScanResultForUnknownAkmNetwork(@NonNull ScanResult scanResult) { 264 return scanResult.capabilities.contains("?"); 265 } 266 267 /** 268 * Helper method to check if the provided |scanResult| corresponds to a pure PSK network. 269 */ isScanResultForPskOnlyNetwork(@onNull ScanResult r)270 public static boolean isScanResultForPskOnlyNetwork(@NonNull ScanResult r) { 271 return ScanResultUtil.isScanResultForPskNetwork(r) 272 && !ScanResultUtil.isScanResultForSaeNetwork(r); 273 } 274 275 /** 276 * Helper method to check if the provided |scanResult| corresponds to a pure SAE network. 277 */ isScanResultForSaeOnlyNetwork(@onNull ScanResult r)278 public static boolean isScanResultForSaeOnlyNetwork(@NonNull ScanResult r) { 279 return !ScanResultUtil.isScanResultForPskNetwork(r) 280 && ScanResultUtil.isScanResultForSaeNetwork(r); 281 } 282 283 /** 284 * Helper method to check if the provided |scanResult| corresponds to a pure OPEN network. 285 */ isScanResultForOpenOnlyNetwork(@onNull ScanResult r)286 public static boolean isScanResultForOpenOnlyNetwork(@NonNull ScanResult r) { 287 return ScanResultUtil.isScanResultForOpenNetwork(r) 288 && !ScanResultUtil.isScanResultForOweNetwork(r); 289 } 290 291 /** 292 * Helper method to check if the provided |scanResult| corresponds to a pure OWE network. 293 */ isScanResultForOweOnlyNetwork(@onNull ScanResult r)294 public static boolean isScanResultForOweOnlyNetwork(@NonNull ScanResult r) { 295 return !ScanResultUtil.isScanResultForOweTransitionNetwork(r) 296 && ScanResultUtil.isScanResultForOweNetwork(r); 297 } 298 299 /** 300 * Helper method to check if the provided |scanResult| corresponds to a pure 301 * WPA2 Enterprise network. 302 */ isScanResultForWpa2EnterpriseOnlyNetwork(@onNull ScanResult r)303 public static boolean isScanResultForWpa2EnterpriseOnlyNetwork(@NonNull ScanResult r) { 304 return ScanResultUtil.isScanResultForEapNetwork(r) 305 && !ScanResultUtil.isScanResultForWpa3EnterpriseTransitionNetwork(r) 306 && !ScanResultUtil.isScanResultForWpa3EnterpriseOnlyNetwork(r); 307 } 308 309 /** 310 * Helper method to check if the provided |scanResult| corresponds to an open network or not. 311 * This checks if the provided capabilities string does not contain either of WEP, PSK, SAE 312 * EAP, or unknown encryption types or not. 313 */ isScanResultForOpenNetwork(@onNull ScanResult scanResult)314 public static boolean isScanResultForOpenNetwork(@NonNull ScanResult scanResult) { 315 return (!(isScanResultForWepNetwork(scanResult) || isScanResultForPskNetwork(scanResult) 316 || isScanResultForEapNetwork(scanResult) || isScanResultForSaeNetwork(scanResult) 317 || isScanResultForWpa3EnterpriseTransitionNetwork(scanResult) 318 || isScanResultForWpa3EnterpriseOnlyNetwork(scanResult) 319 || isScanResultForWapiPskNetwork(scanResult) 320 || isScanResultForWapiCertNetwork(scanResult) 321 || isScanResultForEapSuiteBNetwork(scanResult) 322 || isScanResultForDppNetwork(scanResult) 323 || isScanResultForUnknownAkmNetwork(scanResult))); 324 } 325 326 /** 327 * Helper method to quote the SSID in Scan result to use for comparing/filling SSID stored in 328 * WifiConfiguration object. 329 */ 330 @VisibleForTesting createQuotedSsid(@ullable String ssid)331 public static @NonNull String createQuotedSsid(@Nullable String ssid) { 332 return "\"" + ssid + "\""; 333 } 334 335 /** 336 * Creates a network configuration object using the provided |scanResult|. 337 */ createNetworkFromScanResult( @onNull ScanResult scanResult)338 public static @Nullable WifiConfiguration createNetworkFromScanResult( 339 @NonNull ScanResult scanResult) { 340 WifiConfiguration config = new WifiConfiguration(); 341 config.SSID = createQuotedSsid(scanResult.SSID); 342 List<SecurityParams> list = generateSecurityParamsListFromScanResult(scanResult); 343 if (list.isEmpty()) { 344 return null; 345 } 346 config.setSecurityParams(list); 347 return config; 348 } 349 350 /** 351 * Generate security params from the scan result. 352 * @param scanResult the scan result to be checked. 353 * @return a list of security params. If no known security params, return an empty list. 354 */ generateSecurityParamsListFromScanResult( @onNull ScanResult scanResult)355 public static @NonNull List<SecurityParams> generateSecurityParamsListFromScanResult( 356 @NonNull ScanResult scanResult) { 357 List<SecurityParams> list = new ArrayList<>(); 358 359 // Open network & its upgradable types 360 if (ScanResultUtil.isScanResultForOweTransitionNetwork(scanResult)) { 361 list.add(SecurityParams.createSecurityParamsBySecurityType( 362 WifiConfiguration.SECURITY_TYPE_OPEN)); 363 list.add(SecurityParams.createSecurityParamsBySecurityType( 364 WifiConfiguration.SECURITY_TYPE_OWE)); 365 return list; 366 } else if (ScanResultUtil.isScanResultForOweNetwork(scanResult)) { 367 list.add(SecurityParams.createSecurityParamsBySecurityType( 368 WifiConfiguration.SECURITY_TYPE_OWE)); 369 return list; 370 } else if (ScanResultUtil.isScanResultForOpenNetwork(scanResult)) { 371 list.add(SecurityParams.createSecurityParamsBySecurityType( 372 WifiConfiguration.SECURITY_TYPE_OPEN)); 373 return list; 374 } 375 376 // WEP network which has no upgradable type 377 if (ScanResultUtil.isScanResultForWepNetwork(scanResult)) { 378 list.add(SecurityParams.createSecurityParamsBySecurityType( 379 WifiConfiguration.SECURITY_TYPE_WEP)); 380 return list; 381 } 382 383 // WAPI PSK network which has no upgradable type 384 if (ScanResultUtil.isScanResultForWapiPskNetwork(scanResult)) { 385 list.add(SecurityParams.createSecurityParamsBySecurityType( 386 WifiConfiguration.SECURITY_TYPE_WAPI_PSK)); 387 return list; 388 } 389 390 // WAPI CERT network which has no upgradable type 391 if (ScanResultUtil.isScanResultForWapiCertNetwork(scanResult)) { 392 list.add(SecurityParams.createSecurityParamsBySecurityType( 393 WifiConfiguration.SECURITY_TYPE_WAPI_CERT)); 394 return list; 395 } 396 397 // WPA2 personal network & its upgradable types 398 if (ScanResultUtil.isScanResultForPskNetwork(scanResult) 399 && ScanResultUtil.isScanResultForSaeNetwork(scanResult)) { 400 list.add(SecurityParams.createSecurityParamsBySecurityType( 401 WifiConfiguration.SECURITY_TYPE_PSK)); 402 list.add(SecurityParams.createSecurityParamsBySecurityType( 403 WifiConfiguration.SECURITY_TYPE_SAE)); 404 return list; 405 } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) { 406 list.add(SecurityParams.createSecurityParamsBySecurityType( 407 WifiConfiguration.SECURITY_TYPE_PSK)); 408 return list; 409 } else if (ScanResultUtil.isScanResultForSaeNetwork(scanResult)) { 410 list.add(SecurityParams.createSecurityParamsBySecurityType( 411 WifiConfiguration.SECURITY_TYPE_SAE)); 412 return list; 413 } else if (ScanResultUtil.isScanResultForDppNetwork(scanResult)) { 414 list.add(SecurityParams.createSecurityParamsBySecurityType( 415 WifiConfiguration.SECURITY_TYPE_DPP)); 416 return list; 417 } 418 419 // WPA3 Enterprise 192-bit mode, WPA2/WPA3 enterprise network & its upgradable types 420 if (ScanResultUtil.isScanResultForEapSuiteBNetwork(scanResult)) { 421 list.add(SecurityParams.createSecurityParamsBySecurityType( 422 WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT)); 423 } else if (ScanResultUtil.isScanResultForWpa3EnterpriseTransitionNetwork(scanResult)) { 424 list.add(SecurityParams.createSecurityParamsBySecurityType( 425 WifiConfiguration.SECURITY_TYPE_EAP)); 426 list.add(SecurityParams.createSecurityParamsBySecurityType( 427 WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)); 428 } else if (ScanResultUtil.isScanResultForWpa3EnterpriseOnlyNetwork(scanResult)) { 429 list.add(SecurityParams.createSecurityParamsBySecurityType( 430 WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)); 431 } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) { 432 list.add(SecurityParams.createSecurityParamsBySecurityType( 433 WifiConfiguration.SECURITY_TYPE_EAP)); 434 } 435 // An Enterprise network might be a Passpoint network as well. 436 // R3 network might be also a valid R1/R2 network. 437 if (isScanResultForPasspointR1R2Network(scanResult)) { 438 list.add(SecurityParams.createSecurityParamsBySecurityType( 439 WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2)); 440 } 441 if (isScanResultForPasspointR3Network(scanResult)) { 442 list.add(SecurityParams.createSecurityParamsBySecurityType( 443 WifiConfiguration.SECURITY_TYPE_PASSPOINT_R3)); 444 } 445 return list; 446 } 447 448 /** 449 * Dump the provided scan results list to |pw|. 450 */ dumpScanResults(@onNull PrintWriter pw, @Nullable List<ScanResult> scanResults, long nowMs)451 public static void dumpScanResults(@NonNull PrintWriter pw, 452 @Nullable List<ScanResult> scanResults, long nowMs) { 453 if (scanResults != null && scanResults.size() != 0) { 454 pw.println(" BSSID Frequency RSSI Age(sec) SSID " 455 + " Flags"); 456 for (ScanResult r : scanResults) { 457 long timeStampMs = r.timestamp / 1000; 458 String age; 459 if (timeStampMs <= 0) { 460 age = "___?___"; 461 } else if (nowMs < timeStampMs) { 462 age = " 0.000"; 463 } else if (timeStampMs < nowMs - 1000000) { 464 age = ">1000.0"; 465 } else { 466 age = String.format("%3.3f", (nowMs - timeStampMs) / 1000.0); 467 } 468 String ssid = r.SSID == null ? "" : r.SSID; 469 String rssiInfo = ""; 470 int numRadioChainInfos = r.radioChainInfos == null ? 0 : r.radioChainInfos.length; 471 if (numRadioChainInfos == 1) { 472 rssiInfo = String.format("%5d(%1d:%3d) ", r.level, 473 r.radioChainInfos[0].id, r.radioChainInfos[0].level); 474 } else if (numRadioChainInfos == 2) { 475 rssiInfo = String.format("%5d(%1d:%3d/%1d:%3d)", r.level, 476 r.radioChainInfos[0].id, r.radioChainInfos[0].level, 477 r.radioChainInfos[1].id, r.radioChainInfos[1].level); 478 } else { 479 rssiInfo = String.format("%9d ", r.level); 480 } 481 if ((r.flags & FLAG_PASSPOINT_NETWORK) 482 == FLAG_PASSPOINT_NETWORK) { 483 r.capabilities += "[PASSPOINT]"; 484 } 485 pw.printf(" %17s %9d %18s %7s %-32s %s\n", 486 r.BSSID, 487 r.frequency, 488 rssiInfo, 489 age, 490 String.format("%1.32s", ssid), 491 r.capabilities); 492 } 493 } 494 } 495 496 /** 497 * Check if ScanResult list is valid. 498 */ validateScanResultList(@ullable List<ScanResult> scanResults)499 public static boolean validateScanResultList(@Nullable List<ScanResult> scanResults) { 500 if (scanResults == null || scanResults.isEmpty()) { 501 Log.w(TAG, "Empty or null ScanResult list"); 502 return false; 503 } 504 for (ScanResult scanResult : scanResults) { 505 if (!validate(scanResult)) { 506 Log.w(TAG, "Invalid ScanResult: " + scanResult); 507 return false; 508 } 509 } 510 return true; 511 } 512 validate(@ullable ScanResult scanResult)513 private static boolean validate(@Nullable ScanResult scanResult) { 514 return scanResult != null && scanResult.SSID != null 515 && scanResult.capabilities != null && scanResult.BSSID != null; 516 } 517 518 /** 519 * Redact bytes from a bssid. 520 */ redactBssid(MacAddress bssid, int numRedactedOctets)521 public static String redactBssid(MacAddress bssid, int numRedactedOctets) { 522 if (bssid == null) { 523 return ""; 524 } 525 StringBuilder redactedBssid = new StringBuilder(); 526 byte[] bssidBytes = bssid.toByteArray(); 527 528 if (numRedactedOctets < 0 || numRedactedOctets > 6) { 529 // Reset to default if passed value is invalid. 530 numRedactedOctets = 4; 531 } 532 for (int i = 0; i < 6; i++) { 533 if (i < numRedactedOctets) { 534 redactedBssid.append("xx"); 535 } else { 536 redactedBssid.append(String.format("%02X", bssidBytes[i])); 537 } 538 if (i != 5) { 539 redactedBssid.append(":"); 540 } 541 } 542 return redactedBssid.toString(); 543 } 544 } 545