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