1 /* 2 * Copyright (C) 2019 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.wifitrackerlib; 18 19 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED; 20 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED; 21 22 import static com.android.wifitrackerlib.WifiEntry.SPEED_FAST; 23 import static com.android.wifitrackerlib.WifiEntry.SPEED_MODERATE; 24 import static com.android.wifitrackerlib.WifiEntry.SPEED_NONE; 25 import static com.android.wifitrackerlib.WifiEntry.SPEED_SLOW; 26 import static com.android.wifitrackerlib.WifiEntry.SPEED_VERY_FAST; 27 import static com.android.wifitrackerlib.WifiEntry.Speed; 28 29 import static java.util.Comparator.comparingInt; 30 31 import android.content.Context; 32 import android.content.pm.ApplicationInfo; 33 import android.content.pm.PackageManager; 34 import android.net.NetworkCapabilities; 35 import android.net.NetworkInfo; 36 import android.net.NetworkInfo.DetailedState; 37 import android.net.NetworkKey; 38 import android.net.NetworkScoreManager; 39 import android.net.ScoredNetwork; 40 import android.net.WifiKey; 41 import android.net.wifi.ScanResult; 42 import android.net.wifi.WifiConfiguration; 43 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; 44 import android.net.wifi.WifiInfo; 45 import android.net.wifi.WifiNetworkScoreCache; 46 import android.os.PersistableBundle; 47 import android.provider.Settings; 48 import android.telephony.CarrierConfigManager; 49 import android.telephony.SubscriptionInfo; 50 import android.telephony.SubscriptionManager; 51 import android.telephony.TelephonyManager; 52 import android.text.Annotation; 53 import android.text.SpannableString; 54 import android.text.SpannableStringBuilder; 55 import android.text.TextUtils; 56 import android.text.format.DateUtils; 57 import android.text.style.ClickableSpan; 58 import android.util.FeatureFlagUtils; 59 import android.view.View; 60 61 import androidx.annotation.NonNull; 62 import androidx.annotation.Nullable; 63 64 import com.android.internal.annotations.VisibleForTesting; 65 import com.android.settingslib.HelpUtils; 66 67 import java.util.ArrayList; 68 import java.util.Arrays; 69 import java.util.Collections; 70 import java.util.List; 71 import java.util.StringJoiner; 72 73 /** 74 * Utility methods for WifiTrackerLib. 75 */ 76 public class Utils { 77 /** Copy of the @hide Settings.Global.USE_OPEN_WIFI_PACKAGE constant. */ 78 static final String SETTINGS_GLOBAL_USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; 79 80 @VisibleForTesting 81 static FeatureFlagUtilsWrapper sFeatureFlagUtilsWrapper = new FeatureFlagUtilsWrapper(); 82 83 static class FeatureFlagUtilsWrapper { isProviderModelEnabled(Context context)84 boolean isProviderModelEnabled(Context context) { 85 return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL); 86 } 87 } 88 89 private static NetworkScoreManager sNetworkScoreManager; 90 getActiveScorerPackage(@onNull Context context)91 private static String getActiveScorerPackage(@NonNull Context context) { 92 if (sNetworkScoreManager == null) { 93 sNetworkScoreManager = context.getSystemService(NetworkScoreManager.class); 94 } 95 return sNetworkScoreManager.getActiveScorerPackage(); 96 } 97 98 // Returns the ScanResult with the best RSSI from a list of ScanResults. 99 @Nullable getBestScanResultByLevel(@onNull List<ScanResult> scanResults)100 public static ScanResult getBestScanResultByLevel(@NonNull List<ScanResult> scanResults) { 101 if (scanResults.isEmpty()) return null; 102 103 return Collections.max(scanResults, comparingInt(scanResult -> scanResult.level)); 104 } 105 106 // Returns a list of WifiInfo SECURITY_TYPE_* supported by a ScanResult. 107 // TODO(b/187755981): Move to shared static utils class 108 @NonNull getSecurityTypesFromScanResult(@onNull ScanResult scanResult)109 static List<Integer> getSecurityTypesFromScanResult(@NonNull ScanResult scanResult) { 110 List<Integer> securityTypes = new ArrayList<>(); 111 112 // Open network & its upgradable types 113 if (isScanResultForOweTransitionNetwork(scanResult)) { 114 securityTypes.add(WifiInfo.SECURITY_TYPE_OPEN); 115 securityTypes.add(WifiInfo.SECURITY_TYPE_OWE); 116 return securityTypes; 117 } else if (isScanResultForOweNetwork(scanResult)) { 118 securityTypes.add(WifiInfo.SECURITY_TYPE_OWE); 119 return securityTypes; 120 } else if (isScanResultForOpenNetwork(scanResult)) { 121 securityTypes.add(WifiInfo.SECURITY_TYPE_OPEN); 122 return securityTypes; 123 } 124 125 // WEP network which has no upgradable type 126 if (isScanResultForWepNetwork(scanResult)) { 127 securityTypes.add(WifiInfo.SECURITY_TYPE_WEP); 128 return securityTypes; 129 } 130 131 // WAPI PSK network which has no upgradable type 132 if (isScanResultForWapiPskNetwork(scanResult)) { 133 securityTypes.add(WifiInfo.SECURITY_TYPE_WAPI_PSK); 134 return securityTypes; 135 } 136 137 // WAPI CERT network which has no upgradable type 138 if (isScanResultForWapiCertNetwork(scanResult)) { 139 securityTypes.add( 140 WifiInfo.SECURITY_TYPE_WAPI_CERT); 141 return securityTypes; 142 } 143 144 // WPA2 personal network & its upgradable types 145 if (isScanResultForPskNetwork(scanResult) 146 && isScanResultForSaeNetwork(scanResult)) { 147 securityTypes.add(WifiInfo.SECURITY_TYPE_PSK); 148 securityTypes.add(WifiInfo.SECURITY_TYPE_SAE); 149 return securityTypes; 150 } else if (isScanResultForPskNetwork(scanResult)) { 151 securityTypes.add(WifiInfo.SECURITY_TYPE_PSK); 152 return securityTypes; 153 } else if (isScanResultForSaeNetwork(scanResult)) { 154 securityTypes.add(WifiInfo.SECURITY_TYPE_SAE); 155 return securityTypes; 156 } 157 158 // WPA3 Enterprise 192-bit mode, WPA2/WPA3 enterprise network & its upgradable types 159 if (isScanResultForEapSuiteBNetwork(scanResult)) { 160 securityTypes.add(WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT); 161 } else if (isScanResultForWpa3EnterpriseTransitionNetwork(scanResult)) { 162 securityTypes.add(WifiInfo.SECURITY_TYPE_EAP); 163 securityTypes.add(WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); 164 } else if (isScanResultForWpa3EnterpriseOnlyNetwork(scanResult)) { 165 securityTypes.add(WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); 166 } else if (isScanResultForEapNetwork(scanResult)) { 167 securityTypes.add(WifiInfo.SECURITY_TYPE_EAP); 168 } 169 return securityTypes; 170 } 171 172 // Returns a list of WifiInfo SECURITY_TYPE_* supported by a WifiConfiguration 173 // TODO(b/187755473): Use new public APIs to get the security type instead of relying on the 174 // legacy allowedKeyManagement bitset. getSecurityTypesFromWifiConfiguration(@onNull WifiConfiguration config)175 static List<Integer> getSecurityTypesFromWifiConfiguration(@NonNull WifiConfiguration config) { 176 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WAPI_CERT)) { 177 return Arrays.asList(WifiInfo.SECURITY_TYPE_WAPI_CERT); 178 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WAPI_PSK)) { 179 return Arrays.asList(WifiInfo.SECURITY_TYPE_WAPI_PSK); 180 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) { 181 return Arrays.asList(WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT); 182 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) { 183 return Arrays.asList(WifiInfo.SECURITY_TYPE_OWE); 184 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) { 185 return Arrays.asList(WifiInfo.SECURITY_TYPE_SAE); 186 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA2_PSK)) { 187 return Arrays.asList(WifiInfo.SECURITY_TYPE_PSK); 188 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)) { 189 if (config.requirePmf 190 && !config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.TKIP) 191 && config.allowedProtocols.get(WifiConfiguration.Protocol.RSN)) { 192 return Arrays.asList(WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); 193 } else { 194 // WPA2 configs should also be valid for WPA3-Enterprise APs 195 return Arrays.asList( 196 WifiInfo.SECURITY_TYPE_EAP, WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); 197 } 198 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 199 return Arrays.asList(WifiInfo.SECURITY_TYPE_PSK); 200 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) { 201 if (config.wepKeys != null) { 202 for (int i = 0; i < config.wepKeys.length; i++) { 203 if (config.wepKeys[i] != null) { 204 return Arrays.asList(WifiInfo.SECURITY_TYPE_WEP); 205 } 206 } 207 } 208 } 209 return Arrays.asList(WifiInfo.SECURITY_TYPE_OPEN); 210 } 211 212 /** 213 * Returns a single WifiInfo security type from the list of multiple WifiInfo security 214 * types supported by an entry. 215 * 216 * Single security types will have a 1-to-1 mapping. 217 * Multiple security type networks will collapse to the lowest security type in the group: 218 * - Open/OWE -> Open 219 * - PSK/SAE -> PSK 220 * - EAP/EAP-WPA3 -> EAP 221 */ getSingleSecurityTypeFromMultipleSecurityTypes( @onNull List<Integer> securityTypes)222 static int getSingleSecurityTypeFromMultipleSecurityTypes( 223 @NonNull List<Integer> securityTypes) { 224 if (securityTypes.size() == 1) { 225 return securityTypes.get(0); 226 } else if (securityTypes.size() == 2) { 227 if (securityTypes.contains(WifiInfo.SECURITY_TYPE_OPEN)) { 228 return WifiInfo.SECURITY_TYPE_OPEN; 229 } 230 if (securityTypes.contains(WifiInfo.SECURITY_TYPE_PSK)) { 231 return WifiInfo.SECURITY_TYPE_PSK; 232 } 233 if (securityTypes.contains(WifiInfo.SECURITY_TYPE_EAP)) { 234 return WifiInfo.SECURITY_TYPE_EAP; 235 } 236 } 237 return WifiInfo.SECURITY_TYPE_UNKNOWN; 238 } 239 240 @Speed getAverageSpeedFromScanResults(@onNull WifiNetworkScoreCache scoreCache, @NonNull List<ScanResult> scanResults)241 public static int getAverageSpeedFromScanResults(@NonNull WifiNetworkScoreCache scoreCache, 242 @NonNull List<ScanResult> scanResults) { 243 int count = 0; 244 int totalSpeed = 0; 245 for (ScanResult scanResult : scanResults) { 246 ScoredNetwork scoredNetwork = scoreCache.getScoredNetwork(scanResult); 247 if (scoredNetwork == null) { 248 continue; 249 } 250 @Speed int speed = scoredNetwork.calculateBadge(scanResult.level); 251 if (speed != SPEED_NONE) { 252 count++; 253 totalSpeed += speed; 254 } 255 } 256 if (count == 0) { 257 return SPEED_NONE; 258 } else { 259 return roundToClosestSpeedEnum(totalSpeed / count); 260 } 261 } 262 263 @Speed getSpeedFromWifiInfo(@onNull WifiNetworkScoreCache scoreCache, @NonNull WifiInfo wifiInfo)264 public static int getSpeedFromWifiInfo(@NonNull WifiNetworkScoreCache scoreCache, 265 @NonNull WifiInfo wifiInfo) { 266 final WifiKey wifiKey; 267 try { 268 wifiKey = new WifiKey(wifiInfo.getSSID(), wifiInfo.getBSSID()); 269 } catch (IllegalArgumentException e) { 270 return SPEED_NONE; 271 } 272 ScoredNetwork scoredNetwork = scoreCache.getScoredNetwork( 273 new NetworkKey(wifiKey)); 274 if (scoredNetwork == null) { 275 return SPEED_NONE; 276 } 277 return roundToClosestSpeedEnum(scoredNetwork.calculateBadge(wifiInfo.getRssi())); 278 } 279 280 @Speed roundToClosestSpeedEnum(int speed)281 private static int roundToClosestSpeedEnum(int speed) { 282 if (speed == SPEED_NONE) { 283 return SPEED_NONE; 284 } else if (speed < (SPEED_SLOW + SPEED_MODERATE) / 2) { 285 return SPEED_SLOW; 286 } else if (speed < (SPEED_MODERATE + SPEED_FAST) / 2) { 287 return SPEED_MODERATE; 288 } else if (speed < (SPEED_FAST + SPEED_VERY_FAST) / 2) { 289 return SPEED_FAST; 290 } else { 291 return SPEED_VERY_FAST; 292 } 293 } 294 295 /** 296 * Get the app label for a suggestion/specifier package name, or an empty String if none exist 297 */ getAppLabel(Context context, String packageName)298 static String getAppLabel(Context context, String packageName) { 299 try { 300 String openWifiPackageName = Settings.Global.getString(context.getContentResolver(), 301 SETTINGS_GLOBAL_USE_OPEN_WIFI_PACKAGE); 302 if (!TextUtils.isEmpty(openWifiPackageName) && TextUtils.equals(packageName, 303 getActiveScorerPackage(context))) { 304 packageName = openWifiPackageName; 305 } 306 307 ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo( 308 packageName, 309 0 /* flags */); 310 return appInfo.loadLabel(context.getPackageManager()).toString(); 311 } catch (PackageManager.NameNotFoundException e) { 312 return ""; 313 } 314 } 315 getConnectedDescription(Context context, WifiConfiguration wifiConfiguration, NetworkCapabilities networkCapabilities, String recommendationServiceLabel, boolean isDefaultNetwork, boolean isLowQuality)316 static String getConnectedDescription(Context context, 317 WifiConfiguration wifiConfiguration, 318 NetworkCapabilities networkCapabilities, 319 String recommendationServiceLabel, 320 boolean isDefaultNetwork, 321 boolean isLowQuality) { 322 final StringJoiner sj = new StringJoiner(context.getString( 323 R.string.wifitrackerlib_summary_separator)); 324 final boolean hideConnected = 325 !isDefaultNetwork && sFeatureFlagUtilsWrapper.isProviderModelEnabled(context); 326 327 if (wifiConfiguration != null) { 328 if (wifiConfiguration.fromWifiNetworkSuggestion 329 || wifiConfiguration.fromWifiNetworkSpecifier) { 330 // For suggestion or specifier networks to show "Connected via ..." 331 final String suggestionOrSpecifierLabel = 332 getSuggestionOrSpecifierLabel(context, wifiConfiguration); 333 if (!TextUtils.isEmpty(suggestionOrSpecifierLabel)) { 334 if (hideConnected) { 335 sj.add(context.getString(R.string.wifitrackerlib_available_via_app, 336 suggestionOrSpecifierLabel)); 337 } else { 338 sj.add(context.getString(R.string.wifitrackerlib_connected_via_app, 339 suggestionOrSpecifierLabel)); 340 } 341 } 342 } else if (wifiConfiguration.isEphemeral() && !hideConnected) { 343 // For ephemeral networks to show "Automatically connected via ..." 344 if (!TextUtils.isEmpty(recommendationServiceLabel)) { 345 sj.add(String.format(context.getString( 346 R.string.wifitrackerlib_connected_via_network_scorer), 347 recommendationServiceLabel)); 348 } else { 349 sj.add(context.getString( 350 R.string.wifitrackerlib_connected_via_network_scorer_default)); 351 } 352 } 353 } 354 355 if (isLowQuality) { 356 sj.add(context.getString(R.string.wifi_connected_low_quality)); 357 } 358 359 // For displaying network capability info, such as captive portal or no internet 360 String networkCapabilitiesInformation = 361 getCurrentNetworkCapabilitiesInformation(context, networkCapabilities); 362 if (!TextUtils.isEmpty(networkCapabilitiesInformation)) { 363 sj.add(networkCapabilitiesInformation); 364 } 365 366 // Default to "Connected" if nothing else to display 367 if (sj.length() == 0 && !hideConnected) { 368 return context.getResources().getStringArray(R.array.wifitrackerlib_wifi_status) 369 [DetailedState.CONNECTED.ordinal()]; 370 } 371 372 return sj.toString(); 373 } 374 getConnectingDescription(Context context, NetworkInfo networkInfo)375 static String getConnectingDescription(Context context, NetworkInfo networkInfo) { 376 if (context == null || networkInfo == null) { 377 return ""; 378 } 379 DetailedState detailedState = networkInfo.getDetailedState(); 380 if (detailedState == null) { 381 return ""; 382 } 383 384 final String[] wifiStatusArray = context.getResources() 385 .getStringArray(R.array.wifitrackerlib_wifi_status); 386 final int index = detailedState.ordinal(); 387 return index >= wifiStatusArray.length ? "" : wifiStatusArray[index]; 388 } 389 390 getDisconnectedDescription(Context context, WifiConfiguration wifiConfiguration, boolean forSavedNetworksPage, boolean concise)391 static String getDisconnectedDescription(Context context, 392 WifiConfiguration wifiConfiguration, 393 boolean forSavedNetworksPage, 394 boolean concise) { 395 if (context == null) { 396 return ""; 397 } 398 final StringJoiner sj = new StringJoiner(context.getString( 399 R.string.wifitrackerlib_summary_separator)); 400 401 // For "Saved", "Saved by ...", and "Available via..." 402 if (concise) { 403 sj.add(context.getString(R.string.wifitrackerlib_wifi_disconnected)); 404 } else if (wifiConfiguration != null) { 405 if (forSavedNetworksPage && !wifiConfiguration.isPasspoint()) { 406 final CharSequence appLabel = getAppLabel(context, wifiConfiguration.creatorName); 407 if (!TextUtils.isEmpty(appLabel)) { 408 sj.add(context.getString(R.string.wifitrackerlib_saved_network, appLabel)); 409 } 410 } else { 411 if (wifiConfiguration.fromWifiNetworkSuggestion) { 412 final String suggestionOrSpecifierLabel = 413 getSuggestionOrSpecifierLabel(context, wifiConfiguration); 414 if (!TextUtils.isEmpty(suggestionOrSpecifierLabel)) { 415 sj.add(context.getString( 416 R.string.wifitrackerlib_available_via_app, 417 suggestionOrSpecifierLabel)); 418 } 419 } else { 420 sj.add(context.getString(R.string.wifitrackerlib_wifi_remembered)); 421 } 422 } 423 } 424 425 // For failure messages and disabled reasons 426 final String wifiConfigFailureMessage = 427 getWifiConfigurationFailureMessage(context, wifiConfiguration); 428 if (!TextUtils.isEmpty(wifiConfigFailureMessage)) { 429 sj.add(wifiConfigFailureMessage); 430 } 431 432 return sj.toString(); 433 } 434 getSuggestionOrSpecifierLabel( Context context, WifiConfiguration wifiConfiguration)435 private static String getSuggestionOrSpecifierLabel( 436 Context context, WifiConfiguration wifiConfiguration) { 437 if (context == null || wifiConfiguration == null) { 438 return ""; 439 } 440 441 final String carrierName = getCarrierNameForSubId(context, 442 getSubIdForConfig(context, wifiConfiguration)); 443 if (!TextUtils.isEmpty(carrierName)) { 444 return carrierName; 445 } 446 final String suggestorLabel = getAppLabel(context, wifiConfiguration.creatorName); 447 if (!TextUtils.isEmpty(suggestorLabel)) { 448 return suggestorLabel; 449 } 450 // Fall-back to the package name in case the app label is missing 451 return wifiConfiguration.creatorName; 452 } 453 getWifiConfigurationFailureMessage( Context context, WifiConfiguration wifiConfiguration)454 private static String getWifiConfigurationFailureMessage( 455 Context context, WifiConfiguration wifiConfiguration) { 456 if (context == null || wifiConfiguration == null) { 457 return ""; 458 } 459 460 // Check for any failure messages to display 461 if (wifiConfiguration.hasNoInternetAccess()) { 462 int messageID = 463 wifiConfiguration.getNetworkSelectionStatus().getNetworkSelectionStatus() 464 == NETWORK_SELECTION_PERMANENTLY_DISABLED 465 ? R.string.wifitrackerlib_wifi_no_internet_no_reconnect 466 : R.string.wifitrackerlib_wifi_no_internet; 467 return context.getString(messageID); 468 } else if (wifiConfiguration.getNetworkSelectionStatus().getNetworkSelectionStatus() 469 != NETWORK_SELECTION_ENABLED) { 470 WifiConfiguration.NetworkSelectionStatus networkStatus = 471 wifiConfiguration.getNetworkSelectionStatus(); 472 switch (networkStatus.getNetworkSelectionDisableReason()) { 473 case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE: 474 case WifiConfiguration.NetworkSelectionStatus 475 .DISABLED_AUTHENTICATION_NO_SUBSCRIPTION: 476 return context.getString( 477 R.string.wifitrackerlib_wifi_disabled_password_failure); 478 case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD: 479 return context.getString(R.string.wifitrackerlib_wifi_check_password_try_again); 480 case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE: 481 return context.getString(R.string.wifitrackerlib_wifi_disabled_network_failure); 482 case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION: 483 return context.getString(R.string.wifitrackerlib_wifi_disabled_generic); 484 case WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT: 485 case WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY: 486 return context.getString(R.string.wifitrackerlib_wifi_no_internet_no_reconnect); 487 default: 488 break; 489 } 490 } else { // In range, not disabled. 491 switch (wifiConfiguration.getRecentFailureReason()) { 492 case WifiConfiguration.RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA: 493 case WifiConfiguration.RECENT_FAILURE_REFUSED_TEMPORARILY: 494 case WifiConfiguration.RECENT_FAILURE_DISCONNECTION_AP_BUSY: 495 return context.getString(R.string 496 .wifitrackerlib_wifi_ap_unable_to_handle_new_sta); 497 case WifiConfiguration.RECENT_FAILURE_POOR_CHANNEL_CONDITIONS: 498 return context.getString(R.string.wifitrackerlib_wifi_poor_channel_conditions); 499 case WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_UNSPECIFIED: 500 case WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_AIR_INTERFACE_OVERLOADED: 501 case WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_AUTH_SERVER_OVERLOADED: 502 return context.getString(R.string 503 .wifitrackerlib_wifi_mbo_assoc_disallowed_cannot_connect); 504 case WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_MAX_NUM_STA_ASSOCIATED: 505 return context.getString(R.string 506 .wifitrackerlib_wifi_mbo_assoc_disallowed_max_num_sta_associated); 507 case WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_INSUFFICIENT_RSSI: 508 case WifiConfiguration.RECENT_FAILURE_OCE_RSSI_BASED_ASSOCIATION_REJECTION: 509 return context.getString(R.string 510 .wifitrackerlib_wifi_mbo_oce_assoc_disallowed_insufficient_rssi); 511 case WifiConfiguration.RECENT_FAILURE_NETWORK_NOT_FOUND: 512 return context.getString(R.string.wifitrackerlib_wifi_network_not_found); 513 default: 514 // do nothing 515 } 516 } 517 return ""; 518 } 519 getAutoConnectDescription(@onNull Context context, @NonNull WifiEntry wifiEntry)520 static String getAutoConnectDescription(@NonNull Context context, 521 @NonNull WifiEntry wifiEntry) { 522 if (context == null || wifiEntry == null || !wifiEntry.canSetAutoJoinEnabled()) { 523 return ""; 524 } 525 526 return wifiEntry.isAutoJoinEnabled() 527 ? "" : context.getString(R.string.wifitrackerlib_auto_connect_disable); 528 } 529 getMeteredDescription(@onNull Context context, @Nullable WifiEntry wifiEntry)530 static String getMeteredDescription(@NonNull Context context, @Nullable WifiEntry wifiEntry) { 531 if (context == null || wifiEntry == null) { 532 return ""; 533 } 534 535 if (!wifiEntry.canSetMeteredChoice() 536 && wifiEntry.getMeteredChoice() != WifiEntry.METERED_CHOICE_METERED) { 537 return ""; 538 } 539 540 if (wifiEntry.getMeteredChoice() == WifiEntry.METERED_CHOICE_METERED) { 541 return context.getString(R.string.wifitrackerlib_wifi_metered_label); 542 } else if (wifiEntry.getMeteredChoice() == WifiEntry.METERED_CHOICE_UNMETERED) { 543 return context.getString(R.string.wifitrackerlib_wifi_unmetered_label); 544 } else { // METERED_CHOICE_AUTO 545 return wifiEntry.isMetered() ? context.getString( 546 R.string.wifitrackerlib_wifi_metered_label) : ""; 547 } 548 } 549 getSpeedDescription(@onNull Context context, @NonNull WifiEntry wifiEntry)550 static String getSpeedDescription(@NonNull Context context, @NonNull WifiEntry wifiEntry) { 551 if (context == null || wifiEntry == null) { 552 return ""; 553 } 554 555 @Speed int speed = wifiEntry.getSpeed(); 556 switch (speed) { 557 case SPEED_VERY_FAST: 558 return context.getString(R.string.wifitrackerlib_speed_label_very_fast); 559 case SPEED_FAST: 560 return context.getString(R.string.wifitrackerlib_speed_label_fast); 561 case SPEED_MODERATE: 562 return context.getString(R.string.wifitrackerlib_speed_label_okay); 563 case SPEED_SLOW: 564 return context.getString(R.string.wifitrackerlib_speed_label_slow); 565 case SPEED_NONE: 566 default: 567 return ""; 568 } 569 } 570 getVerboseLoggingDescription(@onNull WifiEntry wifiEntry)571 static String getVerboseLoggingDescription(@NonNull WifiEntry wifiEntry) { 572 if (!BaseWifiTracker.isVerboseLoggingEnabled() || wifiEntry == null) { 573 return ""; 574 } 575 576 final StringJoiner sj = new StringJoiner(" "); 577 578 final String wifiInfoDescription = wifiEntry.getWifiInfoDescription(); 579 if (!TextUtils.isEmpty(wifiInfoDescription)) { 580 sj.add(wifiInfoDescription); 581 } 582 583 final String networkCapabilityDescription = wifiEntry.getNetworkCapabilityDescription(); 584 if (!TextUtils.isEmpty(networkCapabilityDescription)) { 585 sj.add(networkCapabilityDescription); 586 } 587 588 final String scanResultsDescription = wifiEntry.getScanResultDescription(); 589 if (!TextUtils.isEmpty(scanResultsDescription)) { 590 sj.add(scanResultsDescription); 591 } 592 593 final String networkSelectionDescription = wifiEntry.getNetworkSelectionDescription(); 594 if (!TextUtils.isEmpty(networkSelectionDescription)) { 595 sj.add(networkSelectionDescription); 596 } 597 598 return sj.toString(); 599 } 600 getNetworkSelectionDescription(WifiConfiguration wifiConfig)601 static String getNetworkSelectionDescription(WifiConfiguration wifiConfig) { 602 if (wifiConfig == null) { 603 return ""; 604 } 605 606 StringBuilder description = new StringBuilder(); 607 NetworkSelectionStatus networkSelectionStatus = wifiConfig.getNetworkSelectionStatus(); 608 609 if (networkSelectionStatus.getNetworkSelectionStatus() != NETWORK_SELECTION_ENABLED) { 610 description.append(" (" + networkSelectionStatus.getNetworkStatusString()); 611 if (networkSelectionStatus.getDisableTime() > 0) { 612 long now = System.currentTimeMillis(); 613 long elapsedSeconds = (now - networkSelectionStatus.getDisableTime()) / 1000; 614 description.append(" " + DateUtils.formatElapsedTime(elapsedSeconds)); 615 } 616 description.append(")"); 617 } 618 619 int maxNetworkSelectionDisableReason = 620 NetworkSelectionStatus.getMaxNetworkSelectionDisableReason(); 621 for (int reason = 0; reason <= maxNetworkSelectionDisableReason; reason++) { 622 int disableReasonCounter = networkSelectionStatus.getDisableReasonCounter(reason); 623 if (disableReasonCounter == 0) { 624 continue; 625 } 626 description.append(" ") 627 .append(NetworkSelectionStatus.getNetworkSelectionDisableReasonString(reason)) 628 .append("=") 629 .append(disableReasonCounter); 630 } 631 return description.toString(); 632 } 633 getCurrentNetworkCapabilitiesInformation(Context context, NetworkCapabilities networkCapabilities)634 static String getCurrentNetworkCapabilitiesInformation(Context context, 635 NetworkCapabilities networkCapabilities) { 636 if (context == null || networkCapabilities == null) { 637 return ""; 638 } 639 640 if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)) { 641 return context.getString(context.getResources() 642 .getIdentifier("network_available_sign_in", "string", "android")); 643 } 644 645 if (networkCapabilities.hasCapability( 646 NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) { 647 return context.getString(R.string.wifitrackerlib_wifi_limited_connection); 648 } 649 650 if (!networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { 651 if (networkCapabilities.isPrivateDnsBroken()) { 652 return context.getString(R.string.wifitrackerlib_private_dns_broken); 653 } 654 return context.getString( 655 R.string.wifitrackerlib_wifi_connected_cannot_provide_internet); 656 } 657 return ""; 658 } 659 660 /** 661 * Returns the display string corresponding to the detailed state of the given NetworkInfo 662 */ getNetworkDetailedState(Context context, NetworkInfo networkInfo)663 static String getNetworkDetailedState(Context context, NetworkInfo networkInfo) { 664 if (context == null || networkInfo == null) { 665 return ""; 666 } 667 DetailedState detailedState = networkInfo.getDetailedState(); 668 if (detailedState == null) { 669 return ""; 670 } 671 672 String[] wifiStatusArray = context.getResources() 673 .getStringArray(R.array.wifitrackerlib_wifi_status); 674 int index = detailedState.ordinal(); 675 return index >= wifiStatusArray.length ? "" : wifiStatusArray[index]; 676 } 677 678 /** 679 * Check if the SIM is present for target carrier Id. 680 */ isSimPresent(@onNull Context context, int carrierId)681 static boolean isSimPresent(@NonNull Context context, int carrierId) { 682 SubscriptionManager subscriptionManager = 683 (SubscriptionManager) context.getSystemService( 684 Context.TELEPHONY_SUBSCRIPTION_SERVICE); 685 if (subscriptionManager == null) return false; 686 List<SubscriptionInfo> subInfoList = subscriptionManager.getActiveSubscriptionInfoList(); 687 if (subInfoList == null || subInfoList.isEmpty()) { 688 return false; 689 } 690 return subInfoList.stream() 691 .anyMatch(info -> info.getCarrierId() == carrierId); 692 } 693 694 /** 695 * Get the SIM carrier name for target subscription Id. 696 */ getCarrierNameForSubId(@onNull Context context, int subId)697 static @Nullable String getCarrierNameForSubId(@NonNull Context context, int subId) { 698 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 699 return null; 700 } 701 TelephonyManager telephonyManager = 702 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 703 if (telephonyManager == null) return null; 704 TelephonyManager specifiedTm = telephonyManager.createForSubscriptionId(subId); 705 if (specifiedTm == null) { 706 return null; 707 } 708 CharSequence name = specifiedTm.getSimCarrierIdName(); 709 if (name == null) { 710 return null; 711 } 712 return name.toString(); 713 } 714 isSimCredential(@onNull WifiConfiguration config)715 static boolean isSimCredential(@NonNull WifiConfiguration config) { 716 return config.enterpriseConfig != null 717 && config.enterpriseConfig.isAuthenticationSimBased(); 718 } 719 720 /** 721 * Get the best match subscription Id for target WifiConfiguration. 722 */ getSubIdForConfig(@onNull Context context, @NonNull WifiConfiguration config)723 static int getSubIdForConfig(@NonNull Context context, @NonNull WifiConfiguration config) { 724 if (config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { 725 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 726 } 727 SubscriptionManager subscriptionManager = 728 (SubscriptionManager) context.getSystemService( 729 Context.TELEPHONY_SUBSCRIPTION_SERVICE); 730 if (subscriptionManager == null) { 731 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 732 } 733 List<SubscriptionInfo> subInfoList = subscriptionManager.getActiveSubscriptionInfoList(); 734 if (subInfoList == null || subInfoList.isEmpty()) { 735 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 736 } 737 738 int matchSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 739 int dataSubId = SubscriptionManager.getDefaultDataSubscriptionId(); 740 for (SubscriptionInfo subInfo : subInfoList) { 741 if (subInfo.getCarrierId() == config.carrierId) { 742 matchSubId = subInfo.getSubscriptionId(); 743 if (matchSubId == dataSubId) { 744 // Priority of Data sub is higher than non data sub. 745 break; 746 } 747 } 748 } 749 return matchSubId; 750 } 751 752 /** 753 * Check if target subscription Id requires IMSI privacy protection. 754 */ isImsiPrivacyProtectionProvided(@onNull Context context, int subId)755 static boolean isImsiPrivacyProtectionProvided(@NonNull Context context, int subId) { 756 CarrierConfigManager carrierConfigManager = 757 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 758 if (carrierConfigManager == null) { 759 return false; 760 } 761 PersistableBundle bundle = carrierConfigManager.getConfigForSubId(subId); 762 if (bundle == null) { 763 return false; 764 } 765 return (bundle.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT) 766 & TelephonyManager.KEY_TYPE_WLAN) != 0; 767 } 768 getImsiProtectionDescription(Context context, @Nullable WifiConfiguration wifiConfig)769 static CharSequence getImsiProtectionDescription(Context context, 770 @Nullable WifiConfiguration wifiConfig) { 771 if (context == null || wifiConfig == null || !isSimCredential(wifiConfig)) { 772 return ""; 773 } 774 int subId; 775 if (wifiConfig.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { 776 // Config without carrierId use default data subscription. 777 subId = SubscriptionManager.getDefaultSubscriptionId(); 778 } else { 779 subId = getSubIdForConfig(context, wifiConfig); 780 } 781 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID 782 || isImsiPrivacyProtectionProvided(context, subId)) { 783 return ""; 784 } 785 786 // IMSI protection is not provided, return warning message. 787 return linkifyAnnotation(context, context.getText( 788 R.string.wifitrackerlib_imsi_protection_warning), "url", 789 context.getString(R.string.wifitrackerlib_help_url_imsi_protection)); 790 } 791 792 /** Find the annotation of specified id in rawText and linkify it with helpUriString. */ linkifyAnnotation(Context context, CharSequence rawText, String id, String helpUriString)793 static CharSequence linkifyAnnotation(Context context, CharSequence rawText, String id, 794 String helpUriString) { 795 // Return original string when helpUriString is empty. 796 if (TextUtils.isEmpty(helpUriString)) { 797 return rawText; 798 } 799 800 SpannableString spannableText = new SpannableString(rawText); 801 Annotation[] annotations = spannableText.getSpans(0, spannableText.length(), 802 Annotation.class); 803 804 for (Annotation annotation : annotations) { 805 if (TextUtils.equals(annotation.getValue(), id)) { 806 SpannableStringBuilder builder = new SpannableStringBuilder(spannableText); 807 ClickableSpan link = new ClickableSpan() { 808 @Override 809 public void onClick(View view) { 810 view.startActivityForResult(HelpUtils.getHelpIntent(context, helpUriString, 811 view.getClass().getName()), 0); 812 } 813 }; 814 builder.setSpan(link, spannableText.getSpanStart(annotation), 815 spannableText.getSpanEnd(annotation), spannableText.getSpanFlags(link)); 816 return builder; 817 } 818 } 819 return rawText; 820 } 821 822 // Various utility methods copied from com.android.server.wifi.util.ScanResultUtils for 823 // extracting SecurityType from ScanResult. 824 825 /** 826 * Helper method to check if the provided |scanResult| corresponds to a PSK network or not. 827 * This checks if the provided capabilities string contains PSK encryption type or not. 828 * TODO(b/187755981): Move to shared static utils class 829 */ isScanResultForPskNetwork(ScanResult scanResult)830 public static boolean isScanResultForPskNetwork(ScanResult scanResult) { 831 return scanResult.capabilities.contains("PSK"); 832 } 833 834 /** 835 * Helper method to check if the provided |scanResult| corresponds to a WAPI-PSK network or not. 836 * This checks if the provided capabilities string contains PSK encryption type or not. 837 * TODO(b/187755981): Move to shared static utils class 838 */ isScanResultForWapiPskNetwork(ScanResult scanResult)839 public static boolean isScanResultForWapiPskNetwork(ScanResult scanResult) { 840 return scanResult.capabilities.contains("WAPI-PSK"); 841 } 842 843 /** 844 * Helper method to check if the provided |scanResult| corresponds to a WAPI-CERT 845 * network or not. 846 * This checks if the provided capabilities string contains PSK encryption type or not. 847 * TODO(b/187755981): Move to shared static utils class 848 */ isScanResultForWapiCertNetwork(ScanResult scanResult)849 public static boolean isScanResultForWapiCertNetwork(ScanResult scanResult) { 850 return scanResult.capabilities.contains("WAPI-CERT"); 851 } 852 853 /** 854 * Helper method to check if the provided |scanResult| corresponds to a EAP network or not. 855 * This checks these conditions: 856 * - Enable EAP/SHA1, EAP/SHA256 AKM, FT/EAP, or EAP-FILS. 857 * - Not a WPA3 Enterprise only network. 858 * - Not a WPA3 Enterprise transition network. 859 * TODO(b/187755981): Move to shared static utils class 860 */ isScanResultForEapNetwork(ScanResult scanResult)861 public static boolean isScanResultForEapNetwork(ScanResult scanResult) { 862 return (scanResult.capabilities.contains("EAP/SHA1") 863 || scanResult.capabilities.contains("EAP/SHA256") 864 || scanResult.capabilities.contains("FT/EAP") 865 || scanResult.capabilities.contains("EAP-FILS")) 866 && !isScanResultForWpa3EnterpriseOnlyNetwork(scanResult) 867 && !isScanResultForWpa3EnterpriseTransitionNetwork(scanResult); 868 } 869 870 // TODO(b/187755981): Move to shared static utils class isScanResultForPmfMandatoryNetwork(ScanResult scanResult)871 private static boolean isScanResultForPmfMandatoryNetwork(ScanResult scanResult) { 872 return scanResult.capabilities.contains("[MFPR]"); 873 } 874 875 // TODO(b/187755981): Move to shared static utils class isScanResultForPmfCapableNetwork(ScanResult scanResult)876 private static boolean isScanResultForPmfCapableNetwork(ScanResult scanResult) { 877 return scanResult.capabilities.contains("[MFPC]"); 878 } 879 880 /** 881 * Helper method to check if the provided |scanResult| corresponds to 882 * a WPA3 Enterprise transition network or not. 883 * 884 * See Section 3.3 WPA3-Enterprise transition mode in WPA3 Specification 885 * - Enable at least EAP/SHA1 and EAP/SHA256 AKM suites. 886 * - Not enable WPA1 version 1, WEP, and TKIP. 887 * - Management Frame Protection Capable is set. 888 * - Management Frame Protection Required is not set. 889 * TODO(b/187755981): Move to shared static utils class 890 */ isScanResultForWpa3EnterpriseTransitionNetwork(ScanResult scanResult)891 public static boolean isScanResultForWpa3EnterpriseTransitionNetwork(ScanResult scanResult) { 892 return scanResult.capabilities.contains("EAP/SHA1") 893 && scanResult.capabilities.contains("EAP/SHA256") 894 && scanResult.capabilities.contains("RSN") 895 && !scanResult.capabilities.contains("WEP") 896 && !scanResult.capabilities.contains("TKIP") 897 && !isScanResultForPmfMandatoryNetwork(scanResult) 898 && isScanResultForPmfCapableNetwork(scanResult); 899 } 900 901 /** 902 * Helper method to check if the provided |scanResult| corresponds to 903 * a WPA3 Enterprise only network or not. 904 * 905 * See Section 3.2 WPA3-Enterprise only mode in WPA3 Specification 906 * - Enable at least EAP/SHA256 AKM suite. 907 * - Not enable EAP/SHA1 AKM suite. 908 * - Not enable WPA1 version 1, WEP, and TKIP. 909 * - Management Frame Protection Capable is set. 910 * - Management Frame Protection Required is set. 911 * TODO(b/187755981): Move to shared static utils class 912 */ isScanResultForWpa3EnterpriseOnlyNetwork(ScanResult scanResult)913 public static boolean isScanResultForWpa3EnterpriseOnlyNetwork(ScanResult scanResult) { 914 return scanResult.capabilities.contains("EAP/SHA256") 915 && !scanResult.capabilities.contains("EAP/SHA1") 916 && scanResult.capabilities.contains("RSN") 917 && !scanResult.capabilities.contains("WEP") 918 && !scanResult.capabilities.contains("TKIP") 919 && isScanResultForPmfMandatoryNetwork(scanResult) 920 && isScanResultForPmfCapableNetwork(scanResult); 921 } 922 923 924 /** 925 * Helper method to check if the provided |scanResult| corresponds to a WPA3-Enterprise 192-bit 926 * mode network or not. 927 * This checks if the provided capabilities comply these conditions: 928 * - Enable SUITE-B-192 AKM. 929 * - Not enable EAP/SHA1 AKM suite. 930 * - Not enable WPA1 version 1, WEP, and TKIP. 931 * - Management Frame Protection Required is set. 932 * TODO(b/187755981): Move to shared static utils class 933 */ isScanResultForEapSuiteBNetwork(ScanResult scanResult)934 public static boolean isScanResultForEapSuiteBNetwork(ScanResult scanResult) { 935 return scanResult.capabilities.contains("SUITE_B_192") 936 && scanResult.capabilities.contains("RSN") 937 && !scanResult.capabilities.contains("WEP") 938 && !scanResult.capabilities.contains("TKIP") 939 && isScanResultForPmfMandatoryNetwork(scanResult); 940 } 941 942 /** 943 * Helper method to check if the provided |scanResult| corresponds to a WEP network or not. 944 * This checks if the provided capabilities string contains WEP encryption type or not. 945 * TODO(b/187755981): Move to shared static utils class 946 */ isScanResultForWepNetwork(ScanResult scanResult)947 public static boolean isScanResultForWepNetwork(ScanResult scanResult) { 948 return scanResult.capabilities.contains("WEP"); 949 } 950 951 /** 952 * Helper method to check if the provided |scanResult| corresponds to OWE network. 953 * This checks if the provided capabilities string contains OWE or not. 954 * TODO(b/187755981): Move to shared static utils class 955 */ isScanResultForOweNetwork(ScanResult scanResult)956 public static boolean isScanResultForOweNetwork(ScanResult scanResult) { 957 return scanResult.capabilities.contains("OWE"); 958 } 959 960 /** 961 * Helper method to check if the provided |scanResult| corresponds to OWE transition network. 962 * This checks if the provided capabilities string contains OWE_TRANSITION or not. 963 * TODO(b/187755981): Move to shared static utils class 964 */ isScanResultForOweTransitionNetwork(ScanResult scanResult)965 public static boolean isScanResultForOweTransitionNetwork(ScanResult scanResult) { 966 return scanResult.capabilities.contains("OWE_TRANSITION"); 967 } 968 969 /** 970 * Helper method to check if the provided |scanResult| corresponds to SAE network. 971 * This checks if the provided capabilities string contains SAE or not. 972 * TODO(b/187755981): Move to shared static utils class 973 */ isScanResultForSaeNetwork(ScanResult scanResult)974 public static boolean isScanResultForSaeNetwork(ScanResult scanResult) { 975 return scanResult.capabilities.contains("SAE"); 976 } 977 978 /** 979 * Helper method to check if the provided |scanResult| corresponds to PSK-SAE transition 980 * network. This checks if the provided capabilities string contains both PSK and SAE or not. 981 * TODO(b/187755981): Move to shared static utils class 982 */ isScanResultForPskSaeTransitionNetwork(ScanResult scanResult)983 public static boolean isScanResultForPskSaeTransitionNetwork(ScanResult scanResult) { 984 return scanResult.capabilities.contains("PSK") && scanResult.capabilities.contains("SAE"); 985 } 986 987 /** 988 * Helper method to check if the provided |scanResult| corresponds to an unknown amk network. 989 * This checks if the provided capabilities string contains ? or not. 990 * TODO(b/187755981): Move to shared static utils class 991 */ isScanResultForUnknownAkmNetwork(ScanResult scanResult)992 public static boolean isScanResultForUnknownAkmNetwork(ScanResult scanResult) { 993 return scanResult.capabilities.contains("?"); 994 } 995 996 /** 997 * Helper method to check if the provided |scanResult| corresponds to an open network or not. 998 * This checks if the provided capabilities string does not contain either of WEP, PSK, SAE 999 * EAP, or unknown encryption types or not. 1000 * TODO(b/187755981): Move to shared static utils class 1001 */ isScanResultForOpenNetwork(ScanResult scanResult)1002 public static boolean isScanResultForOpenNetwork(ScanResult scanResult) { 1003 return (!(isScanResultForWepNetwork(scanResult) || isScanResultForPskNetwork(scanResult) 1004 || isScanResultForEapNetwork(scanResult) || isScanResultForSaeNetwork(scanResult) 1005 || isScanResultForWpa3EnterpriseTransitionNetwork(scanResult) 1006 || isScanResultForWpa3EnterpriseOnlyNetwork(scanResult) 1007 || isScanResultForWapiPskNetwork(scanResult) 1008 || isScanResultForWapiCertNetwork(scanResult) 1009 || isScanResultForEapSuiteBNetwork(scanResult) 1010 || isScanResultForUnknownAkmNetwork(scanResult))); 1011 } 1012 } 1013