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