1/*
2 * Copyright (C) 2010 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 android.net.wifi;
18
19import android.content.Context;
20import android.content.Intent;
21import android.net.LinkAddress;
22import android.net.LinkProperties;
23import android.net.NetworkUtils;
24import android.net.NetworkInfo.DetailedState;
25import android.net.ProxyProperties;
26import android.net.RouteInfo;
27import android.net.wifi.WifiConfiguration.IpAssignment;
28import android.net.wifi.WifiConfiguration.KeyMgmt;
29import android.net.wifi.WifiConfiguration.ProxySettings;
30import android.net.wifi.WifiConfiguration.Status;
31import android.net.wifi.NetworkUpdateResult;
32import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
33import android.os.Environment;
34import android.os.FileObserver;
35import android.os.Message;
36import android.os.Handler;
37import android.os.HandlerThread;
38import android.os.UserHandle;
39import android.security.KeyStore;
40import android.text.TextUtils;
41import android.util.LocalLog;
42import android.util.Log;
43
44import java.io.BufferedInputStream;
45import java.io.BufferedOutputStream;
46import java.io.BufferedReader;
47import java.io.DataInputStream;
48import java.io.DataOutputStream;
49import java.io.EOFException;
50import java.io.File;
51import java.io.FileDescriptor;
52import java.io.FileInputStream;
53import java.io.FileNotFoundException;
54import java.io.FileOutputStream;
55import java.io.FileReader;
56import java.io.IOException;
57import java.io.PrintWriter;
58import java.net.InetAddress;
59import java.net.UnknownHostException;
60import java.security.PublicKey;
61import java.util.ArrayList;
62import java.util.BitSet;
63import java.util.Collection;
64import java.util.HashMap;
65import java.util.Iterator;
66import java.util.List;
67import java.util.concurrent.atomic.AtomicInteger;
68
69/**
70 * This class provides the API to manage configured
71 * wifi networks. The API is not thread safe is being
72 * used only from WifiStateMachine.
73 *
74 * It deals with the following
75 * - Add/update/remove a WifiConfiguration
76 *   The configuration contains two types of information.
77 *     = IP and proxy configuration that is handled by WifiConfigStore and
78 *       is saved to disk on any change.
79 *
80 *       The format of configuration file is as follows:
81 *       <version>
82 *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
83 *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
84 *       ..
85 *
86 *       (key, value) pairs for a given network are grouped together and can
87 *       be in any order. A EOS at the end of a set of (key, value) pairs
88 *       indicates that the next set of (key, value) pairs are for a new
89 *       network. A network is identified by a unique ID_KEY. If there is no
90 *       ID_KEY in the (key, value) pairs, the data is discarded.
91 *
92 *       An invalid version on read would result in discarding the contents of
93 *       the file. On the next write, the latest version is written to file.
94 *
95 *       Any failures during read or write to the configuration file are ignored
96 *       without reporting to the user since the likelihood of these errors are
97 *       low and the impact on connectivity is low.
98 *
99 *     = SSID & security details that is pushed to the supplicant.
100 *       supplicant saves these details to the disk on calling
101 *       saveConfigCommand().
102 *
103 *       We have two kinds of APIs exposed:
104 *        > public API calls that provide fine grained control
105 *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
106 *          removeNetwork(). For these calls, the config is not persisted
107 *          to the disk. (TODO: deprecate these calls in WifiManager)
108 *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
109 *          These calls persist the supplicant config to disk.
110 *
111 * - Maintain a list of configured networks for quick access
112 *
113 */
114class WifiConfigStore {
115
116    private Context mContext;
117    private static final String TAG = "WifiConfigStore";
118    private static final boolean DBG = true;
119    private static final boolean VDBG = false;
120
121    private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
122
123    /* configured networks with network id as the key */
124    private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
125            new HashMap<Integer, WifiConfiguration>();
126
127    /* A network id is a unique identifier for a network configured in the
128     * supplicant. Network ids are generated when the supplicant reads
129     * the configuration file at start and can thus change for networks.
130     * We store the IP configuration for networks along with a unique id
131     * that is generated from SSID and security type of the network. A mapping
132     * from the generated unique id to network id of the network is needed to
133     * map supplicant config to IP configuration. */
134    private HashMap<Integer, Integer> mNetworkIds =
135            new HashMap<Integer, Integer>();
136
137    /* Tracks the highest priority of configured networks */
138    private int mLastPriority = -1;
139
140    private static final String ipConfigFile = Environment.getDataDirectory() +
141            "/misc/wifi/ipconfig.txt";
142
143    private static final int IPCONFIG_FILE_VERSION = 2;
144
145    /* IP and proxy configuration keys */
146    private static final String ID_KEY = "id";
147    private static final String IP_ASSIGNMENT_KEY = "ipAssignment";
148    private static final String LINK_ADDRESS_KEY = "linkAddress";
149    private static final String GATEWAY_KEY = "gateway";
150    private static final String DNS_KEY = "dns";
151    private static final String PROXY_SETTINGS_KEY = "proxySettings";
152    private static final String PROXY_HOST_KEY = "proxyHost";
153    private static final String PROXY_PORT_KEY = "proxyPort";
154    private static final String PROXY_PAC_FILE = "proxyPac";
155    private static final String EXCLUSION_LIST_KEY = "exclusionList";
156    private static final String EOS = "eos";
157
158    private final LocalLog mLocalLog;
159    private final WpaConfigFileObserver mFileObserver;
160
161    private WifiNative mWifiNative;
162    private final KeyStore mKeyStore = KeyStore.getInstance();
163
164    WifiConfigStore(Context c, WifiNative wn) {
165        mContext = c;
166        mWifiNative = wn;
167
168        if (VDBG) {
169            mLocalLog = mWifiNative.getLocalLog();
170            mFileObserver = new WpaConfigFileObserver();
171            mFileObserver.startWatching();
172        } else {
173            mLocalLog = null;
174            mFileObserver = null;
175        }
176    }
177
178    class WpaConfigFileObserver extends FileObserver {
179
180        public WpaConfigFileObserver() {
181            super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
182        }
183
184        @Override
185        public void onEvent(int event, String path) {
186            if (event == CLOSE_WRITE) {
187                File file = new File(SUPPLICANT_CONFIG_FILE);
188                if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
189            }
190        }
191    }
192
193
194    /**
195     * Fetch the list of configured networks
196     * and enable all stored networks in supplicant.
197     */
198    void loadAndEnableAllNetworks() {
199        if (DBG) log("Loading config and enabling all networks");
200        loadConfiguredNetworks();
201        enableAllNetworks();
202    }
203
204    /**
205     * Fetch the list of currently configured networks
206     * @return List of networks
207     */
208    List<WifiConfiguration> getConfiguredNetworks() {
209        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
210        for(WifiConfiguration config : mConfiguredNetworks.values()) {
211            networks.add(new WifiConfiguration(config));
212        }
213        return networks;
214    }
215
216    /**
217     * enable all networks and save config. This will be a no-op if the list
218     * of configured networks indicates all networks as being enabled
219     */
220    void enableAllNetworks() {
221        boolean networkEnabledStateChanged = false;
222        for(WifiConfiguration config : mConfiguredNetworks.values()) {
223            if(config != null && config.status == Status.DISABLED) {
224                if(mWifiNative.enableNetwork(config.networkId, false)) {
225                    networkEnabledStateChanged = true;
226                    config.status = Status.ENABLED;
227                } else {
228                    loge("Enable network failed on " + config.networkId);
229                }
230            }
231        }
232
233        if (networkEnabledStateChanged) {
234            mWifiNative.saveConfig();
235            sendConfiguredNetworksChangedBroadcast();
236        }
237    }
238
239
240    /**
241     * Selects the specified network for connection. This involves
242     * updating the priority of all the networks and enabling the given
243     * network while disabling others.
244     *
245     * Selecting a network will leave the other networks disabled and
246     * a call to enableAllNetworks() needs to be issued upon a connection
247     * or a failure event from supplicant
248     *
249     * @param netId network to select for connection
250     * @return false if the network id is invalid
251     */
252    boolean selectNetwork(int netId) {
253        if (VDBG) localLog("selectNetwork", netId);
254        if (netId == INVALID_NETWORK_ID) return false;
255
256        // Reset the priority of each network at start or if it goes too high.
257        if (mLastPriority == -1 || mLastPriority > 1000000) {
258            for(WifiConfiguration config : mConfiguredNetworks.values()) {
259                if (config.networkId != INVALID_NETWORK_ID) {
260                    config.priority = 0;
261                    addOrUpdateNetworkNative(config);
262                }
263            }
264            mLastPriority = 0;
265        }
266
267        // Set to the highest priority and save the configuration.
268        WifiConfiguration config = new WifiConfiguration();
269        config.networkId = netId;
270        config.priority = ++mLastPriority;
271
272        addOrUpdateNetworkNative(config);
273        mWifiNative.saveConfig();
274
275        /* Enable the given network while disabling all other networks */
276        enableNetworkWithoutBroadcast(netId, true);
277
278       /* Avoid saving the config & sending a broadcast to prevent settings
279        * from displaying a disabled list of networks */
280        return true;
281    }
282
283    /**
284     * Add/update the specified configuration and save config
285     *
286     * @param config WifiConfiguration to be saved
287     * @return network update result
288     */
289    NetworkUpdateResult saveNetwork(WifiConfiguration config) {
290        if (VDBG) localLog("saveNetwork", config.networkId);
291        // A new network cannot have null SSID
292        if (config == null || (config.networkId == INVALID_NETWORK_ID &&
293                config.SSID == null)) {
294            return new NetworkUpdateResult(INVALID_NETWORK_ID);
295        }
296
297        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
298        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
299        int netId = result.getNetworkId();
300        /* enable a new network */
301        if (newNetwork && netId != INVALID_NETWORK_ID) {
302            mWifiNative.enableNetwork(netId, false);
303            mConfiguredNetworks.get(netId).status = Status.ENABLED;
304        }
305        mWifiNative.saveConfig();
306        sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ?
307                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
308        return result;
309    }
310
311    void updateStatus(int netId, DetailedState state) {
312        if (netId != INVALID_NETWORK_ID) {
313            WifiConfiguration config = mConfiguredNetworks.get(netId);
314            if (config == null) return;
315            switch (state) {
316                case CONNECTED:
317                    config.status = Status.CURRENT;
318                    break;
319                case DISCONNECTED:
320                    //If network is already disabled, keep the status
321                    if (config.status == Status.CURRENT) {
322                        config.status = Status.ENABLED;
323                    }
324                    break;
325                default:
326                    //do nothing, retain the existing state
327                    break;
328            }
329        }
330    }
331
332    /**
333     * Forget the specified network and save config
334     *
335     * @param netId network to forget
336     * @return {@code true} if it succeeds, {@code false} otherwise
337     */
338    boolean forgetNetwork(int netId) {
339        if (VDBG) localLog("forgetNetwork", netId);
340        if (mWifiNative.removeNetwork(netId)) {
341            mWifiNative.saveConfig();
342            removeConfigAndSendBroadcastIfNeeded(netId);
343            return true;
344        } else {
345            loge("Failed to remove network " + netId);
346            return false;
347        }
348    }
349
350    /**
351     * Add/update a network. Note that there is no saveConfig operation.
352     * This function is retained for compatibility with the public
353     * API. The more powerful saveNetwork() is used by the
354     * state machine
355     *
356     * @param config wifi configuration to add/update
357     * @return network Id
358     */
359    int addOrUpdateNetwork(WifiConfiguration config) {
360        if (VDBG) localLog("addOrUpdateNetwork", config.networkId);
361        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
362        if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
363            sendConfiguredNetworksChangedBroadcast(mConfiguredNetworks.get(result.getNetworkId()),
364                    result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
365                            WifiManager.CHANGE_REASON_CONFIG_CHANGE);
366        }
367        return result.getNetworkId();
368    }
369
370    /**
371     * Remove a network. Note that there is no saveConfig operation.
372     * This function is retained for compatibility with the public
373     * API. The more powerful forgetNetwork() is used by the
374     * state machine for network removal
375     *
376     * @param netId network to be removed
377     * @return {@code true} if it succeeds, {@code false} otherwise
378     */
379    boolean removeNetwork(int netId) {
380        if (VDBG) localLog("removeNetwork", netId);
381        boolean ret = mWifiNative.removeNetwork(netId);
382        if (ret) {
383            removeConfigAndSendBroadcastIfNeeded(netId);
384        }
385        return ret;
386    }
387
388    private void removeConfigAndSendBroadcastIfNeeded(int netId) {
389        WifiConfiguration config = mConfiguredNetworks.get(netId);
390        if (config != null) {
391            // Remove any associated keys
392            if (config.enterpriseConfig != null) {
393                config.enterpriseConfig.removeKeys(mKeyStore);
394            }
395            mConfiguredNetworks.remove(netId);
396            mNetworkIds.remove(configKey(config));
397
398            writeIpAndProxyConfigurations();
399            sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
400        }
401    }
402
403    /**
404     * Enable a network. Note that there is no saveConfig operation.
405     * This function is retained for compatibility with the public
406     * API. The more powerful selectNetwork()/saveNetwork() is used by the
407     * state machine for connecting to a network
408     *
409     * @param netId network to be enabled
410     * @return {@code true} if it succeeds, {@code false} otherwise
411     */
412    boolean enableNetwork(int netId, boolean disableOthers) {
413        boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
414        if (disableOthers) {
415            if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId);
416            sendConfiguredNetworksChangedBroadcast();
417        } else {
418            if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId);
419            WifiConfiguration enabledNetwork = null;
420            synchronized(mConfiguredNetworks) {
421                enabledNetwork = mConfiguredNetworks.get(netId);
422            }
423            // check just in case the network was removed by someone else.
424            if (enabledNetwork != null) {
425                sendConfiguredNetworksChangedBroadcast(enabledNetwork,
426                        WifiManager.CHANGE_REASON_CONFIG_CHANGE);
427            }
428        }
429        return ret;
430    }
431
432    boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
433        boolean ret = mWifiNative.enableNetwork(netId, disableOthers);
434
435        WifiConfiguration config = mConfiguredNetworks.get(netId);
436        if (config != null) config.status = Status.ENABLED;
437
438        if (disableOthers) {
439            markAllNetworksDisabledExcept(netId);
440        }
441        return ret;
442    }
443
444    void disableAllNetworks() {
445        if (VDBG) localLog("disableAllNetworks");
446        boolean networkDisabled = false;
447        for(WifiConfiguration config : mConfiguredNetworks.values()) {
448            if(config != null && config.status != Status.DISABLED) {
449                if(mWifiNative.disableNetwork(config.networkId)) {
450                    networkDisabled = true;
451                    config.status = Status.DISABLED;
452                } else {
453                    loge("Disable network failed on " + config.networkId);
454                }
455            }
456        }
457
458        if (networkDisabled) {
459            sendConfiguredNetworksChangedBroadcast();
460        }
461    }
462    /**
463     * Disable a network. Note that there is no saveConfig operation.
464     * @param netId network to be disabled
465     * @return {@code true} if it succeeds, {@code false} otherwise
466     */
467    boolean disableNetwork(int netId) {
468        return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
469    }
470
471    /**
472     * Disable a network. Note that there is no saveConfig operation.
473     * @param netId network to be disabled
474     * @param reason reason code network was disabled
475     * @return {@code true} if it succeeds, {@code false} otherwise
476     */
477    boolean disableNetwork(int netId, int reason) {
478        if (VDBG) localLog("disableNetwork", netId);
479        boolean ret = mWifiNative.disableNetwork(netId);
480        WifiConfiguration network = null;
481        WifiConfiguration config = mConfiguredNetworks.get(netId);
482        /* Only change the reason if the network was not previously disabled */
483        if (config != null && config.status != Status.DISABLED) {
484            config.status = Status.DISABLED;
485            config.disableReason = reason;
486            network = config;
487        }
488        if (network != null) {
489            sendConfiguredNetworksChangedBroadcast(network,
490                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
491        }
492        return ret;
493    }
494
495    /**
496     * Save the configured networks in supplicant to disk
497     * @return {@code true} if it succeeds, {@code false} otherwise
498     */
499    boolean saveConfig() {
500        return mWifiNative.saveConfig();
501    }
502
503    /**
504     * Start WPS pin method configuration with pin obtained
505     * from the access point
506     * @param config WPS configuration
507     * @return Wps result containing status and pin
508     */
509    WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
510        WpsResult result = new WpsResult();
511        if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
512            /* WPS leaves all networks disabled */
513            markAllNetworksDisabled();
514            result.status = WpsResult.Status.SUCCESS;
515        } else {
516            loge("Failed to start WPS pin method configuration");
517            result.status = WpsResult.Status.FAILURE;
518        }
519        return result;
520    }
521
522    /**
523     * Start WPS pin method configuration with pin obtained
524     * from the device
525     * @return WpsResult indicating status and pin
526     */
527    WpsResult startWpsWithPinFromDevice(WpsInfo config) {
528        WpsResult result = new WpsResult();
529        result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
530        /* WPS leaves all networks disabled */
531        if (!TextUtils.isEmpty(result.pin)) {
532            markAllNetworksDisabled();
533            result.status = WpsResult.Status.SUCCESS;
534        } else {
535            loge("Failed to start WPS pin method configuration");
536            result.status = WpsResult.Status.FAILURE;
537        }
538        return result;
539    }
540
541    /**
542     * Start WPS push button configuration
543     * @param config WPS configuration
544     * @return WpsResult indicating status and pin
545     */
546    WpsResult startWpsPbc(WpsInfo config) {
547        WpsResult result = new WpsResult();
548        if (mWifiNative.startWpsPbc(config.BSSID)) {
549            /* WPS leaves all networks disabled */
550            markAllNetworksDisabled();
551            result.status = WpsResult.Status.SUCCESS;
552        } else {
553            loge("Failed to start WPS push button configuration");
554            result.status = WpsResult.Status.FAILURE;
555        }
556        return result;
557    }
558
559    /**
560     * Fetch the link properties for a given network id
561     * @return LinkProperties for the given network id
562     */
563    LinkProperties getLinkProperties(int netId) {
564        WifiConfiguration config = mConfiguredNetworks.get(netId);
565        if (config != null) return new LinkProperties(config.linkProperties);
566        return null;
567    }
568
569    /**
570     * set IP configuration for a given network id
571     */
572    void setLinkProperties(int netId, LinkProperties linkProperties) {
573        WifiConfiguration config = mConfiguredNetworks.get(netId);
574        if (config != null) {
575            // add old proxy details - TODO - is this still needed?
576            if(config.linkProperties != null) {
577                linkProperties.setHttpProxy(config.linkProperties.getHttpProxy());
578            }
579            config.linkProperties = linkProperties;
580        }
581    }
582
583    /**
584     * clear IP configuration for a given network id
585     * @param network id
586     */
587    void clearLinkProperties(int netId) {
588        WifiConfiguration config = mConfiguredNetworks.get(netId);
589        if (config != null && config.linkProperties != null) {
590            // Clear everything except proxy
591            ProxyProperties proxy = config.linkProperties.getHttpProxy();
592            config.linkProperties.clear();
593            config.linkProperties.setHttpProxy(proxy);
594        }
595    }
596
597
598    /**
599     * Fetch the proxy properties for a given network id
600     * @param network id
601     * @return ProxyProperties for the network id
602     */
603    ProxyProperties getProxyProperties(int netId) {
604        LinkProperties linkProperties = getLinkProperties(netId);
605        if (linkProperties != null) {
606            return new ProxyProperties(linkProperties.getHttpProxy());
607        }
608        return null;
609    }
610
611    /**
612     * Return if the specified network is using static IP
613     * @param network id
614     * @return {@code true} if using static ip for netId
615     */
616    boolean isUsingStaticIp(int netId) {
617        WifiConfiguration config = mConfiguredNetworks.get(netId);
618        if (config != null && config.ipAssignment == IpAssignment.STATIC) {
619            return true;
620        }
621        return false;
622    }
623
624    /**
625     * Should be called when a single network configuration is made.
626     * @param network The network configuration that changed.
627     * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
628     * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
629     */
630    private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
631            int reason) {
632        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
633        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
634        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
635        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
636        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
637        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
638    }
639
640    /**
641     * Should be called when multiple network configuration changes are made.
642     */
643    private void sendConfiguredNetworksChangedBroadcast() {
644        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
645        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
646        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
647        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
648    }
649
650    void loadConfiguredNetworks() {
651        String listStr = mWifiNative.listNetworks();
652        mLastPriority = 0;
653
654        mConfiguredNetworks.clear();
655        mNetworkIds.clear();
656
657        if (listStr == null)
658            return;
659
660        String[] lines = listStr.split("\n");
661        // Skip the first line, which is a header
662        for (int i = 1; i < lines.length; i++) {
663            String[] result = lines[i].split("\t");
664            // network-id | ssid | bssid | flags
665            WifiConfiguration config = new WifiConfiguration();
666            try {
667                config.networkId = Integer.parseInt(result[0]);
668            } catch(NumberFormatException e) {
669                loge("Failed to read network-id '" + result[0] + "'");
670                continue;
671            }
672            if (result.length > 3) {
673                if (result[3].indexOf("[CURRENT]") != -1)
674                    config.status = WifiConfiguration.Status.CURRENT;
675                else if (result[3].indexOf("[DISABLED]") != -1)
676                    config.status = WifiConfiguration.Status.DISABLED;
677                else
678                    config.status = WifiConfiguration.Status.ENABLED;
679            } else {
680                config.status = WifiConfiguration.Status.ENABLED;
681            }
682            readNetworkVariables(config);
683            if (config.priority > mLastPriority) {
684                mLastPriority = config.priority;
685            }
686            config.ipAssignment = IpAssignment.DHCP;
687            config.proxySettings = ProxySettings.NONE;
688
689            if (mNetworkIds.containsKey(configKey(config))) {
690                // That SSID is already known, just ignore this duplicate entry
691                if (VDBG) localLog("discarded duplicate network", config.networkId);
692            } else {
693                mConfiguredNetworks.put(config.networkId, config);
694                mNetworkIds.put(configKey(config), config.networkId);
695                if (VDBG) localLog("loaded configured network", config.networkId);
696            }
697        }
698
699        readIpAndProxyConfigurations();
700        sendConfiguredNetworksChangedBroadcast();
701
702        if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks");
703
704        if (mNetworkIds.size() == 0) {
705            // no networks? Lets log if the wpa_supplicant.conf file contents
706            BufferedReader reader = null;
707            try {
708                reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
709                if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---");
710                for (String line = reader.readLine(); line != null; line = reader.readLine()) {
711                    if (VDBG) localLog(line);
712                }
713                if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---");
714            } catch (FileNotFoundException e) {
715                if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
716            } catch (IOException e) {
717                if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
718            } finally {
719                try {
720                    if (reader != null) {
721                        reader.close();
722                    }
723                } catch (IOException e) {
724                    // Just ignore the fact that we couldn't close
725                }
726            }
727        }
728    }
729
730    /* Mark all networks except specified netId as disabled */
731    private void markAllNetworksDisabledExcept(int netId) {
732        for(WifiConfiguration config : mConfiguredNetworks.values()) {
733            if(config != null && config.networkId != netId) {
734                if (config.status != Status.DISABLED) {
735                    config.status = Status.DISABLED;
736                    config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
737                }
738            }
739        }
740    }
741
742    private void markAllNetworksDisabled() {
743        markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
744    }
745
746    boolean needsUnlockedKeyStore() {
747
748        // Any network using certificates to authenticate access requires
749        // unlocked key store; unless the certificates can be stored with
750        // hardware encryption
751
752        for(WifiConfiguration config : mConfiguredNetworks.values()) {
753
754            if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
755                    && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
756
757                if (config.enterpriseConfig.needsSoftwareBackedKeyStore()) {
758                    return true;
759                }
760            }
761        }
762
763        return false;
764    }
765
766    private void writeIpAndProxyConfigurations() {
767
768        /* Make a copy */
769        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
770        for(WifiConfiguration config : mConfiguredNetworks.values()) {
771            networks.add(new WifiConfiguration(config));
772        }
773
774        DelayedDiskWrite.write(networks);
775    }
776
777    private static class DelayedDiskWrite {
778
779        private static HandlerThread sDiskWriteHandlerThread;
780        private static Handler sDiskWriteHandler;
781        /* Tracks multiple writes on the same thread */
782        private static int sWriteSequence = 0;
783        private static final String TAG = "DelayedDiskWrite";
784
785        static void write (final List<WifiConfiguration> networks) {
786
787            /* Do a delayed write to disk on a seperate handler thread */
788            synchronized (DelayedDiskWrite.class) {
789                if (++sWriteSequence == 1) {
790                    sDiskWriteHandlerThread = new HandlerThread("WifiConfigThread");
791                    sDiskWriteHandlerThread.start();
792                    sDiskWriteHandler = new Handler(sDiskWriteHandlerThread.getLooper());
793                }
794            }
795
796            sDiskWriteHandler.post(new Runnable() {
797                    @Override
798                    public void run() {
799                        onWriteCalled(networks);
800                    }
801                });
802        }
803
804        private static void onWriteCalled(List<WifiConfiguration> networks) {
805
806            DataOutputStream out = null;
807            try {
808                out = new DataOutputStream(new BufferedOutputStream(
809                            new FileOutputStream(ipConfigFile)));
810
811                out.writeInt(IPCONFIG_FILE_VERSION);
812
813                for(WifiConfiguration config : networks) {
814                    boolean writeToFile = false;
815
816                    try {
817                        LinkProperties linkProperties = config.linkProperties;
818                        switch (config.ipAssignment) {
819                            case STATIC:
820                                out.writeUTF(IP_ASSIGNMENT_KEY);
821                                out.writeUTF(config.ipAssignment.toString());
822                                for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) {
823                                    out.writeUTF(LINK_ADDRESS_KEY);
824                                    out.writeUTF(linkAddr.getAddress().getHostAddress());
825                                    out.writeInt(linkAddr.getNetworkPrefixLength());
826                                }
827                                for (RouteInfo route : linkProperties.getRoutes()) {
828                                    out.writeUTF(GATEWAY_KEY);
829                                    LinkAddress dest = route.getDestination();
830                                    if (dest != null) {
831                                        out.writeInt(1);
832                                        out.writeUTF(dest.getAddress().getHostAddress());
833                                        out.writeInt(dest.getNetworkPrefixLength());
834                                    } else {
835                                        out.writeInt(0);
836                                    }
837                                    if (route.getGateway() != null) {
838                                        out.writeInt(1);
839                                        out.writeUTF(route.getGateway().getHostAddress());
840                                    } else {
841                                        out.writeInt(0);
842                                    }
843                                }
844                                for (InetAddress inetAddr : linkProperties.getDnses()) {
845                                    out.writeUTF(DNS_KEY);
846                                    out.writeUTF(inetAddr.getHostAddress());
847                                }
848                                writeToFile = true;
849                                break;
850                            case DHCP:
851                                out.writeUTF(IP_ASSIGNMENT_KEY);
852                                out.writeUTF(config.ipAssignment.toString());
853                                writeToFile = true;
854                                break;
855                            case UNASSIGNED:
856                                /* Ignore */
857                                break;
858                            default:
859                                loge("Ignore invalid ip assignment while writing");
860                                break;
861                        }
862
863                        switch (config.proxySettings) {
864                            case STATIC:
865                                ProxyProperties proxyProperties = linkProperties.getHttpProxy();
866                                String exclusionList = proxyProperties.getExclusionList();
867                                out.writeUTF(PROXY_SETTINGS_KEY);
868                                out.writeUTF(config.proxySettings.toString());
869                                out.writeUTF(PROXY_HOST_KEY);
870                                out.writeUTF(proxyProperties.getHost());
871                                out.writeUTF(PROXY_PORT_KEY);
872                                out.writeInt(proxyProperties.getPort());
873                                out.writeUTF(EXCLUSION_LIST_KEY);
874                                out.writeUTF(exclusionList);
875                                writeToFile = true;
876                                break;
877                            case PAC:
878                                ProxyProperties proxyPacProperties = linkProperties.getHttpProxy();
879                                out.writeUTF(PROXY_SETTINGS_KEY);
880                                out.writeUTF(config.proxySettings.toString());
881                                out.writeUTF(PROXY_PAC_FILE);
882                                out.writeUTF(proxyPacProperties.getPacFileUrl());
883                                writeToFile = true;
884                                break;
885                            case NONE:
886                                out.writeUTF(PROXY_SETTINGS_KEY);
887                                out.writeUTF(config.proxySettings.toString());
888                                writeToFile = true;
889                                break;
890                            case UNASSIGNED:
891                                /* Ignore */
892                                break;
893                            default:
894                                loge("Ignthisore invalid proxy settings while writing");
895                                break;
896                        }
897                        if (writeToFile) {
898                            out.writeUTF(ID_KEY);
899                            out.writeInt(configKey(config));
900                        }
901                    } catch (NullPointerException e) {
902                        loge("Failure in writing " + config.linkProperties + e);
903                    }
904                    out.writeUTF(EOS);
905                }
906
907            } catch (IOException e) {
908                loge("Error writing data file");
909            } finally {
910                if (out != null) {
911                    try {
912                        out.close();
913                    } catch (Exception e) {}
914                }
915
916                //Quit if no more writes sent
917                synchronized (DelayedDiskWrite.class) {
918                    if (--sWriteSequence == 0) {
919                        sDiskWriteHandler.getLooper().quit();
920                        sDiskWriteHandler = null;
921                        sDiskWriteHandlerThread = null;
922                    }
923                }
924            }
925        }
926
927        private static void loge(String s) {
928            Log.e(TAG, s);
929        }
930    }
931
932    private void readIpAndProxyConfigurations() {
933
934        DataInputStream in = null;
935        try {
936            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
937                    ipConfigFile)));
938
939            int version = in.readInt();
940            if (version != 2 && version != 1) {
941                loge("Bad version on IP configuration file, ignore read");
942                return;
943            }
944
945            while (true) {
946                int id = -1;
947                // Default is DHCP with no proxy
948                IpAssignment ipAssignment = IpAssignment.DHCP;
949                ProxySettings proxySettings = ProxySettings.NONE;
950                LinkProperties linkProperties = new LinkProperties();
951                String proxyHost = null;
952                String pacFileUrl = null;
953                int proxyPort = -1;
954                String exclusionList = null;
955                String key;
956
957                do {
958                    key = in.readUTF();
959                    try {
960                        if (key.equals(ID_KEY)) {
961                            id = in.readInt();
962                        } else if (key.equals(IP_ASSIGNMENT_KEY)) {
963                            ipAssignment = IpAssignment.valueOf(in.readUTF());
964                        } else if (key.equals(LINK_ADDRESS_KEY)) {
965                            LinkAddress linkAddr = new LinkAddress(
966                                    NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt());
967                            linkProperties.addLinkAddress(linkAddr);
968                        } else if (key.equals(GATEWAY_KEY)) {
969                            LinkAddress dest = null;
970                            InetAddress gateway = null;
971                            if (version == 1) {
972                                // only supported default gateways - leave the dest/prefix empty
973                                gateway = NetworkUtils.numericToInetAddress(in.readUTF());
974                            } else {
975                                if (in.readInt() == 1) {
976                                    dest = new LinkAddress(
977                                            NetworkUtils.numericToInetAddress(in.readUTF()),
978                                            in.readInt());
979                                }
980                                if (in.readInt() == 1) {
981                                    gateway = NetworkUtils.numericToInetAddress(in.readUTF());
982                                }
983                            }
984                            linkProperties.addRoute(new RouteInfo(dest, gateway));
985                        } else if (key.equals(DNS_KEY)) {
986                            linkProperties.addDns(
987                                    NetworkUtils.numericToInetAddress(in.readUTF()));
988                        } else if (key.equals(PROXY_SETTINGS_KEY)) {
989                            proxySettings = ProxySettings.valueOf(in.readUTF());
990                        } else if (key.equals(PROXY_HOST_KEY)) {
991                            proxyHost = in.readUTF();
992                        } else if (key.equals(PROXY_PORT_KEY)) {
993                            proxyPort = in.readInt();
994                        } else if (key.equals(PROXY_PAC_FILE)) {
995                            pacFileUrl = in.readUTF();
996                        } else if (key.equals(EXCLUSION_LIST_KEY)) {
997                            exclusionList = in.readUTF();
998                        } else if (key.equals(EOS)) {
999                            break;
1000                        } else {
1001                            loge("Ignore unknown key " + key + "while reading");
1002                        }
1003                    } catch (IllegalArgumentException e) {
1004                        loge("Ignore invalid address while reading" + e);
1005                    }
1006                } while (true);
1007
1008                if (id != -1) {
1009                    WifiConfiguration config = mConfiguredNetworks.get(
1010                            mNetworkIds.get(id));
1011
1012                    if (config == null) {
1013                        loge("configuration found for missing network, ignored");
1014                    } else {
1015                        config.linkProperties = linkProperties;
1016                        switch (ipAssignment) {
1017                            case STATIC:
1018                            case DHCP:
1019                                config.ipAssignment = ipAssignment;
1020                                break;
1021                            case UNASSIGNED:
1022                                loge("BUG: Found UNASSIGNED IP on file, use DHCP");
1023                                config.ipAssignment = IpAssignment.DHCP;
1024                                break;
1025                            default:
1026                                loge("Ignore invalid ip assignment while reading");
1027                                break;
1028                        }
1029
1030                        switch (proxySettings) {
1031                            case STATIC:
1032                                config.proxySettings = proxySettings;
1033                                ProxyProperties proxyProperties =
1034                                    new ProxyProperties(proxyHost, proxyPort, exclusionList);
1035                                linkProperties.setHttpProxy(proxyProperties);
1036                                break;
1037                            case PAC:
1038                                config.proxySettings = proxySettings;
1039                                ProxyProperties proxyPacProperties =
1040                                        new ProxyProperties(pacFileUrl);
1041                                linkProperties.setHttpProxy(proxyPacProperties);
1042                                break;
1043                            case NONE:
1044                                config.proxySettings = proxySettings;
1045                                break;
1046                            case UNASSIGNED:
1047                                loge("BUG: Found UNASSIGNED proxy on file, use NONE");
1048                                config.proxySettings = ProxySettings.NONE;
1049                                break;
1050                            default:
1051                                loge("Ignore invalid proxy settings while reading");
1052                                break;
1053                        }
1054                    }
1055                } else {
1056                    if (DBG) log("Missing id while parsing configuration");
1057                }
1058            }
1059        } catch (EOFException ignore) {
1060        } catch (IOException e) {
1061            loge("Error parsing configuration" + e);
1062        } finally {
1063            if (in != null) {
1064                try {
1065                    in.close();
1066                } catch (Exception e) {}
1067            }
1068        }
1069    }
1070
1071    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
1072        /*
1073         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1074         * network configuration. Otherwise, the networkId should
1075         * refer to an existing configuration.
1076         */
1077
1078        if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
1079
1080        int netId = config.networkId;
1081        boolean newNetwork = false;
1082        // networkId of INVALID_NETWORK_ID means we want to create a new network
1083        if (netId == INVALID_NETWORK_ID) {
1084            Integer savedNetId = mNetworkIds.get(configKey(config));
1085            if (savedNetId != null) {
1086                netId = savedNetId;
1087            } else {
1088                newNetwork = true;
1089                netId = mWifiNative.addNetwork();
1090                if (netId < 0) {
1091                    loge("Failed to add a network!");
1092                    return new NetworkUpdateResult(INVALID_NETWORK_ID);
1093                }
1094            }
1095        }
1096
1097        boolean updateFailed = true;
1098
1099        setVariables: {
1100
1101            if (config.SSID != null &&
1102                    !mWifiNative.setNetworkVariable(
1103                        netId,
1104                        WifiConfiguration.ssidVarName,
1105                        config.SSID)) {
1106                loge("failed to set SSID: "+config.SSID);
1107                break setVariables;
1108            }
1109
1110            if (config.BSSID != null &&
1111                    !mWifiNative.setNetworkVariable(
1112                        netId,
1113                        WifiConfiguration.bssidVarName,
1114                        config.BSSID)) {
1115                loge("failed to set BSSID: "+config.BSSID);
1116                break setVariables;
1117            }
1118
1119            String allowedKeyManagementString =
1120                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
1121            if (config.allowedKeyManagement.cardinality() != 0 &&
1122                    !mWifiNative.setNetworkVariable(
1123                        netId,
1124                        WifiConfiguration.KeyMgmt.varName,
1125                        allowedKeyManagementString)) {
1126                loge("failed to set key_mgmt: "+
1127                        allowedKeyManagementString);
1128                break setVariables;
1129            }
1130
1131            String allowedProtocolsString =
1132                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
1133            if (config.allowedProtocols.cardinality() != 0 &&
1134                    !mWifiNative.setNetworkVariable(
1135                        netId,
1136                        WifiConfiguration.Protocol.varName,
1137                        allowedProtocolsString)) {
1138                loge("failed to set proto: "+
1139                        allowedProtocolsString);
1140                break setVariables;
1141            }
1142
1143            String allowedAuthAlgorithmsString =
1144                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
1145            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
1146                    !mWifiNative.setNetworkVariable(
1147                        netId,
1148                        WifiConfiguration.AuthAlgorithm.varName,
1149                        allowedAuthAlgorithmsString)) {
1150                loge("failed to set auth_alg: "+
1151                        allowedAuthAlgorithmsString);
1152                break setVariables;
1153            }
1154
1155            String allowedPairwiseCiphersString =
1156                    makeString(config.allowedPairwiseCiphers,
1157                    WifiConfiguration.PairwiseCipher.strings);
1158            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
1159                    !mWifiNative.setNetworkVariable(
1160                        netId,
1161                        WifiConfiguration.PairwiseCipher.varName,
1162                        allowedPairwiseCiphersString)) {
1163                loge("failed to set pairwise: "+
1164                        allowedPairwiseCiphersString);
1165                break setVariables;
1166            }
1167
1168            String allowedGroupCiphersString =
1169                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
1170            if (config.allowedGroupCiphers.cardinality() != 0 &&
1171                    !mWifiNative.setNetworkVariable(
1172                        netId,
1173                        WifiConfiguration.GroupCipher.varName,
1174                        allowedGroupCiphersString)) {
1175                loge("failed to set group: "+
1176                        allowedGroupCiphersString);
1177                break setVariables;
1178            }
1179
1180            // Prevent client screw-up by passing in a WifiConfiguration we gave it
1181            // by preventing "*" as a key.
1182            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
1183                    !mWifiNative.setNetworkVariable(
1184                        netId,
1185                        WifiConfiguration.pskVarName,
1186                        config.preSharedKey)) {
1187                loge("failed to set psk");
1188                break setVariables;
1189            }
1190
1191            boolean hasSetKey = false;
1192            if (config.wepKeys != null) {
1193                for (int i = 0; i < config.wepKeys.length; i++) {
1194                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
1195                    // by preventing "*" as a key.
1196                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
1197                        if (!mWifiNative.setNetworkVariable(
1198                                    netId,
1199                                    WifiConfiguration.wepKeyVarNames[i],
1200                                    config.wepKeys[i])) {
1201                            loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
1202                            break setVariables;
1203                        }
1204                        hasSetKey = true;
1205                    }
1206                }
1207            }
1208
1209            if (hasSetKey) {
1210                if (!mWifiNative.setNetworkVariable(
1211                            netId,
1212                            WifiConfiguration.wepTxKeyIdxVarName,
1213                            Integer.toString(config.wepTxKeyIndex))) {
1214                    loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
1215                    break setVariables;
1216                }
1217            }
1218
1219            if (!mWifiNative.setNetworkVariable(
1220                        netId,
1221                        WifiConfiguration.priorityVarName,
1222                        Integer.toString(config.priority))) {
1223                loge(config.SSID + ": failed to set priority: "
1224                        +config.priority);
1225                break setVariables;
1226            }
1227
1228            if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
1229                        netId,
1230                        WifiConfiguration.hiddenSSIDVarName,
1231                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
1232                loge(config.SSID + ": failed to set hiddenSSID: "+
1233                        config.hiddenSSID);
1234                break setVariables;
1235            }
1236
1237            if (config.enterpriseConfig != null &&
1238                    config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
1239
1240                WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
1241
1242                if (enterpriseConfig.needsKeyStore()) {
1243                    /**
1244                     * Keyguard settings may eventually be controlled by device policy.
1245                     * We check here if keystore is unlocked before installing
1246                     * credentials.
1247                     * TODO: Do we need a dialog here ?
1248                     */
1249                    if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
1250                        loge(config.SSID + ": key store is locked");
1251                        break setVariables;
1252                    }
1253
1254                    try {
1255                        /* config passed may include only fields being updated.
1256                         * In order to generate the key id, fetch uninitialized
1257                         * fields from the currently tracked configuration
1258                         */
1259                        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
1260                        String keyId = config.getKeyIdForCredentials(currentConfig);
1261
1262                        if (!enterpriseConfig.installKeys(mKeyStore, keyId)) {
1263                            loge(config.SSID + ": failed to install keys");
1264                            break setVariables;
1265                        }
1266                    } catch (IllegalStateException e) {
1267                        loge(config.SSID + " invalid config for key installation");
1268                        break setVariables;
1269                    }
1270                }
1271
1272                HashMap<String, String> enterpriseFields = enterpriseConfig.getFields();
1273                for (String key : enterpriseFields.keySet()) {
1274                        String value = enterpriseFields.get(key);
1275                        if (!mWifiNative.setNetworkVariable(
1276                                    netId,
1277                                    key,
1278                                    value)) {
1279                            enterpriseConfig.removeKeys(mKeyStore);
1280                            loge(config.SSID + ": failed to set " + key +
1281                                    ": " + value);
1282                            break setVariables;
1283                        }
1284                }
1285            }
1286            updateFailed = false;
1287        } //end of setVariables
1288
1289        if (updateFailed) {
1290            if (newNetwork) {
1291                mWifiNative.removeNetwork(netId);
1292                loge("Failed to set a network variable, removed network: " + netId);
1293            }
1294            return new NetworkUpdateResult(INVALID_NETWORK_ID);
1295        }
1296
1297        /* An update of the network variables requires reading them
1298         * back from the supplicant to update mConfiguredNetworks.
1299         * This is because some of the variables (SSID, wep keys &
1300         * passphrases) reflect different values when read back than
1301         * when written. For example, wep key is stored as * irrespective
1302         * of the value sent to the supplicant
1303         */
1304        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
1305        if (currentConfig == null) {
1306            currentConfig = new WifiConfiguration();
1307            currentConfig.ipAssignment = IpAssignment.DHCP;
1308            currentConfig.proxySettings = ProxySettings.NONE;
1309            currentConfig.networkId = netId;
1310        }
1311
1312        readNetworkVariables(currentConfig);
1313
1314        mConfiguredNetworks.put(netId, currentConfig);
1315        mNetworkIds.put(configKey(currentConfig), netId);
1316
1317        NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
1318        result.setIsNewNetwork(newNetwork);
1319        result.setNetworkId(netId);
1320        return result;
1321    }
1322
1323    /* Compare current and new configuration and write to file on change */
1324    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
1325            WifiConfiguration currentConfig,
1326            WifiConfiguration newConfig) {
1327        boolean ipChanged = false;
1328        boolean proxyChanged = false;
1329        LinkProperties linkProperties = null;
1330
1331        switch (newConfig.ipAssignment) {
1332            case STATIC:
1333                Collection<LinkAddress> currentLinkAddresses = currentConfig.linkProperties
1334                        .getLinkAddresses();
1335                Collection<LinkAddress> newLinkAddresses = newConfig.linkProperties
1336                        .getLinkAddresses();
1337                Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses();
1338                Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses();
1339                Collection<RouteInfo> currentRoutes = currentConfig.linkProperties.getRoutes();
1340                Collection<RouteInfo> newRoutes = newConfig.linkProperties.getRoutes();
1341
1342                boolean linkAddressesDiffer =
1343                        (currentLinkAddresses.size() != newLinkAddresses.size()) ||
1344                        !currentLinkAddresses.containsAll(newLinkAddresses);
1345                boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) ||
1346                        !currentDnses.containsAll(newDnses);
1347                boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) ||
1348                        !currentRoutes.containsAll(newRoutes);
1349
1350                if ((currentConfig.ipAssignment != newConfig.ipAssignment) ||
1351                        linkAddressesDiffer ||
1352                        dnsesDiffer ||
1353                        routesDiffer) {
1354                    ipChanged = true;
1355                }
1356                break;
1357            case DHCP:
1358                if (currentConfig.ipAssignment != newConfig.ipAssignment) {
1359                    ipChanged = true;
1360                }
1361                break;
1362            case UNASSIGNED:
1363                /* Ignore */
1364                break;
1365            default:
1366                loge("Ignore invalid ip assignment during write");
1367                break;
1368        }
1369
1370        switch (newConfig.proxySettings) {
1371            case STATIC:
1372            case PAC:
1373                ProxyProperties newHttpProxy = newConfig.linkProperties.getHttpProxy();
1374                ProxyProperties currentHttpProxy = currentConfig.linkProperties.getHttpProxy();
1375
1376                if (newHttpProxy != null) {
1377                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
1378                } else {
1379                    proxyChanged = (currentHttpProxy != null);
1380                }
1381                break;
1382            case NONE:
1383                if (currentConfig.proxySettings != newConfig.proxySettings) {
1384                    proxyChanged = true;
1385                }
1386                break;
1387            case UNASSIGNED:
1388                /* Ignore */
1389                break;
1390            default:
1391                loge("Ignore invalid proxy configuration during write");
1392                break;
1393        }
1394
1395        if (!ipChanged) {
1396            linkProperties = copyIpSettingsFromConfig(currentConfig);
1397        } else {
1398            currentConfig.ipAssignment = newConfig.ipAssignment;
1399            linkProperties = copyIpSettingsFromConfig(newConfig);
1400            log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
1401                    linkProperties.toString());
1402        }
1403
1404
1405        if (!proxyChanged) {
1406            linkProperties.setHttpProxy(currentConfig.linkProperties.getHttpProxy());
1407        } else {
1408            currentConfig.proxySettings = newConfig.proxySettings;
1409            linkProperties.setHttpProxy(newConfig.linkProperties.getHttpProxy());
1410            log("proxy changed SSID = " + currentConfig.SSID);
1411            if (linkProperties.getHttpProxy() != null) {
1412                log(" proxyProperties: " + linkProperties.getHttpProxy().toString());
1413            }
1414        }
1415
1416        if (ipChanged || proxyChanged) {
1417            currentConfig.linkProperties = linkProperties;
1418            writeIpAndProxyConfigurations();
1419            sendConfiguredNetworksChangedBroadcast(currentConfig,
1420                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1421        }
1422        return new NetworkUpdateResult(ipChanged, proxyChanged);
1423    }
1424
1425    private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) {
1426        LinkProperties linkProperties = new LinkProperties();
1427        linkProperties.setInterfaceName(config.linkProperties.getInterfaceName());
1428        for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
1429            linkProperties.addLinkAddress(linkAddr);
1430        }
1431        for (RouteInfo route : config.linkProperties.getRoutes()) {
1432            linkProperties.addRoute(route);
1433        }
1434        for (InetAddress dns : config.linkProperties.getDnses()) {
1435            linkProperties.addDns(dns);
1436        }
1437        return linkProperties;
1438    }
1439
1440    /**
1441     * Read the variables from the supplicant daemon that are needed to
1442     * fill in the WifiConfiguration object.
1443     *
1444     * @param config the {@link WifiConfiguration} object to be filled in.
1445     */
1446    private void readNetworkVariables(WifiConfiguration config) {
1447
1448        int netId = config.networkId;
1449        if (netId < 0)
1450            return;
1451
1452        /*
1453         * TODO: maybe should have a native method that takes an array of
1454         * variable names and returns an array of values. But we'd still
1455         * be doing a round trip to the supplicant daemon for each variable.
1456         */
1457        String value;
1458
1459        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
1460        if (!TextUtils.isEmpty(value)) {
1461            if (value.charAt(0) != '"') {
1462                config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
1463                //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
1464                //supplicant string
1465            } else {
1466                config.SSID = value;
1467            }
1468        } else {
1469            config.SSID = null;
1470        }
1471
1472        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
1473        if (!TextUtils.isEmpty(value)) {
1474            config.BSSID = value;
1475        } else {
1476            config.BSSID = null;
1477        }
1478
1479        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
1480        config.priority = -1;
1481        if (!TextUtils.isEmpty(value)) {
1482            try {
1483                config.priority = Integer.parseInt(value);
1484            } catch (NumberFormatException ignore) {
1485            }
1486        }
1487
1488        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
1489        config.hiddenSSID = false;
1490        if (!TextUtils.isEmpty(value)) {
1491            try {
1492                config.hiddenSSID = Integer.parseInt(value) != 0;
1493            } catch (NumberFormatException ignore) {
1494            }
1495        }
1496
1497        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
1498        config.wepTxKeyIndex = -1;
1499        if (!TextUtils.isEmpty(value)) {
1500            try {
1501                config.wepTxKeyIndex = Integer.parseInt(value);
1502            } catch (NumberFormatException ignore) {
1503            }
1504        }
1505
1506        for (int i = 0; i < 4; i++) {
1507            value = mWifiNative.getNetworkVariable(netId,
1508                    WifiConfiguration.wepKeyVarNames[i]);
1509            if (!TextUtils.isEmpty(value)) {
1510                config.wepKeys[i] = value;
1511            } else {
1512                config.wepKeys[i] = null;
1513            }
1514        }
1515
1516        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
1517        if (!TextUtils.isEmpty(value)) {
1518            config.preSharedKey = value;
1519        } else {
1520            config.preSharedKey = null;
1521        }
1522
1523        value = mWifiNative.getNetworkVariable(config.networkId,
1524                WifiConfiguration.Protocol.varName);
1525        if (!TextUtils.isEmpty(value)) {
1526            String vals[] = value.split(" ");
1527            for (String val : vals) {
1528                int index =
1529                    lookupString(val, WifiConfiguration.Protocol.strings);
1530                if (0 <= index) {
1531                    config.allowedProtocols.set(index);
1532                }
1533            }
1534        }
1535
1536        value = mWifiNative.getNetworkVariable(config.networkId,
1537                WifiConfiguration.KeyMgmt.varName);
1538        if (!TextUtils.isEmpty(value)) {
1539            String vals[] = value.split(" ");
1540            for (String val : vals) {
1541                int index =
1542                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
1543                if (0 <= index) {
1544                    config.allowedKeyManagement.set(index);
1545                }
1546            }
1547        }
1548
1549        value = mWifiNative.getNetworkVariable(config.networkId,
1550                WifiConfiguration.AuthAlgorithm.varName);
1551        if (!TextUtils.isEmpty(value)) {
1552            String vals[] = value.split(" ");
1553            for (String val : vals) {
1554                int index =
1555                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
1556                if (0 <= index) {
1557                    config.allowedAuthAlgorithms.set(index);
1558                }
1559            }
1560        }
1561
1562        value = mWifiNative.getNetworkVariable(config.networkId,
1563                WifiConfiguration.PairwiseCipher.varName);
1564        if (!TextUtils.isEmpty(value)) {
1565            String vals[] = value.split(" ");
1566            for (String val : vals) {
1567                int index =
1568                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
1569                if (0 <= index) {
1570                    config.allowedPairwiseCiphers.set(index);
1571                }
1572            }
1573        }
1574
1575        value = mWifiNative.getNetworkVariable(config.networkId,
1576                WifiConfiguration.GroupCipher.varName);
1577        if (!TextUtils.isEmpty(value)) {
1578            String vals[] = value.split(" ");
1579            for (String val : vals) {
1580                int index =
1581                    lookupString(val, WifiConfiguration.GroupCipher.strings);
1582                if (0 <= index) {
1583                    config.allowedGroupCiphers.set(index);
1584                }
1585            }
1586        }
1587
1588        if (config.enterpriseConfig == null) {
1589            config.enterpriseConfig = new WifiEnterpriseConfig();
1590        }
1591        HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
1592        for (String key : WifiEnterpriseConfig.getSupplicantKeys()) {
1593            value = mWifiNative.getNetworkVariable(netId, key);
1594            if (!TextUtils.isEmpty(value)) {
1595                enterpriseFields.put(key, removeDoubleQuotes(value));
1596            } else {
1597                enterpriseFields.put(key, WifiEnterpriseConfig.EMPTY_VALUE);
1598            }
1599        }
1600
1601        if (config.enterpriseConfig.migrateOldEapTlsNative(mWifiNative, netId)) {
1602            saveConfig();
1603        }
1604
1605        config.enterpriseConfig.migrateCerts(mKeyStore);
1606        config.enterpriseConfig.initializeSoftwareKeystoreFlag(mKeyStore);
1607    }
1608
1609    private String removeDoubleQuotes(String string) {
1610        int length = string.length();
1611        if ((length > 1) && (string.charAt(0) == '"')
1612                && (string.charAt(length - 1) == '"')) {
1613            return string.substring(1, length - 1);
1614        }
1615        return string;
1616    }
1617
1618    private String convertToQuotedString(String string) {
1619        return "\"" + string + "\"";
1620    }
1621
1622    private String makeString(BitSet set, String[] strings) {
1623        StringBuffer buf = new StringBuffer();
1624        int nextSetBit = -1;
1625
1626        /* Make sure all set bits are in [0, strings.length) to avoid
1627         * going out of bounds on strings.  (Shouldn't happen, but...) */
1628        set = set.get(0, strings.length);
1629
1630        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
1631            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
1632        }
1633
1634        // remove trailing space
1635        if (set.cardinality() > 0) {
1636            buf.setLength(buf.length() - 1);
1637        }
1638
1639        return buf.toString();
1640    }
1641
1642    private int lookupString(String string, String[] strings) {
1643        int size = strings.length;
1644
1645        string = string.replace('-', '_');
1646
1647        for (int i = 0; i < size; i++)
1648            if (string.equals(strings[i]))
1649                return i;
1650
1651        // if we ever get here, we should probably add the
1652        // value to WifiConfiguration to reflect that it's
1653        // supported by the WPA supplicant
1654        loge("Failed to look-up a string: " + string);
1655
1656        return -1;
1657    }
1658
1659    /* Returns a unique for a given configuration */
1660    private static int configKey(WifiConfiguration config) {
1661        String key;
1662
1663        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1664            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
1665        } else if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
1666                config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1667            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
1668        } else if (config.wepKeys[0] != null) {
1669            key = config.SSID + "WEP";
1670        } else {
1671            key = config.SSID + KeyMgmt.strings[KeyMgmt.NONE];
1672        }
1673
1674        return key.hashCode();
1675    }
1676
1677    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1678        pw.println("WifiConfigStore");
1679        pw.println("mLastPriority " + mLastPriority);
1680        pw.println("Configured networks");
1681        for (WifiConfiguration conf : getConfiguredNetworks()) {
1682            pw.println(conf);
1683        }
1684        pw.println();
1685
1686        if (mLocalLog != null) {
1687            pw.println("WifiConfigStore - Log Begin ----");
1688            mLocalLog.dump(fd, pw, args);
1689            pw.println("WifiConfigStore - Log End ----");
1690        }
1691    }
1692
1693    public String getConfigFile() {
1694        return ipConfigFile;
1695    }
1696
1697    private void loge(String s) {
1698        Log.e(TAG, s);
1699    }
1700
1701    private void log(String s) {
1702        Log.d(TAG, s);
1703    }
1704
1705    private void localLog(String s) {
1706        if (mLocalLog != null) {
1707            mLocalLog.log(s);
1708        }
1709    }
1710
1711    private void localLog(String s, int netId) {
1712        if (mLocalLog == null) {
1713            return;
1714        }
1715
1716        WifiConfiguration config;
1717        synchronized(mConfiguredNetworks) {
1718            config = mConfiguredNetworks.get(netId);
1719        }
1720
1721        if (config != null) {
1722            mLocalLog.log(s + " " + config.getPrintableSsid());
1723        } else {
1724            mLocalLog.log(s + " " + netId);
1725        }
1726    }
1727
1728}
1729