diff options
Diffstat (limited to 'libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java')
-rw-r--r-- | libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java | 798 |
1 files changed, 540 insertions, 258 deletions
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java index 90d62f280..9c4f376ac 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java @@ -19,14 +19,6 @@ package com.android.wifitrackerlib; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED; -import static com.android.wifitrackerlib.StandardWifiEntry.ssidAndSecurityToStandardWifiEntryKey; -import static com.android.wifitrackerlib.WifiEntry.SECURITY_EAP; -import static com.android.wifitrackerlib.WifiEntry.SECURITY_EAP_SUITE_B; -import static com.android.wifitrackerlib.WifiEntry.SECURITY_NONE; -import static com.android.wifitrackerlib.WifiEntry.SECURITY_OWE; -import static com.android.wifitrackerlib.WifiEntry.SECURITY_PSK; -import static com.android.wifitrackerlib.WifiEntry.SECURITY_SAE; -import static com.android.wifitrackerlib.WifiEntry.SECURITY_WEP; import static com.android.wifitrackerlib.WifiEntry.SPEED_FAST; import static com.android.wifitrackerlib.WifiEntry.SPEED_MODERATE; import static com.android.wifitrackerlib.WifiEntry.SPEED_NONE; @@ -35,7 +27,6 @@ import static com.android.wifitrackerlib.WifiEntry.SPEED_VERY_FAST; import static com.android.wifitrackerlib.WifiEntry.Speed; import static java.util.Comparator.comparingInt; -import static java.util.stream.Collectors.groupingBy; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -46,13 +37,13 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkKey; import android.net.NetworkScoreManager; import android.net.ScoredNetwork; +import android.net.WifiKey; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; import android.net.wifi.WifiInfo; import android.net.wifi.WifiNetworkScoreCache; import android.os.PersistableBundle; -import android.os.UserHandle; import android.provider.Settings; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; @@ -64,24 +55,37 @@ import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.style.ClickableSpan; +import android.util.FeatureFlagUtils; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.HelpUtils; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.StringJoiner; /** * Utility methods for WifiTrackerLib. */ -class Utils { +public class Utils { + /** Copy of the @hide Settings.Global.USE_OPEN_WIFI_PACKAGE constant. */ + static final String SETTINGS_GLOBAL_USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; + + @VisibleForTesting + static FeatureFlagUtilsWrapper sFeatureFlagUtilsWrapper = new FeatureFlagUtilsWrapper(); + + static class FeatureFlagUtilsWrapper { + boolean isProviderModelEnabled(Context context) { + return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL); + } + } + private static NetworkScoreManager sNetworkScoreManager; private static String getActiveScorerPackage(@NonNull Context context) { @@ -93,234 +97,148 @@ class Utils { // Returns the ScanResult with the best RSSI from a list of ScanResults. @Nullable - static ScanResult getBestScanResultByLevel(@NonNull List<ScanResult> scanResults) { + public static ScanResult getBestScanResultByLevel(@NonNull List<ScanResult> scanResults) { if (scanResults.isEmpty()) return null; return Collections.max(scanResults, comparingInt(scanResult -> scanResult.level)); } - // Returns a list of SECURITY types supported by a ScanResult. - static List<Integer> getSecurityTypesFromScanResult(@NonNull ScanResult scan) { - final List<Integer> securityTypes = new ArrayList<>(); - if (scan.capabilities == null) { - securityTypes.add(SECURITY_NONE); - } else if (scan.capabilities.contains("PSK") && scan.capabilities.contains("SAE")) { - securityTypes.add(SECURITY_PSK); - securityTypes.add(SECURITY_SAE); - } else if (scan.capabilities.contains("OWE_TRANSITION")) { - securityTypes.add(SECURITY_NONE); - securityTypes.add(SECURITY_OWE); - } else if (scan.capabilities.contains("OWE")) { - securityTypes.add(SECURITY_OWE); - } else if (scan.capabilities.contains("WEP")) { - securityTypes.add(SECURITY_WEP); - } else if (scan.capabilities.contains("SAE")) { - securityTypes.add(SECURITY_SAE); - } else if (scan.capabilities.contains("PSK")) { - securityTypes.add(SECURITY_PSK); - } else if (scan.capabilities.contains("EAP_SUITE_B_192")) { - securityTypes.add(SECURITY_EAP_SUITE_B); - } else if (scan.capabilities.contains("EAP")) { - securityTypes.add(SECURITY_EAP); - } else { - securityTypes.add(SECURITY_NONE); + // Returns a list of WifiInfo SECURITY_TYPE_* supported by a ScanResult. + // TODO(b/187755981): Move to shared static utils class + @NonNull + static List<Integer> getSecurityTypesFromScanResult(@NonNull ScanResult scanResult) { + List<Integer> securityTypes = new ArrayList<>(); + + // Open network & its upgradable types + if (isScanResultForOweTransitionNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_OPEN); + securityTypes.add(WifiInfo.SECURITY_TYPE_OWE); + return securityTypes; + } else if (isScanResultForOweNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_OWE); + return securityTypes; + } else if (isScanResultForOpenNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_OPEN); + return securityTypes; + } + + // WEP network which has no upgradable type + if (isScanResultForWepNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_WEP); + return securityTypes; + } + + // WAPI PSK network which has no upgradable type + if (isScanResultForWapiPskNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_WAPI_PSK); + return securityTypes; + } + + // WAPI CERT network which has no upgradable type + if (isScanResultForWapiCertNetwork(scanResult)) { + securityTypes.add( + WifiInfo.SECURITY_TYPE_WAPI_CERT); + return securityTypes; + } + + // WPA2 personal network & its upgradable types + if (isScanResultForPskNetwork(scanResult) + && isScanResultForSaeNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_PSK); + securityTypes.add(WifiInfo.SECURITY_TYPE_SAE); + return securityTypes; + } else if (isScanResultForPskNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_PSK); + return securityTypes; + } else if (isScanResultForSaeNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_SAE); + return securityTypes; + } + + // WPA3 Enterprise 192-bit mode, WPA2/WPA3 enterprise network & its upgradable types + if (isScanResultForEapSuiteBNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT); + } else if (isScanResultForWpa3EnterpriseTransitionNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_EAP); + securityTypes.add(WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); + } else if (isScanResultForWpa3EnterpriseOnlyNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); + } else if (isScanResultForEapNetwork(scanResult)) { + securityTypes.add(WifiInfo.SECURITY_TYPE_EAP); } return securityTypes; } - // Returns the SECURITY type supported by a WifiConfiguration - @WifiEntry.Security - static int getSecurityTypeFromWifiConfiguration(@NonNull WifiConfiguration config) { - if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) { - return SECURITY_SAE; - } - if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { - return SECURITY_PSK; - } - if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) { - return SECURITY_EAP_SUITE_B; - } - if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) - || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) { - return SECURITY_EAP; - } - if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) { - return SECURITY_OWE; + // Returns a list of WifiInfo SECURITY_TYPE_* supported by a WifiConfiguration + // TODO(b/187755473): Use new public APIs to get the security type instead of relying on the + // legacy allowedKeyManagement bitset. + static List<Integer> getSecurityTypesFromWifiConfiguration(@NonNull WifiConfiguration config) { + if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WAPI_CERT)) { + return Arrays.asList(WifiInfo.SECURITY_TYPE_WAPI_CERT); + } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WAPI_PSK)) { + return Arrays.asList(WifiInfo.SECURITY_TYPE_WAPI_PSK); + } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) { + return Arrays.asList(WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT); + } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) { + return Arrays.asList(WifiInfo.SECURITY_TYPE_OWE); + } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) { + return Arrays.asList(WifiInfo.SECURITY_TYPE_SAE); + } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA2_PSK)) { + return Arrays.asList(WifiInfo.SECURITY_TYPE_PSK); + } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)) { + if (config.requirePmf + && !config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.TKIP) + && config.allowedProtocols.get(WifiConfiguration.Protocol.RSN)) { + return Arrays.asList(WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); + } else { + // WPA2 configs should also be valid for WPA3-Enterprise APs + return Arrays.asList( + WifiInfo.SECURITY_TYPE_EAP, WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); + } + } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { + return Arrays.asList(WifiInfo.SECURITY_TYPE_PSK); + } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) { + if (config.wepKeys != null) { + for (int i = 0; i < config.wepKeys.length; i++) { + if (config.wepKeys[i] != null) { + return Arrays.asList(WifiInfo.SECURITY_TYPE_WEP); + } + } + } } - return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE; + return Arrays.asList(WifiInfo.SECURITY_TYPE_OPEN); } /** - * Maps ScanResults into any number of WifiEntry keys each ScanResult matches. If - * chooseSingleSecurity is true, then ScanResults with multiple security capabilities will be - * matched to a single security type for the purpose of user selection. + * Returns a single WifiInfo security type from the list of multiple WifiInfo security + * types supported by an entry. * - * @param scanResults ScanResults to be mapped. - * @param chooseSingleSecurity If this is true, map scan results with multiple security - * capabilities to a single security for coalescing into a single - * WifiEntry. - * @param wifiConfigsByKey Mapping of WifiConfiguration to WifiEntry key. Only used if - * chooseSingleSecurity is true. - * @param isWpa3SaeSupported If this is false, do not map to SECURITY_SAE - * @param isWpa3SuiteBSupported If this is false, do not map to SECURITY_EAP_SUITE_B - * @param isEnhancedOpenSupported If this is false, do not map to SECURITY_OWE - * @return Map of WifiEntry key to list of corresponding ScanResults. + * Single security types will have a 1-to-1 mapping. + * Multiple security type networks will collapse to the lowest security type in the group: + * - Open/OWE -> Open + * - PSK/SAE -> PSK + * - EAP/EAP-WPA3 -> EAP */ - static Map<String, List<ScanResult>> mapScanResultsToKey( - @NonNull List<ScanResult> scanResults, - boolean chooseSingleSecurity, - @Nullable Map<String, WifiConfiguration> wifiConfigsByKey, - boolean isWpa3SaeSupported, - boolean isWpa3SuiteBSupported, - boolean isEnhancedOpenSupported) { - if (wifiConfigsByKey == null) { - wifiConfigsByKey = new HashMap<>(); - } - final Map<String, List<ScanResult>> scanResultsBySsid = scanResults.stream() - .filter(scanResult -> !TextUtils.isEmpty(scanResult.SSID)) - .collect(groupingBy(scanResult -> scanResult.SSID)); - final Map<String, List<ScanResult>> scanResultsByKey = new HashMap<>(); - - for (String ssid : scanResultsBySsid.keySet()) { - final boolean pskConfigExists = wifiConfigsByKey.containsKey( - ssidAndSecurityToStandardWifiEntryKey(ssid, SECURITY_PSK)); - final boolean saeConfigExists = wifiConfigsByKey.containsKey( - ssidAndSecurityToStandardWifiEntryKey(ssid, SECURITY_SAE)); - final boolean openConfigExists = wifiConfigsByKey.containsKey( - ssidAndSecurityToStandardWifiEntryKey(ssid, SECURITY_NONE)); - final boolean oweConfigExists = wifiConfigsByKey.containsKey( - ssidAndSecurityToStandardWifiEntryKey(ssid, SECURITY_OWE)); - - boolean pskInRange = false; - boolean saeInRange = false; - boolean oweInRange = false; - boolean openInRange = false; - for (ScanResult scan : scanResultsBySsid.get(ssid)) { - final List<Integer> securityTypes = getSecurityTypesFromScanResult(scan); - if (securityTypes.contains(SECURITY_PSK)) { - pskInRange = true; - } - if (securityTypes.contains(SECURITY_SAE)) { - saeInRange = true; - } - if (securityTypes.contains(SECURITY_OWE)) { - oweInRange = true; - } - if (securityTypes.contains(SECURITY_NONE)) { - openInRange = true; - } + static int getSingleSecurityTypeFromMultipleSecurityTypes( + @NonNull List<Integer> securityTypes) { + if (securityTypes.size() == 1) { + return securityTypes.get(0); + } else if (securityTypes.size() == 2) { + if (securityTypes.contains(WifiInfo.SECURITY_TYPE_OPEN)) { + return WifiInfo.SECURITY_TYPE_OPEN; } - - for (ScanResult scan : scanResultsBySsid.get(ssid)) { - List<Integer> securityTypes = getSecurityTypesFromScanResult(scan); - List<Integer> chosenSecurityTypes = new ArrayList<>(); - // Ignore security types that are unsupported - if (!isWpa3SaeSupported) { - securityTypes.remove((Integer) SECURITY_SAE); - } - if (!isWpa3SuiteBSupported) { - securityTypes.remove((Integer) SECURITY_EAP_SUITE_B); - } - if (!isEnhancedOpenSupported) { - securityTypes.remove((Integer) SECURITY_OWE); - } - - final boolean isSae = securityTypes.contains(SECURITY_SAE) - && !securityTypes.contains(SECURITY_PSK); - final boolean isPsk = securityTypes.contains(SECURITY_PSK) - && !securityTypes.contains(SECURITY_SAE); - final boolean isPskSaeTransition = securityTypes.contains(SECURITY_PSK) - && securityTypes.contains(SECURITY_SAE); - final boolean isOwe = securityTypes.contains(SECURITY_OWE) - && !securityTypes.contains(SECURITY_NONE); - final boolean isOweTransition = securityTypes.contains(SECURITY_NONE) - && securityTypes.contains(SECURITY_OWE); - final boolean isOpen = securityTypes.contains(SECURITY_NONE) - && !securityTypes.contains(SECURITY_OWE); - - if (chooseSingleSecurity) { - if (isPsk) { - if (!pskConfigExists && saeConfigExists && saeInRange) { - // If we don't have a PSK config, but there is an SAE AP in-range and - // an SAE config we can use for connection, then ignore the PSK AP so - // that the user only has the SAE AP to select. - continue; - } else { - chosenSecurityTypes.add(SECURITY_PSK); - } - } else if (isPskSaeTransition) { - // Map to SAE if we have an SAE config and no PSK config (use SAE config to - // connect). Else, map to PSK for wider compatibility. - if (!pskConfigExists && saeConfigExists) { - chosenSecurityTypes.add(SECURITY_SAE); - } else { - chosenSecurityTypes.add(SECURITY_PSK); - } - } else if (isSae) { - // Map to SAE if we either - // 1) have an SAE config and no PSK config (use SAE config to connect). - // 2) have no configs at all, and no PSK APs are in range. (save new - // network with SAE security). - // Else, map to PSK for wider compatibility. - if (!pskConfigExists && (saeConfigExists || !pskInRange)) { - chosenSecurityTypes.add(SECURITY_SAE); - } else { - chosenSecurityTypes.add(SECURITY_PSK); - } - } else if (isOwe) { - // If an open AP is in range, use it instead if we have a config for it and - // no OWE config. - if (openInRange && openConfigExists && !oweConfigExists) { - continue; - } else { - chosenSecurityTypes.add(SECURITY_OWE); - } - } else if (isOweTransition) { - // Map to OWE if we either - // 1) have an OWE config (use OWE config to connect). - // 2) have no configs at all (save new network with OWE security). - // Otherwise, if we have an open config only, map to open security so that - // config is used for connection. - if (oweConfigExists || !openConfigExists) { - chosenSecurityTypes.add(SECURITY_OWE); - } else { - chosenSecurityTypes.add(SECURITY_NONE); - } - } else if (isOpen) { - // If an OWE AP is in-range, then use it instead if we have a config for it - // or no configs at all. - if (oweInRange && (oweConfigExists || !openConfigExists)) { - continue; - } else { - chosenSecurityTypes.add(SECURITY_NONE); - } - } else { - chosenSecurityTypes.addAll(securityTypes); - } - } else { - chosenSecurityTypes.addAll(securityTypes); - if (isSae) { - // If we don't need to choose a single security type for the user to select, - // then SAE scans can also match to PSK configs, which will be dynamically - // upgraded to SAE by the framework at connection time. - chosenSecurityTypes.add(SECURITY_PSK); - } - } - - for (int security : chosenSecurityTypes) { - final String key = ssidAndSecurityToStandardWifiEntryKey(ssid, security); - if (!scanResultsByKey.containsKey(key)) { - scanResultsByKey.put(key, new ArrayList<>()); - } - scanResultsByKey.get(key).add(scan); - } + if (securityTypes.contains(WifiInfo.SECURITY_TYPE_PSK)) { + return WifiInfo.SECURITY_TYPE_PSK; + } + if (securityTypes.contains(WifiInfo.SECURITY_TYPE_EAP)) { + return WifiInfo.SECURITY_TYPE_EAP; } } - return scanResultsByKey; + return WifiInfo.SECURITY_TYPE_UNKNOWN; } @Speed - static int getAverageSpeedFromScanResults(@NonNull WifiNetworkScoreCache scoreCache, + public static int getAverageSpeedFromScanResults(@NonNull WifiNetworkScoreCache scoreCache, @NonNull List<ScanResult> scanResults) { int count = 0; int totalSpeed = 0; @@ -343,10 +261,16 @@ class Utils { } @Speed - static int getSpeedFromWifiInfo(@NonNull WifiNetworkScoreCache scoreCache, + public static int getSpeedFromWifiInfo(@NonNull WifiNetworkScoreCache scoreCache, @NonNull WifiInfo wifiInfo) { + final WifiKey wifiKey; + try { + wifiKey = new WifiKey(wifiInfo.getSSID(), wifiInfo.getBSSID()); + } catch (IllegalArgumentException e) { + return SPEED_NONE; + } ScoredNetwork scoredNetwork = scoreCache.getScoredNetwork( - NetworkKey.createFromWifiInfo(wifiInfo)); + new NetworkKey(wifiKey)); if (scoredNetwork == null) { return SPEED_NONE; } @@ -374,36 +298,172 @@ class Utils { static String getAppLabel(Context context, String packageName) { try { String openWifiPackageName = Settings.Global.getString(context.getContentResolver(), - Settings.Global.USE_OPEN_WIFI_PACKAGE); + SETTINGS_GLOBAL_USE_OPEN_WIFI_PACKAGE); if (!TextUtils.isEmpty(openWifiPackageName) && TextUtils.equals(packageName, getActiveScorerPackage(context))) { packageName = openWifiPackageName; } - ApplicationInfo appInfo = context.getPackageManager().getApplicationInfoAsUser( + ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo( packageName, - 0 /* flags */, - UserHandle.getUserId(UserHandle.USER_CURRENT)); + 0 /* flags */); return appInfo.loadLabel(context.getPackageManager()).toString(); } catch (PackageManager.NameNotFoundException e) { return ""; } } - static String getDisconnectedStateDescription(Context context, WifiEntry wifiEntry) { - if (context == null || wifiEntry == null) { + static String getConnectedDescription(Context context, + WifiConfiguration wifiConfiguration, + NetworkCapabilities networkCapabilities, + String recommendationServiceLabel, + boolean isDefaultNetwork, + boolean isLowQuality) { + final StringJoiner sj = new StringJoiner(context.getString( + R.string.wifitrackerlib_summary_separator)); + final boolean hideConnected = + !isDefaultNetwork && sFeatureFlagUtilsWrapper.isProviderModelEnabled(context); + + if (wifiConfiguration != null) { + if (wifiConfiguration.fromWifiNetworkSuggestion + || wifiConfiguration.fromWifiNetworkSpecifier) { + // For suggestion or specifier networks to show "Connected via ..." + final String suggestionOrSpecifierLabel = + getSuggestionOrSpecifierLabel(context, wifiConfiguration); + if (!TextUtils.isEmpty(suggestionOrSpecifierLabel)) { + if (hideConnected) { + sj.add(context.getString(R.string.wifitrackerlib_available_via_app, + suggestionOrSpecifierLabel)); + } else { + sj.add(context.getString(R.string.wifitrackerlib_connected_via_app, + suggestionOrSpecifierLabel)); + } + } + } else if (wifiConfiguration.isEphemeral() && !hideConnected) { + // For ephemeral networks to show "Automatically connected via ..." + if (!TextUtils.isEmpty(recommendationServiceLabel)) { + sj.add(String.format(context.getString( + R.string.wifitrackerlib_connected_via_network_scorer), + recommendationServiceLabel)); + } else { + sj.add(context.getString( + R.string.wifitrackerlib_connected_via_network_scorer_default)); + } + } + } + + if (isLowQuality) { + sj.add(context.getString(R.string.wifi_connected_low_quality)); + } + + // For displaying network capability info, such as captive portal or no internet + String networkCapabilitiesInformation = + getCurrentNetworkCapabilitiesInformation(context, networkCapabilities); + if (!TextUtils.isEmpty(networkCapabilitiesInformation)) { + sj.add(networkCapabilitiesInformation); + } + + // Default to "Connected" if nothing else to display + if (sj.length() == 0 && !hideConnected) { + return context.getResources().getStringArray(R.array.wifitrackerlib_wifi_status) + [DetailedState.CONNECTED.ordinal()]; + } + + return sj.toString(); + } + + static String getConnectingDescription(Context context, NetworkInfo networkInfo) { + if (context == null || networkInfo == null) { return ""; } - WifiConfiguration wifiConfiguration = wifiEntry.getWifiConfiguration(); - if (wifiConfiguration == null) { - return null; + DetailedState detailedState = networkInfo.getDetailedState(); + if (detailedState == null) { + return ""; + } + + final String[] wifiStatusArray = context.getResources() + .getStringArray(R.array.wifitrackerlib_wifi_status); + final int index = detailedState.ordinal(); + return index >= wifiStatusArray.length ? "" : wifiStatusArray[index]; + } + + + static String getDisconnectedDescription(Context context, + WifiConfiguration wifiConfiguration, + boolean forSavedNetworksPage, + boolean concise) { + if (context == null) { + return ""; + } + final StringJoiner sj = new StringJoiner(context.getString( + R.string.wifitrackerlib_summary_separator)); + + // For "Saved", "Saved by ...", and "Available via..." + if (concise) { + sj.add(context.getString(R.string.wifitrackerlib_wifi_disconnected)); + } else if (wifiConfiguration != null) { + if (forSavedNetworksPage && !wifiConfiguration.isPasspoint()) { + final CharSequence appLabel = getAppLabel(context, wifiConfiguration.creatorName); + if (!TextUtils.isEmpty(appLabel)) { + sj.add(context.getString(R.string.wifitrackerlib_saved_network, appLabel)); + } + } else { + if (wifiConfiguration.fromWifiNetworkSuggestion) { + final String suggestionOrSpecifierLabel = + getSuggestionOrSpecifierLabel(context, wifiConfiguration); + if (!TextUtils.isEmpty(suggestionOrSpecifierLabel)) { + sj.add(context.getString( + R.string.wifitrackerlib_available_via_app, + suggestionOrSpecifierLabel)); + } + } else { + sj.add(context.getString(R.string.wifitrackerlib_wifi_remembered)); + } + } } + // For failure messages and disabled reasons + final String wifiConfigFailureMessage = + getWifiConfigurationFailureMessage(context, wifiConfiguration); + if (!TextUtils.isEmpty(wifiConfigFailureMessage)) { + sj.add(wifiConfigFailureMessage); + } + + return sj.toString(); + } + + private static String getSuggestionOrSpecifierLabel( + Context context, WifiConfiguration wifiConfiguration) { + if (context == null || wifiConfiguration == null) { + return ""; + } + + final String carrierName = getCarrierNameForSubId(context, + getSubIdForConfig(context, wifiConfiguration)); + if (!TextUtils.isEmpty(carrierName)) { + return carrierName; + } + final String suggestorLabel = getAppLabel(context, wifiConfiguration.creatorName); + if (!TextUtils.isEmpty(suggestorLabel)) { + return suggestorLabel; + } + // Fall-back to the package name in case the app label is missing + return wifiConfiguration.creatorName; + } + + private static String getWifiConfigurationFailureMessage( + Context context, WifiConfiguration wifiConfiguration) { + if (context == null || wifiConfiguration == null) { + return ""; + } + + // Check for any failure messages to display if (wifiConfiguration.hasNoInternetAccess()) { int messageID = wifiConfiguration.getNetworkSelectionStatus().getNetworkSelectionStatus() == NETWORK_SELECTION_PERMANENTLY_DISABLED - ? R.string.wifi_no_internet_no_reconnect : R.string.wifi_no_internet; + ? R.string.wifitrackerlib_wifi_no_internet_no_reconnect + : R.string.wifitrackerlib_wifi_no_internet; return context.getString(messageID); } else if (wifiConfiguration.getNetworkSelectionStatus().getNetworkSelectionStatus() != NETWORK_SELECTION_ENABLED) { @@ -411,22 +471,47 @@ class Utils { wifiConfiguration.getNetworkSelectionStatus(); switch (networkStatus.getNetworkSelectionDisableReason()) { case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE: - return context.getString(R.string.wifi_disabled_password_failure); + case WifiConfiguration.NetworkSelectionStatus + .DISABLED_AUTHENTICATION_NO_SUBSCRIPTION: + return context.getString( + R.string.wifitrackerlib_wifi_disabled_password_failure); case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD: - return context.getString(R.string.wifi_check_password_try_again); + return context.getString(R.string.wifitrackerlib_wifi_check_password_try_again); case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE: - return context.getString(R.string.wifi_disabled_network_failure); + return context.getString(R.string.wifitrackerlib_wifi_disabled_network_failure); case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION: - return context.getString(R.string.wifi_disabled_generic); + return context.getString(R.string.wifitrackerlib_wifi_disabled_generic); + case WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT: + case WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY: + return context.getString(R.string.wifitrackerlib_wifi_no_internet_no_reconnect); default: break; } - } else if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) { - // Do nothing because users know it by signal icon. } else { // In range, not disabled. - if (wifiConfiguration.getRecentFailureReason() - == WifiConfiguration.RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA) { - return context.getString(R.string.wifi_ap_unable_to_handle_new_sta); + switch (wifiConfiguration.getRecentFailureReason()) { + case WifiConfiguration.RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA: + case WifiConfiguration.RECENT_FAILURE_REFUSED_TEMPORARILY: + case WifiConfiguration.RECENT_FAILURE_DISCONNECTION_AP_BUSY: + return context.getString(R.string + .wifitrackerlib_wifi_ap_unable_to_handle_new_sta); + case WifiConfiguration.RECENT_FAILURE_POOR_CHANNEL_CONDITIONS: + return context.getString(R.string.wifitrackerlib_wifi_poor_channel_conditions); + case WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_UNSPECIFIED: + case WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_AIR_INTERFACE_OVERLOADED: + case WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_AUTH_SERVER_OVERLOADED: + return context.getString(R.string + .wifitrackerlib_wifi_mbo_assoc_disallowed_cannot_connect); + case WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_MAX_NUM_STA_ASSOCIATED: + return context.getString(R.string + .wifitrackerlib_wifi_mbo_assoc_disallowed_max_num_sta_associated); + case WifiConfiguration.RECENT_FAILURE_MBO_ASSOC_DISALLOWED_INSUFFICIENT_RSSI: + case WifiConfiguration.RECENT_FAILURE_OCE_RSSI_BASED_ASSOCIATION_REJECTION: + return context.getString(R.string + .wifitrackerlib_wifi_mbo_oce_assoc_disallowed_insufficient_rssi); + case WifiConfiguration.RECENT_FAILURE_NETWORK_NOT_FOUND: + return context.getString(R.string.wifitrackerlib_wifi_network_not_found); + default: + // do nothing } } return ""; @@ -439,7 +524,7 @@ class Utils { } return wifiEntry.isAutoJoinEnabled() - ? "" : context.getString(R.string.auto_connect_disable); + ? "" : context.getString(R.string.wifitrackerlib_auto_connect_disable); } static String getMeteredDescription(@NonNull Context context, @Nullable WifiEntry wifiEntry) { @@ -453,11 +538,12 @@ class Utils { } if (wifiEntry.getMeteredChoice() == WifiEntry.METERED_CHOICE_METERED) { - return context.getString(R.string.wifi_metered_label); + return context.getString(R.string.wifitrackerlib_wifi_metered_label); } else if (wifiEntry.getMeteredChoice() == WifiEntry.METERED_CHOICE_UNMETERED) { - return context.getString(R.string.wifi_unmetered_label); + return context.getString(R.string.wifitrackerlib_wifi_unmetered_label); } else { // METERED_CHOICE_AUTO - return wifiEntry.isMetered() ? context.getString(R.string.wifi_metered_label) : ""; + return wifiEntry.isMetered() ? context.getString( + R.string.wifitrackerlib_wifi_metered_label) : ""; } } @@ -469,13 +555,13 @@ class Utils { @Speed int speed = wifiEntry.getSpeed(); switch (speed) { case SPEED_VERY_FAST: - return context.getString(R.string.speed_label_very_fast); + return context.getString(R.string.wifitrackerlib_speed_label_very_fast); case SPEED_FAST: - return context.getString(R.string.speed_label_fast); + return context.getString(R.string.wifitrackerlib_speed_label_fast); case SPEED_MODERATE: - return context.getString(R.string.speed_label_okay); + return context.getString(R.string.wifitrackerlib_speed_label_okay); case SPEED_SLOW: - return context.getString(R.string.speed_label_slow); + return context.getString(R.string.wifitrackerlib_speed_label_slow); case SPEED_NONE: default: return ""; @@ -558,30 +644,34 @@ class Utils { if (networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) { - return context.getString(R.string.wifi_limited_connection); + return context.getString(R.string.wifitrackerlib_wifi_limited_connection); } if (!networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { if (networkCapabilities.isPrivateDnsBroken()) { - return context.getString(R.string.private_dns_broken); + return context.getString(R.string.wifitrackerlib_private_dns_broken); } - return context.getString(R.string.wifi_connected_no_internet); + return context.getString( + R.string.wifitrackerlib_wifi_connected_cannot_provide_internet); } return ""; } + /** + * Returns the display string corresponding to the detailed state of the given NetworkInfo + */ static String getNetworkDetailedState(Context context, NetworkInfo networkInfo) { if (context == null || networkInfo == null) { return ""; } - DetailedState detailState = networkInfo.getDetailedState(); - if (detailState == null) { + DetailedState detailedState = networkInfo.getDetailedState(); + if (detailedState == null) { return ""; } String[] wifiStatusArray = context.getResources() - .getStringArray(R.array.wifi_status); - int index = detailState.ordinal(); + .getStringArray(R.array.wifitrackerlib_wifi_status); + int index = detailedState.ordinal(); return index >= wifiStatusArray.length ? "" : wifiStatusArray[index]; } @@ -694,8 +784,9 @@ class Utils { } // IMSI protection is not provided, return warning message. - return linkifyAnnotation(context, context.getText(R.string.imsi_protection_warning), "url", - context.getString(R.string.help_url_imsi_protection)); + return linkifyAnnotation(context, context.getText( + R.string.wifitrackerlib_imsi_protection_warning), "url", + context.getString(R.string.wifitrackerlib_help_url_imsi_protection)); } /** Find the annotation of specified id in rawText and linkify it with helpUriString. */ @@ -727,4 +818,195 @@ class Utils { } return rawText; } + + // Various utility methods copied from com.android.server.wifi.util.ScanResultUtils for + // extracting SecurityType from ScanResult. + + /** + * Helper method to check if the provided |scanResult| corresponds to a PSK network or not. + * This checks if the provided capabilities string contains PSK encryption type or not. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForPskNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("PSK"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to a WAPI-PSK network or not. + * This checks if the provided capabilities string contains PSK encryption type or not. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForWapiPskNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("WAPI-PSK"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to a WAPI-CERT + * network or not. + * This checks if the provided capabilities string contains PSK encryption type or not. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForWapiCertNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("WAPI-CERT"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to a EAP network or not. + * This checks these conditions: + * - Enable EAP/SHA1, EAP/SHA256 AKM, FT/EAP, or EAP-FILS. + * - Not a WPA3 Enterprise only network. + * - Not a WPA3 Enterprise transition network. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForEapNetwork(ScanResult scanResult) { + return (scanResult.capabilities.contains("EAP/SHA1") + || scanResult.capabilities.contains("EAP/SHA256") + || scanResult.capabilities.contains("FT/EAP") + || scanResult.capabilities.contains("EAP-FILS")) + && !isScanResultForWpa3EnterpriseOnlyNetwork(scanResult) + && !isScanResultForWpa3EnterpriseTransitionNetwork(scanResult); + } + + // TODO(b/187755981): Move to shared static utils class + private static boolean isScanResultForPmfMandatoryNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("[MFPR]"); + } + + // TODO(b/187755981): Move to shared static utils class + private static boolean isScanResultForPmfCapableNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("[MFPC]"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to + * a WPA3 Enterprise transition network or not. + * + * See Section 3.3 WPA3-Enterprise transition mode in WPA3 Specification + * - Enable at least EAP/SHA1 and EAP/SHA256 AKM suites. + * - Not enable WPA1 version 1, WEP, and TKIP. + * - Management Frame Protection Capable is set. + * - Management Frame Protection Required is not set. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForWpa3EnterpriseTransitionNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("EAP/SHA1") + && scanResult.capabilities.contains("EAP/SHA256") + && scanResult.capabilities.contains("RSN") + && !scanResult.capabilities.contains("WEP") + && !scanResult.capabilities.contains("TKIP") + && !isScanResultForPmfMandatoryNetwork(scanResult) + && isScanResultForPmfCapableNetwork(scanResult); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to + * a WPA3 Enterprise only network or not. + * + * See Section 3.2 WPA3-Enterprise only mode in WPA3 Specification + * - Enable at least EAP/SHA256 AKM suite. + * - Not enable EAP/SHA1 AKM suite. + * - Not enable WPA1 version 1, WEP, and TKIP. + * - Management Frame Protection Capable is set. + * - Management Frame Protection Required is set. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForWpa3EnterpriseOnlyNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("EAP/SHA256") + && !scanResult.capabilities.contains("EAP/SHA1") + && scanResult.capabilities.contains("RSN") + && !scanResult.capabilities.contains("WEP") + && !scanResult.capabilities.contains("TKIP") + && isScanResultForPmfMandatoryNetwork(scanResult) + && isScanResultForPmfCapableNetwork(scanResult); + } + + + /** + * Helper method to check if the provided |scanResult| corresponds to a WPA3-Enterprise 192-bit + * mode network or not. + * This checks if the provided capabilities comply these conditions: + * - Enable SUITE-B-192 AKM. + * - Not enable EAP/SHA1 AKM suite. + * - Not enable WPA1 version 1, WEP, and TKIP. + * - Management Frame Protection Required is set. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForEapSuiteBNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("SUITE_B_192") + && scanResult.capabilities.contains("RSN") + && !scanResult.capabilities.contains("WEP") + && !scanResult.capabilities.contains("TKIP") + && isScanResultForPmfMandatoryNetwork(scanResult); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to a WEP network or not. + * This checks if the provided capabilities string contains WEP encryption type or not. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForWepNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("WEP"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to OWE network. + * This checks if the provided capabilities string contains OWE or not. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForOweNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("OWE"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to OWE transition network. + * This checks if the provided capabilities string contains OWE_TRANSITION or not. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForOweTransitionNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("OWE_TRANSITION"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to SAE network. + * This checks if the provided capabilities string contains SAE or not. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForSaeNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("SAE"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to PSK-SAE transition + * network. This checks if the provided capabilities string contains both PSK and SAE or not. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForPskSaeTransitionNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("PSK") && scanResult.capabilities.contains("SAE"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to an unknown amk network. + * This checks if the provided capabilities string contains ? or not. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForUnknownAkmNetwork(ScanResult scanResult) { + return scanResult.capabilities.contains("?"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to an open network or not. + * This checks if the provided capabilities string does not contain either of WEP, PSK, SAE + * EAP, or unknown encryption types or not. + * TODO(b/187755981): Move to shared static utils class + */ + public static boolean isScanResultForOpenNetwork(ScanResult scanResult) { + return (!(isScanResultForWepNetwork(scanResult) || isScanResultForPskNetwork(scanResult) + || isScanResultForEapNetwork(scanResult) || isScanResultForSaeNetwork(scanResult) + || isScanResultForWpa3EnterpriseTransitionNetwork(scanResult) + || isScanResultForWpa3EnterpriseOnlyNetwork(scanResult) + || isScanResultForWapiPskNetwork(scanResult) + || isScanResultForWapiCertNetwork(scanResult) + || isScanResultForEapSuiteBNetwork(scanResult) + || isScanResultForUnknownAkmNetwork(scanResult))); + } } |