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