Home | History | Annotate | Download | only in wifi
      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