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