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