| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.settings.wifi; |
| |
| import android.app.admin.DevicePolicyManager; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.net.NetworkCapabilities; |
| import android.net.TetheringManager; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.SoftApConfiguration; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.WifiManager; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.provider.Settings; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import androidx.annotation.VisibleForTesting; |
| |
| import com.android.settings.R; |
| import com.android.settings.Utils; |
| import com.android.wifitrackerlib.WifiEntry; |
| |
| import java.nio.charset.StandardCharsets; |
| |
| /** A utility class for Wi-Fi functions. */ |
| public class WifiUtils extends com.android.settingslib.wifi.WifiUtils { |
| |
| static final String TAG = "WifiUtils"; |
| |
| private static final int SSID_ASCII_MIN_LENGTH = 1; |
| private static final int SSID_ASCII_MAX_LENGTH = 32; |
| |
| private static final int PSK_PASSPHRASE_ASCII_MIN_LENGTH = 8; |
| private static final int PSK_PASSPHRASE_ASCII_MAX_LENGTH = 63; |
| |
| private static Boolean sCanShowWifiHotspotCached; |
| |
| public static boolean isSSIDTooLong(String ssid) { |
| if (TextUtils.isEmpty(ssid)) { |
| return false; |
| } |
| return ssid.getBytes(StandardCharsets.UTF_8).length > SSID_ASCII_MAX_LENGTH; |
| } |
| |
| public static boolean isSSIDTooShort(String ssid) { |
| if (TextUtils.isEmpty(ssid)) { |
| return true; |
| } |
| return ssid.length() < SSID_ASCII_MIN_LENGTH; |
| } |
| |
| /** |
| * Check if the hotspot password is valid. |
| */ |
| public static boolean isHotspotPasswordValid(String password, int securityType) { |
| final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); |
| try { |
| if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK |
| || securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) { |
| if (password.length() < PSK_PASSPHRASE_ASCII_MIN_LENGTH |
| || password.length() > PSK_PASSPHRASE_ASCII_MAX_LENGTH) { |
| return false; |
| } |
| } |
| configBuilder.setPassphrase(password, securityType); |
| } catch (Exception e) { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * This method is a stripped and negated version of WifiConfigStore.canModifyNetwork. |
| * |
| * @param context Context of caller |
| * @param config The WiFi config. |
| * @return true if Settings cannot modify the config due to lockDown. |
| */ |
| public static boolean isNetworkLockedDown(Context context, WifiConfiguration config) { |
| if (config == null) { |
| return false; |
| } |
| |
| final DevicePolicyManager dpm = |
| (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); |
| final PackageManager pm = context.getPackageManager(); |
| final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); |
| |
| // Check if device has DPM capability. If it has and dpm is still null, then we |
| // treat this case with suspicion and bail out. |
| if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) { |
| return true; |
| } |
| |
| boolean isConfigEligibleForLockdown = false; |
| if (dpm != null) { |
| final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser(); |
| if (deviceOwner != null) { |
| final int deviceOwnerUserId = dpm.getDeviceOwnerUserId(); |
| try { |
| final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(), |
| deviceOwnerUserId); |
| isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid; |
| } catch (PackageManager.NameNotFoundException e) { |
| // don't care |
| } |
| } else if (dpm.isOrganizationOwnedDeviceWithManagedProfile()) { |
| int profileOwnerUserId = Utils.getManagedProfileId(um, UserHandle.myUserId()); |
| final ComponentName profileOwner = dpm.getProfileOwnerAsUser(profileOwnerUserId); |
| if (profileOwner != null) { |
| try { |
| final int profileOwnerUid = pm.getPackageUidAsUser( |
| profileOwner.getPackageName(), profileOwnerUserId); |
| isConfigEligibleForLockdown = profileOwnerUid == config.creatorUid; |
| } catch (PackageManager.NameNotFoundException e) { |
| // don't care |
| } |
| } |
| } |
| } |
| if (!isConfigEligibleForLockdown) { |
| return false; |
| } |
| |
| final ContentResolver resolver = context.getContentResolver(); |
| final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver, |
| Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0; |
| return isLockdownFeatureEnabled; |
| } |
| |
| /** Returns true if the provided NetworkCapabilities indicate a captive portal network. */ |
| public static boolean canSignIntoNetwork(NetworkCapabilities capabilities) { |
| return (capabilities != null |
| && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)); |
| } |
| |
| /** |
| * Provides a simple way to generate a new {@link WifiConfiguration} obj from |
| * {@link ScanResult} or {@link WifiEntry}. Either {@code wifiEntry} or {@code scanResult |
| * } input should be not null for retrieving information, otherwise will throw |
| * IllegalArgumentException. |
| * This method prefers to take {@link WifiEntry} input in priority. Therefore this method |
| * will take {@link WifiEntry} input as preferred data extraction source when you input |
| * both {@link WifiEntry} and {@link ScanResult}, and ignore {@link ScanResult} input. |
| * |
| * Duplicated and simplified method from {@link WifiConfigController#getConfig()}. |
| * TODO(b/120827021): Should be removed if the there is have a common one in shared place (e.g. |
| * SettingsLib). |
| * |
| * @param wifiEntry Input data for retrieving WifiConfiguration. |
| * @param scanResult Input data for retrieving WifiConfiguration. |
| * @return WifiConfiguration obj based on input. |
| */ |
| public static WifiConfiguration getWifiConfig(WifiEntry wifiEntry, ScanResult scanResult) { |
| if (wifiEntry == null && scanResult == null) { |
| throw new IllegalArgumentException( |
| "At least one of WifiEntry and ScanResult input is required."); |
| } |
| |
| final WifiConfiguration config = new WifiConfiguration(); |
| final int security; |
| |
| if (wifiEntry == null) { |
| config.SSID = "\"" + scanResult.SSID + "\""; |
| security = getWifiEntrySecurity(scanResult); |
| } else { |
| if (wifiEntry.getWifiConfiguration() == null) { |
| config.SSID = "\"" + wifiEntry.getSsid() + "\""; |
| } else { |
| config.networkId = wifiEntry.getWifiConfiguration().networkId; |
| config.hiddenSSID = wifiEntry.getWifiConfiguration().hiddenSSID; |
| } |
| security = wifiEntry.getSecurity(); |
| } |
| |
| switch (security) { |
| case WifiEntry.SECURITY_NONE: |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); |
| break; |
| |
| case WifiEntry.SECURITY_WEP: |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP); |
| break; |
| |
| case WifiEntry.SECURITY_PSK: |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); |
| break; |
| |
| case WifiEntry.SECURITY_EAP_SUITE_B: |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); |
| break; |
| |
| case WifiEntry.SECURITY_EAP: |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); |
| break; |
| |
| case WifiEntry.SECURITY_SAE: |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE); |
| break; |
| |
| case WifiEntry.SECURITY_OWE: |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); |
| break; |
| |
| default: |
| break; |
| } |
| return config; |
| } |
| |
| /** |
| * Gets security value from ScanResult. |
| * |
| * @param result ScanResult |
| * @return Related security value based on {@link WifiEntry}. |
| */ |
| public static int getWifiEntrySecurity(ScanResult result) { |
| if (result.capabilities.contains("WEP")) { |
| return WifiEntry.SECURITY_WEP; |
| } else if (result.capabilities.contains("SAE")) { |
| return WifiEntry.SECURITY_SAE; |
| } else if (result.capabilities.contains("PSK")) { |
| return WifiEntry.SECURITY_PSK; |
| } else if (result.capabilities.contains("EAP_SUITE_B_192")) { |
| return WifiEntry.SECURITY_EAP_SUITE_B; |
| } else if (result.capabilities.contains("EAP")) { |
| return WifiEntry.SECURITY_EAP; |
| } else if (result.capabilities.contains("OWE")) { |
| return WifiEntry.SECURITY_OWE; |
| } |
| |
| return WifiEntry.SECURITY_NONE; |
| } |
| |
| /** |
| * Check if Wi-Fi hotspot settings can be displayed. |
| * |
| * @param context Context of caller |
| * @return true if Wi-Fi hotspot settings can be displayed |
| */ |
| public static boolean checkShowWifiHotspot(Context context) { |
| if (context == null) return false; |
| |
| boolean showWifiHotspotSettings = |
| context.getResources().getBoolean(R.bool.config_show_wifi_hotspot_settings); |
| if (!showWifiHotspotSettings) { |
| Log.w(TAG, "config_show_wifi_hotspot_settings:false"); |
| return false; |
| } |
| |
| WifiManager wifiManager = context.getSystemService(WifiManager.class); |
| if (wifiManager == null) { |
| Log.e(TAG, "WifiManager is null"); |
| return false; |
| } |
| |
| TetheringManager tetheringManager = context.getSystemService(TetheringManager.class); |
| if (tetheringManager == null) { |
| Log.e(TAG, "TetheringManager is null"); |
| return false; |
| } |
| String[] wifiRegexs = tetheringManager.getTetherableWifiRegexs(); |
| if (wifiRegexs == null || wifiRegexs.length == 0) { |
| Log.w(TAG, "TetherableWifiRegexs is empty"); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Return the cached result to see if Wi-Fi hotspot settings can be displayed. |
| * |
| * @param context Context of caller |
| * @return true if Wi-Fi hotspot settings can be displayed |
| */ |
| public static boolean canShowWifiHotspot(Context context) { |
| if (sCanShowWifiHotspotCached == null) { |
| sCanShowWifiHotspotCached = checkShowWifiHotspot(context); |
| } |
| return sCanShowWifiHotspotCached; |
| } |
| |
| /** |
| * Sets the sCanShowWifiHotspotCached for testing purposes. |
| * |
| * @param cached Cached value for #canShowWifiHotspot() |
| */ |
| @VisibleForTesting |
| public static void setCanShowWifiHotspotCached(Boolean cached) { |
| sCanShowWifiHotspotCached = cached; |
| } |
| } |