1 /* 2 * Copyright (C) 2016 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.server.wifi; 18 19 import android.content.pm.UserInfo; 20 import android.net.IpConfiguration; 21 import android.net.wifi.WifiConfiguration; 22 import android.net.wifi.WifiEnterpriseConfig; 23 import android.net.wifi.WifiScanner; 24 import android.os.UserHandle; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import java.security.cert.X509Certificate; 29 import java.util.Arrays; 30 import java.util.Comparator; 31 import java.util.List; 32 import java.util.Objects; 33 34 /** 35 * WifiConfiguration utility for any {@link android.net.wifi.WifiConfiguration} related operations. 36 * Currently contains: 37 * > Helper method to check if the WifiConfiguration object is visible to the provided users. 38 * > Helper methods to identify the encryption of a WifiConfiguration object. 39 */ 40 public class WifiConfigurationUtil { 41 /** 42 * Check whether a network configuration is visible to a user or any of its managed profiles. 43 * 44 * @param config the network configuration whose visibility should be checked 45 * @param profiles the user IDs of the user itself and all its managed profiles (can be obtained 46 * via {@link android.os.UserManager#getProfiles}) 47 * @return whether the network configuration is visible to the user or any of its managed 48 * profiles 49 */ 50 public static boolean isVisibleToAnyProfile(WifiConfiguration config, List<UserInfo> profiles) { 51 return (config.shared || doesUidBelongToAnyProfile(config.creatorUid, profiles)); 52 } 53 54 /** 55 * Check whether a uid belong to a user or any of its managed profiles. 56 * 57 * @param uid uid of the app. 58 * @param profiles the user IDs of the user itself and all its managed profiles (can be obtained 59 * via {@link android.os.UserManager#getProfiles}) 60 * @return whether the uid belongs to the user or any of its managed profiles. 61 */ 62 public static boolean doesUidBelongToAnyProfile(int uid, List<UserInfo> profiles) { 63 final int userId = UserHandle.getUserId(uid); 64 for (UserInfo profile : profiles) { 65 if (profile.id == userId) { 66 return true; 67 } 68 } 69 return false; 70 } 71 72 /** 73 * Checks if the provided |wepKeys| array contains any non-null value; 74 */ 75 public static boolean hasAnyValidWepKey(String[] wepKeys) { 76 for (int i = 0; i < wepKeys.length; i++) { 77 if (wepKeys[i] != null) { 78 return true; 79 } 80 } 81 return false; 82 } 83 84 /** 85 * Helper method to check if the provided |config| corresponds to a PSK network or not. 86 */ 87 public static boolean isConfigForPskNetwork(WifiConfiguration config) { 88 return config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK); 89 } 90 91 /** 92 * Helper method to check if the provided |config| corresponds to a EAP network or not. 93 */ 94 public static boolean isConfigForEapNetwork(WifiConfiguration config) { 95 return (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) 96 || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)); 97 } 98 99 /** 100 * Helper method to check if the provided |config| corresponds to a WEP network or not. 101 */ 102 public static boolean isConfigForWepNetwork(WifiConfiguration config) { 103 return (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE) 104 && hasAnyValidWepKey(config.wepKeys)); 105 } 106 107 /** 108 * Helper method to check if the provided |config| corresponds to an open network or not. 109 */ 110 public static boolean isConfigForOpenNetwork(WifiConfiguration config) { 111 return !(isConfigForWepNetwork(config) || isConfigForPskNetwork(config) 112 || isConfigForEapNetwork(config)); 113 } 114 115 /** 116 * Compare existing and new WifiConfiguration objects after a network update and return if 117 * IP parameters have changed or not. 118 * 119 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 120 * @param newConfig New WifiConfiguration object corresponding to the network. 121 * @return true if IP parameters have changed, false otherwise. 122 */ 123 public static boolean hasIpChanged(WifiConfiguration existingConfig, 124 WifiConfiguration newConfig) { 125 if (existingConfig.getIpAssignment() != newConfig.getIpAssignment()) { 126 return true; 127 } 128 if (newConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) { 129 return !Objects.equals(existingConfig.getStaticIpConfiguration(), 130 newConfig.getStaticIpConfiguration()); 131 } 132 return false; 133 } 134 135 /** 136 * Compare existing and new WifiConfiguration objects after a network update and return if 137 * proxy parameters have changed or not. 138 * 139 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 140 * @param newConfig New WifiConfiguration object corresponding to the network. 141 * @return true if proxy parameters have changed, false if no existing config and proxy settings 142 * are NONE, false otherwise. 143 */ 144 public static boolean hasProxyChanged(WifiConfiguration existingConfig, 145 WifiConfiguration newConfig) { 146 if (existingConfig == null) { 147 return newConfig.getProxySettings() != IpConfiguration.ProxySettings.NONE; 148 } 149 if (newConfig.getProxySettings() != existingConfig.getProxySettings()) { 150 return true; 151 } 152 return !Objects.equals(existingConfig.getHttpProxy(), newConfig.getHttpProxy()); 153 } 154 155 /** 156 * Compare existing and new WifiEnterpriseConfig objects after a network update and return if 157 * credential parameters have changed or not. 158 * 159 * @param existingEnterpriseConfig Existing WifiConfiguration object corresponding to the 160 * network. 161 * @param newEnterpriseConfig New WifiConfiguration object corresponding to the network. 162 * @return true if credentials have changed, false otherwise. 163 */ 164 @VisibleForTesting 165 public static boolean hasEnterpriseConfigChanged(WifiEnterpriseConfig existingEnterpriseConfig, 166 WifiEnterpriseConfig newEnterpriseConfig) { 167 if (existingEnterpriseConfig != null && newEnterpriseConfig != null) { 168 if (existingEnterpriseConfig.getEapMethod() != newEnterpriseConfig.getEapMethod()) { 169 return true; 170 } 171 if (existingEnterpriseConfig.getPhase2Method() 172 != newEnterpriseConfig.getPhase2Method()) { 173 return true; 174 } 175 X509Certificate[] existingCaCerts = existingEnterpriseConfig.getCaCertificates(); 176 X509Certificate[] newCaCerts = newEnterpriseConfig.getCaCertificates(); 177 if (!Arrays.equals(existingCaCerts, newCaCerts)) { 178 return true; 179 } 180 } else { 181 // One of the configs may have an enterpriseConfig 182 if (existingEnterpriseConfig != null || newEnterpriseConfig != null) { 183 return true; 184 } 185 } 186 return false; 187 } 188 189 /** 190 * Compare existing and new WifiConfiguration objects after a network update and return if 191 * credential parameters have changed or not. 192 * 193 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 194 * @param newConfig New WifiConfiguration object corresponding to the network. 195 * @return true if credentials have changed, false otherwise. 196 */ 197 public static boolean hasCredentialChanged(WifiConfiguration existingConfig, 198 WifiConfiguration newConfig) { 199 if (!Objects.equals(existingConfig.allowedKeyManagement, 200 newConfig.allowedKeyManagement)) { 201 return true; 202 } 203 if (!Objects.equals(existingConfig.allowedProtocols, newConfig.allowedProtocols)) { 204 return true; 205 } 206 if (!Objects.equals(existingConfig.allowedAuthAlgorithms, 207 newConfig.allowedAuthAlgorithms)) { 208 return true; 209 } 210 if (!Objects.equals(existingConfig.allowedPairwiseCiphers, 211 newConfig.allowedPairwiseCiphers)) { 212 return true; 213 } 214 if (!Objects.equals(existingConfig.allowedGroupCiphers, 215 newConfig.allowedGroupCiphers)) { 216 return true; 217 } 218 if (!Objects.equals(existingConfig.preSharedKey, newConfig.preSharedKey)) { 219 return true; 220 } 221 if (!Arrays.equals(existingConfig.wepKeys, newConfig.wepKeys)) { 222 return true; 223 } 224 if (existingConfig.wepTxKeyIndex != newConfig.wepTxKeyIndex) { 225 return true; 226 } 227 if (existingConfig.hiddenSSID != newConfig.hiddenSSID) { 228 return true; 229 } 230 if (hasEnterpriseConfigChanged(existingConfig.enterpriseConfig, 231 newConfig.enterpriseConfig)) { 232 return true; 233 } 234 return false; 235 } 236 237 /** 238 * Check if the provided two networks are the same. 239 * 240 * @param config Configuration corresponding to a network. 241 * @param config1 Configuration corresponding to another network. 242 * 243 * @return true if |config| and |config1| are the same network. 244 * false otherwise. 245 */ 246 public static boolean isSameNetwork(WifiConfiguration config, WifiConfiguration config1) { 247 if (config == null && config1 == null) { 248 return true; 249 } 250 if (config == null || config1 == null) { 251 return false; 252 } 253 if (config.networkId != config1.networkId) { 254 return false; 255 } 256 if (!Objects.equals(config.SSID, config1.SSID)) { 257 return false; 258 } 259 String networkSelectionBSSID = config.getNetworkSelectionStatus() 260 .getNetworkSelectionBSSID(); 261 String networkSelectionBSSID1 = config1.getNetworkSelectionStatus() 262 .getNetworkSelectionBSSID(); 263 if (!Objects.equals(networkSelectionBSSID, networkSelectionBSSID1)) { 264 return false; 265 } 266 if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) { 267 return false; 268 } 269 return true; 270 } 271 272 /** 273 * Create a PnoNetwork object from the provided WifiConfiguration. 274 * 275 * @param config Configuration corresponding to the network. 276 * @param newPriority New priority to be assigned to the network. 277 * @return PnoNetwork object corresponding to the network. 278 */ 279 public static WifiScanner.PnoSettings.PnoNetwork createPnoNetwork( 280 WifiConfiguration config, int newPriority) { 281 WifiScanner.PnoSettings.PnoNetwork pnoNetwork = 282 new WifiScanner.PnoSettings.PnoNetwork(config.SSID); 283 if (config.hiddenSSID) { 284 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN; 285 } 286 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND; 287 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND; 288 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 289 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK; 290 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) 291 || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) { 292 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL; 293 } else { 294 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN; 295 } 296 return pnoNetwork; 297 } 298 299 300 /** 301 * General WifiConfiguration list sorting algorithm: 302 * 1, Place the fully enabled networks first. 303 * 2. Next place all the temporarily disabled networks. 304 * 3. Place the permanently disabled networks last (Permanently disabled networks are removed 305 * before WifiConfigManager uses this comparator today!). 306 * 307 * Among the networks with the same status, sort them in the order determined by the return of 308 * {@link #compareNetworksWithSameStatus(WifiConfiguration, WifiConfiguration)} method 309 * implementation. 310 */ 311 public abstract static class WifiConfigurationComparator implements 312 Comparator<WifiConfiguration> { 313 private static final int ENABLED_NETWORK_SCORE = 3; 314 private static final int TEMPORARY_DISABLED_NETWORK_SCORE = 2; 315 private static final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1; 316 317 @Override 318 public int compare(WifiConfiguration a, WifiConfiguration b) { 319 int configAScore = getNetworkStatusScore(a); 320 int configBScore = getNetworkStatusScore(b); 321 if (configAScore == configBScore) { 322 return compareNetworksWithSameStatus(a, b); 323 } else { 324 return Integer.compare(configBScore, configAScore); 325 } 326 } 327 328 // This needs to be implemented by the connected/disconnected PNO list comparator. 329 abstract int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b); 330 331 /** 332 * Returns an integer representing a score for each configuration. The scores are assigned 333 * based on the status of the configuration. The scores are assigned according to the order: 334 * Fully enabled network > Temporarily disabled network > Permanently disabled network. 335 */ 336 private int getNetworkStatusScore(WifiConfiguration config) { 337 if (config.getNetworkSelectionStatus().isNetworkEnabled()) { 338 return ENABLED_NETWORK_SCORE; 339 } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) { 340 return TEMPORARY_DISABLED_NETWORK_SCORE; 341 } else { 342 return PERMANENTLY_DISABLED_NETWORK_SCORE; 343 } 344 } 345 } 346 } 347