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