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