1 /* 2 * Copyright (C) 2017 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.settings.wifi; 18 19 import android.app.admin.DevicePolicyManager; 20 import android.content.ComponentName; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.pm.PackageManager; 24 import android.net.NetworkCapabilities; 25 import android.net.TetheringManager; 26 import android.net.wifi.ScanResult; 27 import android.net.wifi.SoftApConfiguration; 28 import android.net.wifi.WifiConfiguration; 29 import android.net.wifi.WifiManager; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.provider.Settings; 33 import android.text.TextUtils; 34 import android.util.Log; 35 36 import androidx.annotation.VisibleForTesting; 37 38 import com.android.settings.R; 39 import com.android.settings.Utils; 40 import com.android.wifitrackerlib.WifiEntry; 41 42 import java.nio.charset.StandardCharsets; 43 44 /** A utility class for Wi-Fi functions. */ 45 public class WifiUtils extends com.android.settingslib.wifi.WifiUtils { 46 47 static final String TAG = "WifiUtils"; 48 49 private static final int SSID_ASCII_MIN_LENGTH = 1; 50 private static final int SSID_ASCII_MAX_LENGTH = 32; 51 52 private static final int PSK_PASSPHRASE_ASCII_MIN_LENGTH = 8; 53 private static final int PSK_PASSPHRASE_ASCII_MAX_LENGTH = 63; 54 55 private static Boolean sCanShowWifiHotspotCached; 56 isSSIDTooLong(String ssid)57 public static boolean isSSIDTooLong(String ssid) { 58 if (TextUtils.isEmpty(ssid)) { 59 return false; 60 } 61 return ssid.getBytes(StandardCharsets.UTF_8).length > SSID_ASCII_MAX_LENGTH; 62 } 63 isSSIDTooShort(String ssid)64 public static boolean isSSIDTooShort(String ssid) { 65 if (TextUtils.isEmpty(ssid)) { 66 return true; 67 } 68 return ssid.length() < SSID_ASCII_MIN_LENGTH; 69 } 70 71 /** 72 * Check if the hotspot password is valid. 73 */ isHotspotPasswordValid(String password, int securityType)74 public static boolean isHotspotPasswordValid(String password, int securityType) { 75 final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); 76 try { 77 if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK 78 || securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) { 79 if (password.length() < PSK_PASSPHRASE_ASCII_MIN_LENGTH 80 || password.length() > PSK_PASSPHRASE_ASCII_MAX_LENGTH) { 81 return false; 82 } 83 } 84 configBuilder.setPassphrase(password, securityType); 85 } catch (Exception e) { 86 return false; 87 } 88 return true; 89 } 90 91 /** 92 * This method is a stripped and negated version of WifiConfigStore.canModifyNetwork. 93 * 94 * @param context Context of caller 95 * @param config The WiFi config. 96 * @return true if Settings cannot modify the config due to lockDown. 97 */ isNetworkLockedDown(Context context, WifiConfiguration config)98 public static boolean isNetworkLockedDown(Context context, WifiConfiguration config) { 99 if (context == null || config == null) { 100 return false; 101 } 102 103 final DevicePolicyManager dpm = 104 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 105 final PackageManager pm = context.getPackageManager(); 106 final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 107 108 // Check if device has DPM capability. If it has and dpm is still null, then we 109 // treat this case with suspicion and bail out. 110 if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) { 111 return true; 112 } 113 114 boolean isConfigEligibleForLockdown = false; 115 if (dpm != null) { 116 final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser(); 117 if (deviceOwner != null) { 118 final int deviceOwnerUserId = dpm.getDeviceOwnerUserId(); 119 try { 120 final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(), 121 deviceOwnerUserId); 122 isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid; 123 } catch (PackageManager.NameNotFoundException e) { 124 // don't care 125 } 126 } else if (dpm.isOrganizationOwnedDeviceWithManagedProfile()) { 127 int profileOwnerUserId = Utils.getManagedProfileId(um, UserHandle.myUserId()); 128 final ComponentName profileOwner = dpm.getProfileOwnerAsUser(profileOwnerUserId); 129 if (profileOwner != null) { 130 try { 131 final int profileOwnerUid = pm.getPackageUidAsUser( 132 profileOwner.getPackageName(), profileOwnerUserId); 133 isConfigEligibleForLockdown = profileOwnerUid == config.creatorUid; 134 } catch (PackageManager.NameNotFoundException e) { 135 // don't care 136 } 137 } 138 } 139 } 140 if (!isConfigEligibleForLockdown) { 141 return false; 142 } 143 144 final ContentResolver resolver = context.getContentResolver(); 145 final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver, 146 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0; 147 return isLockdownFeatureEnabled; 148 } 149 150 /** Returns true if the provided NetworkCapabilities indicate a captive portal network. */ canSignIntoNetwork(NetworkCapabilities capabilities)151 public static boolean canSignIntoNetwork(NetworkCapabilities capabilities) { 152 return (capabilities != null 153 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)); 154 } 155 156 /** 157 * Provides a simple way to generate a new {@link WifiConfiguration} obj from 158 * {@link ScanResult} or {@link WifiEntry}. Either {@code wifiEntry} or {@code scanResult 159 * } input should be not null for retrieving information, otherwise will throw 160 * IllegalArgumentException. 161 * This method prefers to take {@link WifiEntry} input in priority. Therefore this method 162 * will take {@link WifiEntry} input as preferred data extraction source when you input 163 * both {@link WifiEntry} and {@link ScanResult}, and ignore {@link ScanResult} input. 164 * 165 * Duplicated and simplified method from {@link WifiConfigController#getConfig()}. 166 * TODO(b/120827021): Should be removed if the there is have a common one in shared place (e.g. 167 * SettingsLib). 168 * 169 * @param wifiEntry Input data for retrieving WifiConfiguration. 170 * @param scanResult Input data for retrieving WifiConfiguration. 171 * @return WifiConfiguration obj based on input. 172 */ getWifiConfig(WifiEntry wifiEntry, ScanResult scanResult)173 public static WifiConfiguration getWifiConfig(WifiEntry wifiEntry, ScanResult scanResult) { 174 if (wifiEntry == null && scanResult == null) { 175 throw new IllegalArgumentException( 176 "At least one of WifiEntry and ScanResult input is required."); 177 } 178 179 final WifiConfiguration config = new WifiConfiguration(); 180 final int security; 181 182 if (wifiEntry == null) { 183 config.SSID = "\"" + scanResult.SSID + "\""; 184 security = getWifiEntrySecurity(scanResult); 185 } else { 186 if (wifiEntry.getWifiConfiguration() == null) { 187 config.SSID = "\"" + wifiEntry.getSsid() + "\""; 188 } else { 189 config.networkId = wifiEntry.getWifiConfiguration().networkId; 190 config.hiddenSSID = wifiEntry.getWifiConfiguration().hiddenSSID; 191 } 192 security = wifiEntry.getSecurity(); 193 } 194 195 switch (security) { 196 case WifiEntry.SECURITY_NONE: 197 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); 198 break; 199 200 case WifiEntry.SECURITY_WEP: 201 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP); 202 break; 203 204 case WifiEntry.SECURITY_PSK: 205 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); 206 break; 207 208 case WifiEntry.SECURITY_EAP_SUITE_B: 209 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); 210 break; 211 212 case WifiEntry.SECURITY_EAP: 213 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); 214 break; 215 216 case WifiEntry.SECURITY_SAE: 217 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE); 218 break; 219 220 case WifiEntry.SECURITY_OWE: 221 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); 222 break; 223 224 default: 225 break; 226 } 227 return config; 228 } 229 230 /** 231 * Gets security value from ScanResult. 232 * 233 * @param result ScanResult 234 * @return Related security value based on {@link WifiEntry}. 235 */ getWifiEntrySecurity(ScanResult result)236 public static int getWifiEntrySecurity(ScanResult result) { 237 if (result.capabilities.contains("WEP")) { 238 return WifiEntry.SECURITY_WEP; 239 } else if (result.capabilities.contains("SAE")) { 240 return WifiEntry.SECURITY_SAE; 241 } else if (result.capabilities.contains("PSK")) { 242 return WifiEntry.SECURITY_PSK; 243 } else if (result.capabilities.contains("EAP_SUITE_B_192")) { 244 return WifiEntry.SECURITY_EAP_SUITE_B; 245 } else if (result.capabilities.contains("EAP")) { 246 return WifiEntry.SECURITY_EAP; 247 } else if (result.capabilities.contains("OWE")) { 248 return WifiEntry.SECURITY_OWE; 249 } 250 251 return WifiEntry.SECURITY_NONE; 252 } 253 254 /** 255 * Check if Wi-Fi hotspot settings can be displayed. 256 * 257 * @param context Context of caller 258 * @return true if Wi-Fi hotspot settings can be displayed 259 */ checkShowWifiHotspot(Context context)260 public static boolean checkShowWifiHotspot(Context context) { 261 if (context == null) return false; 262 263 boolean showWifiHotspotSettings = 264 context.getResources().getBoolean(R.bool.config_show_wifi_hotspot_settings); 265 if (!showWifiHotspotSettings) { 266 Log.w(TAG, "config_show_wifi_hotspot_settings:false"); 267 return false; 268 } 269 270 WifiManager wifiManager = context.getSystemService(WifiManager.class); 271 if (wifiManager == null) { 272 Log.e(TAG, "WifiManager is null"); 273 return false; 274 } 275 276 TetheringManager tetheringManager = context.getSystemService(TetheringManager.class); 277 if (tetheringManager == null) { 278 Log.e(TAG, "TetheringManager is null"); 279 return false; 280 } 281 String[] wifiRegexs = tetheringManager.getTetherableWifiRegexs(); 282 if (wifiRegexs == null || wifiRegexs.length == 0) { 283 Log.w(TAG, "TetherableWifiRegexs is empty"); 284 return false; 285 } 286 return true; 287 } 288 289 /** 290 * Return the cached result to see if Wi-Fi hotspot settings can be displayed. 291 * 292 * @param context Context of caller 293 * @return true if Wi-Fi hotspot settings can be displayed 294 */ canShowWifiHotspot(Context context)295 public static boolean canShowWifiHotspot(Context context) { 296 if (sCanShowWifiHotspotCached == null) { 297 sCanShowWifiHotspotCached = checkShowWifiHotspot(context); 298 } 299 return sCanShowWifiHotspotCached; 300 } 301 302 /** 303 * Sets the sCanShowWifiHotspotCached for testing purposes. 304 * 305 * @param cached Cached value for #canShowWifiHotspot() 306 */ 307 @VisibleForTesting setCanShowWifiHotspotCached(Boolean cached)308 public static void setCanShowWifiHotspotCached(Boolean cached) { 309 sCanShowWifiHotspotCached = cached; 310 } 311 } 312