WifiConfigStore.java revision e095675c872f40f630aa3f9189eb5c02f3cfee6d
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.Message;
35import android.os.Handler;
36import android.os.HandlerThread;
37import android.os.UserHandle;
38import android.text.TextUtils;
39import android.util.Log;
40
41import java.io.BufferedInputStream;
42import java.io.BufferedOutputStream;
43import java.io.DataInputStream;
44import java.io.DataOutputStream;
45import java.io.EOFException;
46import java.io.FileDescriptor;
47import java.io.FileInputStream;
48import java.io.FileOutputStream;
49import java.io.IOException;
50import java.io.PrintWriter;
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     * set IP configuration for a given network id
506     */
507    void setLinkProperties(int netId, LinkProperties linkProperties) {
508        WifiConfiguration config = mConfiguredNetworks.get(netId);
509        if (config != null) {
510            // add old proxy details - TODO - is this still needed?
511            if(config.linkProperties != null) {
512                linkProperties.setHttpProxy(config.linkProperties.getHttpProxy());
513            }
514            config.linkProperties = linkProperties;
515        }
516    }
517
518    /**
519     * clear IP configuration for a given network id
520     * @param network id
521     */
522    void clearLinkProperties(int netId) {
523        WifiConfiguration config = mConfiguredNetworks.get(netId);
524        if (config != null && config.linkProperties != null) {
525            // Clear everything except proxy
526            ProxyProperties proxy = config.linkProperties.getHttpProxy();
527            config.linkProperties.clear();
528            config.linkProperties.setHttpProxy(proxy);
529        }
530    }
531
532
533    /**
534     * Fetch the proxy properties for a given network id
535     * @param network id
536     * @return ProxyProperties for the network id
537     */
538    ProxyProperties getProxyProperties(int netId) {
539        LinkProperties linkProperties = getLinkProperties(netId);
540        if (linkProperties != null) {
541            return new ProxyProperties(linkProperties.getHttpProxy());
542        }
543        return null;
544    }
545
546    /**
547     * Return if the specified network is using static IP
548     * @param network id
549     * @return {@code true} if using static ip for netId
550     */
551    boolean isUsingStaticIp(int netId) {
552        WifiConfiguration config = mConfiguredNetworks.get(netId);
553        if (config != null && config.ipAssignment == IpAssignment.STATIC) {
554            return true;
555        }
556        return false;
557    }
558
559    /**
560     * Should be called when a single network configuration is made.
561     * @param network The network configuration that changed.
562     * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
563     * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
564     */
565    private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
566            int reason) {
567        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
568        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
569        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
570        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
571        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
572        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
573    }
574
575    /**
576     * Should be called when multiple network configuration changes are made.
577     */
578    private void sendConfiguredNetworksChangedBroadcast() {
579        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
580        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
581        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
582        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
583    }
584
585    void loadConfiguredNetworks() {
586        String listStr = mWifiNative.listNetworks();
587        mLastPriority = 0;
588
589        mConfiguredNetworks.clear();
590        mNetworkIds.clear();
591
592        if (listStr == null)
593            return;
594
595        String[] lines = listStr.split("\n");
596        // Skip the first line, which is a header
597        for (int i = 1; i < lines.length; i++) {
598            String[] result = lines[i].split("\t");
599            // network-id | ssid | bssid | flags
600            WifiConfiguration config = new WifiConfiguration();
601            try {
602                config.networkId = Integer.parseInt(result[0]);
603            } catch(NumberFormatException e) {
604                continue;
605            }
606            if (result.length > 3) {
607                if (result[3].indexOf("[CURRENT]") != -1)
608                    config.status = WifiConfiguration.Status.CURRENT;
609                else if (result[3].indexOf("[DISABLED]") != -1)
610                    config.status = WifiConfiguration.Status.DISABLED;
611                else
612                    config.status = WifiConfiguration.Status.ENABLED;
613            } else {
614                config.status = WifiConfiguration.Status.ENABLED;
615            }
616            readNetworkVariables(config);
617            if (config.priority > mLastPriority) {
618                mLastPriority = config.priority;
619            }
620            mConfiguredNetworks.put(config.networkId, config);
621            mNetworkIds.put(configKey(config), config.networkId);
622        }
623
624        readIpAndProxyConfigurations();
625        sendConfiguredNetworksChangedBroadcast();
626    }
627
628    /* Mark all networks except specified netId as disabled */
629    private void markAllNetworksDisabledExcept(int netId) {
630        for(WifiConfiguration config : mConfiguredNetworks.values()) {
631            if(config != null && config.networkId != netId) {
632                if (config.status != Status.DISABLED) {
633                    config.status = Status.DISABLED;
634                    config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
635                }
636            }
637        }
638    }
639
640    private void markAllNetworksDisabled() {
641        markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
642    }
643
644    private void writeIpAndProxyConfigurations() {
645
646        /* Make a copy */
647        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
648        for(WifiConfiguration config : mConfiguredNetworks.values()) {
649            networks.add(new WifiConfiguration(config));
650        }
651
652        DelayedDiskWrite.write(networks);
653    }
654
655    private static class DelayedDiskWrite {
656
657        private static HandlerThread sDiskWriteHandlerThread;
658        private static Handler sDiskWriteHandler;
659        /* Tracks multiple writes on the same thread */
660        private static int sWriteSequence = 0;
661        private static final String TAG = "DelayedDiskWrite";
662
663        static void write (final List<WifiConfiguration> networks) {
664
665            /* Do a delayed write to disk on a seperate handler thread */
666            synchronized (DelayedDiskWrite.class) {
667                if (++sWriteSequence == 1) {
668                    sDiskWriteHandlerThread = new HandlerThread("WifiConfigThread");
669                    sDiskWriteHandlerThread.start();
670                    sDiskWriteHandler = new Handler(sDiskWriteHandlerThread.getLooper());
671                }
672            }
673
674            sDiskWriteHandler.post(new Runnable() {
675                    @Override
676                    public void run() {
677                        onWriteCalled(networks);
678                    }
679                });
680        }
681
682        private static void onWriteCalled(List<WifiConfiguration> networks) {
683
684            DataOutputStream out = null;
685            try {
686                out = new DataOutputStream(new BufferedOutputStream(
687                            new FileOutputStream(ipConfigFile)));
688
689                out.writeInt(IPCONFIG_FILE_VERSION);
690
691                for(WifiConfiguration config : networks) {
692                    boolean writeToFile = false;
693
694                    try {
695                        LinkProperties linkProperties = config.linkProperties;
696                        switch (config.ipAssignment) {
697                            case STATIC:
698                                out.writeUTF(IP_ASSIGNMENT_KEY);
699                                out.writeUTF(config.ipAssignment.toString());
700                                for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) {
701                                    out.writeUTF(LINK_ADDRESS_KEY);
702                                    out.writeUTF(linkAddr.getAddress().getHostAddress());
703                                    out.writeInt(linkAddr.getNetworkPrefixLength());
704                                }
705                                for (RouteInfo route : linkProperties.getRoutes()) {
706                                    out.writeUTF(GATEWAY_KEY);
707                                    LinkAddress dest = route.getDestination();
708                                    if (dest != null) {
709                                        out.writeInt(1);
710                                        out.writeUTF(dest.getAddress().getHostAddress());
711                                        out.writeInt(dest.getNetworkPrefixLength());
712                                    } else {
713                                        out.writeInt(0);
714                                    }
715                                    if (route.getGateway() != null) {
716                                        out.writeInt(1);
717                                        out.writeUTF(route.getGateway().getHostAddress());
718                                    } else {
719                                        out.writeInt(0);
720                                    }
721                                }
722                                for (InetAddress inetAddr : linkProperties.getDnses()) {
723                                    out.writeUTF(DNS_KEY);
724                                    out.writeUTF(inetAddr.getHostAddress());
725                                }
726                                writeToFile = true;
727                                break;
728                            case DHCP:
729                                out.writeUTF(IP_ASSIGNMENT_KEY);
730                                out.writeUTF(config.ipAssignment.toString());
731                                writeToFile = true;
732                                break;
733                            case UNASSIGNED:
734                                /* Ignore */
735                                break;
736                            default:
737                                loge("Ignore invalid ip assignment while writing");
738                                break;
739                        }
740
741                        switch (config.proxySettings) {
742                            case STATIC:
743                                ProxyProperties proxyProperties = linkProperties.getHttpProxy();
744                                String exclusionList = proxyProperties.getExclusionList();
745                                out.writeUTF(PROXY_SETTINGS_KEY);
746                                out.writeUTF(config.proxySettings.toString());
747                                out.writeUTF(PROXY_HOST_KEY);
748                                out.writeUTF(proxyProperties.getHost());
749                                out.writeUTF(PROXY_PORT_KEY);
750                                out.writeInt(proxyProperties.getPort());
751                                out.writeUTF(EXCLUSION_LIST_KEY);
752                                out.writeUTF(exclusionList);
753                                writeToFile = true;
754                                break;
755                            case NONE:
756                                out.writeUTF(PROXY_SETTINGS_KEY);
757                                out.writeUTF(config.proxySettings.toString());
758                                writeToFile = true;
759                                break;
760                            case UNASSIGNED:
761                                /* Ignore */
762                                break;
763                            default:
764                                loge("Ignthisore invalid proxy settings while writing");
765                                break;
766                        }
767                        if (writeToFile) {
768                            out.writeUTF(ID_KEY);
769                            out.writeInt(configKey(config));
770                        }
771                    } catch (NullPointerException e) {
772                        loge("Failure in writing " + config.linkProperties + e);
773                    }
774                    out.writeUTF(EOS);
775                }
776
777            } catch (IOException e) {
778                loge("Error writing data file");
779            } finally {
780                if (out != null) {
781                    try {
782                        out.close();
783                    } catch (Exception e) {}
784                }
785
786                //Quit if no more writes sent
787                synchronized (DelayedDiskWrite.class) {
788                    if (--sWriteSequence == 0) {
789                        sDiskWriteHandler.getLooper().quit();
790                        sDiskWriteHandler = null;
791                        sDiskWriteHandlerThread = null;
792                    }
793                }
794            }
795        }
796
797        private static void loge(String s) {
798            Log.e(TAG, s);
799        }
800    }
801
802    private void readIpAndProxyConfigurations() {
803
804        DataInputStream in = null;
805        try {
806            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
807                    ipConfigFile)));
808
809            int version = in.readInt();
810            if (version != 2 && version != 1) {
811                loge("Bad version on IP configuration file, ignore read");
812                return;
813            }
814
815            while (true) {
816                int id = -1;
817                IpAssignment ipAssignment = IpAssignment.UNASSIGNED;
818                ProxySettings proxySettings = ProxySettings.UNASSIGNED;
819                LinkProperties linkProperties = new LinkProperties();
820                String proxyHost = null;
821                int proxyPort = -1;
822                String exclusionList = null;
823                String key;
824
825                do {
826                    key = in.readUTF();
827                    try {
828                        if (key.equals(ID_KEY)) {
829                            id = in.readInt();
830                        } else if (key.equals(IP_ASSIGNMENT_KEY)) {
831                            ipAssignment = IpAssignment.valueOf(in.readUTF());
832                        } else if (key.equals(LINK_ADDRESS_KEY)) {
833                            LinkAddress linkAddr = new LinkAddress(
834                                    NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt());
835                            linkProperties.addLinkAddress(linkAddr);
836                        } else if (key.equals(GATEWAY_KEY)) {
837                            LinkAddress dest = null;
838                            InetAddress gateway = null;
839                            if (version == 1) {
840                                // only supported default gateways - leave the dest/prefix empty
841                                gateway = NetworkUtils.numericToInetAddress(in.readUTF());
842                            } else {
843                                if (in.readInt() == 1) {
844                                    dest = new LinkAddress(
845                                            NetworkUtils.numericToInetAddress(in.readUTF()),
846                                            in.readInt());
847                                }
848                                if (in.readInt() == 1) {
849                                    gateway = NetworkUtils.numericToInetAddress(in.readUTF());
850                                }
851                            }
852                            linkProperties.addRoute(new RouteInfo(dest, gateway));
853                        } else if (key.equals(DNS_KEY)) {
854                            linkProperties.addDns(
855                                    NetworkUtils.numericToInetAddress(in.readUTF()));
856                        } else if (key.equals(PROXY_SETTINGS_KEY)) {
857                            proxySettings = ProxySettings.valueOf(in.readUTF());
858                        } else if (key.equals(PROXY_HOST_KEY)) {
859                            proxyHost = in.readUTF();
860                        } else if (key.equals(PROXY_PORT_KEY)) {
861                            proxyPort = in.readInt();
862                        } else if (key.equals(EXCLUSION_LIST_KEY)) {
863                            exclusionList = in.readUTF();
864                        } else if (key.equals(EOS)) {
865                            break;
866                        } else {
867                            loge("Ignore unknown key " + key + "while reading");
868                        }
869                    } catch (IllegalArgumentException e) {
870                        loge("Ignore invalid address while reading" + e);
871                    }
872                } while (true);
873
874                if (id != -1) {
875                    WifiConfiguration config = mConfiguredNetworks.get(
876                            mNetworkIds.get(id));
877
878                    if (config == null) {
879                        loge("configuration found for missing network, ignored");
880                    } else {
881                        config.linkProperties = linkProperties;
882                        switch (ipAssignment) {
883                            case STATIC:
884                            case DHCP:
885                                config.ipAssignment = ipAssignment;
886                                break;
887                            case UNASSIGNED:
888                                //Ignore
889                                break;
890                            default:
891                                loge("Ignore invalid ip assignment while reading");
892                                break;
893                        }
894
895                        switch (proxySettings) {
896                            case STATIC:
897                                config.proxySettings = proxySettings;
898                                ProxyProperties proxyProperties =
899                                    new ProxyProperties(proxyHost, proxyPort, exclusionList);
900                                linkProperties.setHttpProxy(proxyProperties);
901                                break;
902                            case NONE:
903                                config.proxySettings = proxySettings;
904                                break;
905                            case UNASSIGNED:
906                                //Ignore
907                                break;
908                            default:
909                                loge("Ignore invalid proxy settings while reading");
910                                break;
911                        }
912                    }
913                } else {
914                    if (DBG) log("Missing id while parsing configuration");
915                }
916            }
917        } catch (EOFException ignore) {
918        } catch (IOException e) {
919            loge("Error parsing configuration" + e);
920        } finally {
921            if (in != null) {
922                try {
923                    in.close();
924                } catch (Exception e) {}
925            }
926        }
927    }
928
929    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
930        /*
931         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
932         * network configuration. Otherwise, the networkId should
933         * refer to an existing configuration.
934         */
935        int netId = config.networkId;
936        boolean newNetwork = false;
937        // networkId of INVALID_NETWORK_ID means we want to create a new network
938        if (netId == INVALID_NETWORK_ID) {
939            Integer savedNetId = mNetworkIds.get(configKey(config));
940            if (savedNetId != null) {
941                netId = savedNetId;
942            } else {
943                newNetwork = true;
944                netId = mWifiNative.addNetwork();
945                if (netId < 0) {
946                    loge("Failed to add a network!");
947                    return new NetworkUpdateResult(INVALID_NETWORK_ID);
948                }
949            }
950        }
951
952        boolean updateFailed = true;
953
954        setVariables: {
955
956            if (config.SSID != null &&
957                    !mWifiNative.setNetworkVariable(
958                        netId,
959                        WifiConfiguration.ssidVarName,
960                        config.SSID)) {
961                loge("failed to set SSID: "+config.SSID);
962                break setVariables;
963            }
964
965            if (config.BSSID != null &&
966                    !mWifiNative.setNetworkVariable(
967                        netId,
968                        WifiConfiguration.bssidVarName,
969                        config.BSSID)) {
970                loge("failed to set BSSID: "+config.BSSID);
971                break setVariables;
972            }
973
974            String allowedKeyManagementString =
975                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
976            if (config.allowedKeyManagement.cardinality() != 0 &&
977                    !mWifiNative.setNetworkVariable(
978                        netId,
979                        WifiConfiguration.KeyMgmt.varName,
980                        allowedKeyManagementString)) {
981                loge("failed to set key_mgmt: "+
982                        allowedKeyManagementString);
983                break setVariables;
984            }
985
986            String allowedProtocolsString =
987                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
988            if (config.allowedProtocols.cardinality() != 0 &&
989                    !mWifiNative.setNetworkVariable(
990                        netId,
991                        WifiConfiguration.Protocol.varName,
992                        allowedProtocolsString)) {
993                loge("failed to set proto: "+
994                        allowedProtocolsString);
995                break setVariables;
996            }
997
998            String allowedAuthAlgorithmsString =
999                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
1000            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
1001                    !mWifiNative.setNetworkVariable(
1002                        netId,
1003                        WifiConfiguration.AuthAlgorithm.varName,
1004                        allowedAuthAlgorithmsString)) {
1005                loge("failed to set auth_alg: "+
1006                        allowedAuthAlgorithmsString);
1007                break setVariables;
1008            }
1009
1010            String allowedPairwiseCiphersString =
1011                    makeString(config.allowedPairwiseCiphers,
1012                    WifiConfiguration.PairwiseCipher.strings);
1013            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
1014                    !mWifiNative.setNetworkVariable(
1015                        netId,
1016                        WifiConfiguration.PairwiseCipher.varName,
1017                        allowedPairwiseCiphersString)) {
1018                loge("failed to set pairwise: "+
1019                        allowedPairwiseCiphersString);
1020                break setVariables;
1021            }
1022
1023            String allowedGroupCiphersString =
1024                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
1025            if (config.allowedGroupCiphers.cardinality() != 0 &&
1026                    !mWifiNative.setNetworkVariable(
1027                        netId,
1028                        WifiConfiguration.GroupCipher.varName,
1029                        allowedGroupCiphersString)) {
1030                loge("failed to set group: "+
1031                        allowedGroupCiphersString);
1032                break setVariables;
1033            }
1034
1035            // Prevent client screw-up by passing in a WifiConfiguration we gave it
1036            // by preventing "*" as a key.
1037            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
1038                    !mWifiNative.setNetworkVariable(
1039                        netId,
1040                        WifiConfiguration.pskVarName,
1041                        config.preSharedKey)) {
1042                loge("failed to set psk");
1043                break setVariables;
1044            }
1045
1046            boolean hasSetKey = false;
1047            if (config.wepKeys != null) {
1048                for (int i = 0; i < config.wepKeys.length; i++) {
1049                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
1050                    // by preventing "*" as a key.
1051                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
1052                        if (!mWifiNative.setNetworkVariable(
1053                                    netId,
1054                                    WifiConfiguration.wepKeyVarNames[i],
1055                                    config.wepKeys[i])) {
1056                            loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
1057                            break setVariables;
1058                        }
1059                        hasSetKey = true;
1060                    }
1061                }
1062            }
1063
1064            if (hasSetKey) {
1065                if (!mWifiNative.setNetworkVariable(
1066                            netId,
1067                            WifiConfiguration.wepTxKeyIdxVarName,
1068                            Integer.toString(config.wepTxKeyIndex))) {
1069                    loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
1070                    break setVariables;
1071                }
1072            }
1073
1074            if (!mWifiNative.setNetworkVariable(
1075                        netId,
1076                        WifiConfiguration.priorityVarName,
1077                        Integer.toString(config.priority))) {
1078                loge(config.SSID + ": failed to set priority: "
1079                        +config.priority);
1080                break setVariables;
1081            }
1082
1083            if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
1084                        netId,
1085                        WifiConfiguration.hiddenSSIDVarName,
1086                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
1087                loge(config.SSID + ": failed to set hiddenSSID: "+
1088                        config.hiddenSSID);
1089                break setVariables;
1090            }
1091
1092            if (config.enterpriseConfig != null) {
1093                HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
1094                for (String key : enterpriseFields.keySet()) {
1095                        String value = enterpriseFields.get(key);
1096                        if (!mWifiNative.setNetworkVariable(
1097                                    netId,
1098                                    key,
1099                                    value)) {
1100                            loge(config.SSID + ": failed to set " + key +
1101                                    ": " + value);
1102                            break setVariables;
1103                        }
1104                }
1105            }
1106            updateFailed = false;
1107        }
1108
1109        if (updateFailed) {
1110            if (newNetwork) {
1111                mWifiNative.removeNetwork(netId);
1112                loge("Failed to set a network variable, removed network: " + netId);
1113            }
1114            return new NetworkUpdateResult(INVALID_NETWORK_ID);
1115        }
1116
1117        /* An update of the network variables requires reading them
1118         * back from the supplicant to update mConfiguredNetworks.
1119         * This is because some of the variables (SSID, wep keys &
1120         * passphrases) reflect different values when read back than
1121         * when written. For example, wep key is stored as * irrespective
1122         * of the value sent to the supplicant
1123         */
1124        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
1125        if (currentConfig == null) {
1126            currentConfig = new WifiConfiguration();
1127            currentConfig.networkId = netId;
1128        }
1129
1130        readNetworkVariables(currentConfig);
1131
1132        mConfiguredNetworks.put(netId, currentConfig);
1133        mNetworkIds.put(configKey(currentConfig), netId);
1134
1135        NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
1136        result.setIsNewNetwork(newNetwork);
1137        result.setNetworkId(netId);
1138        return result;
1139    }
1140
1141    /* Compare current and new configuration and write to file on change */
1142    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
1143            WifiConfiguration currentConfig,
1144            WifiConfiguration newConfig) {
1145        boolean ipChanged = false;
1146        boolean proxyChanged = false;
1147        LinkProperties linkProperties = new LinkProperties();
1148
1149        switch (newConfig.ipAssignment) {
1150            case STATIC:
1151                Collection<LinkAddress> currentLinkAddresses = currentConfig.linkProperties
1152                        .getLinkAddresses();
1153                Collection<LinkAddress> newLinkAddresses = newConfig.linkProperties
1154                        .getLinkAddresses();
1155                Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses();
1156                Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses();
1157                Collection<RouteInfo> currentRoutes = currentConfig.linkProperties.getRoutes();
1158                Collection<RouteInfo> newRoutes = newConfig.linkProperties.getRoutes();
1159
1160                boolean linkAddressesDiffer =
1161                        (currentLinkAddresses.size() != newLinkAddresses.size()) ||
1162                        !currentLinkAddresses.containsAll(newLinkAddresses);
1163                boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) ||
1164                        !currentDnses.containsAll(newDnses);
1165                boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) ||
1166                        !currentRoutes.containsAll(newRoutes);
1167
1168                if ((currentConfig.ipAssignment != newConfig.ipAssignment) ||
1169                        linkAddressesDiffer ||
1170                        dnsesDiffer ||
1171                        routesDiffer) {
1172                    ipChanged = true;
1173                }
1174                break;
1175            case DHCP:
1176                if (currentConfig.ipAssignment != newConfig.ipAssignment) {
1177                    ipChanged = true;
1178                }
1179                break;
1180            case UNASSIGNED:
1181                /* Ignore */
1182                break;
1183            default:
1184                loge("Ignore invalid ip assignment during write");
1185                break;
1186        }
1187
1188        switch (newConfig.proxySettings) {
1189            case STATIC:
1190                ProxyProperties newHttpProxy = newConfig.linkProperties.getHttpProxy();
1191                ProxyProperties currentHttpProxy = currentConfig.linkProperties.getHttpProxy();
1192
1193                if (newHttpProxy != null) {
1194                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
1195                } else {
1196                    proxyChanged = (currentHttpProxy != null);
1197                }
1198                break;
1199            case NONE:
1200                if (currentConfig.proxySettings != newConfig.proxySettings) {
1201                    proxyChanged = true;
1202                }
1203                break;
1204            case UNASSIGNED:
1205                /* Ignore */
1206                break;
1207            default:
1208                loge("Ignore invalid proxy configuration during write");
1209                break;
1210        }
1211
1212        if (!ipChanged) {
1213            addIpSettingsFromConfig(linkProperties, currentConfig);
1214        } else {
1215            currentConfig.ipAssignment = newConfig.ipAssignment;
1216            addIpSettingsFromConfig(linkProperties, newConfig);
1217            log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
1218                    linkProperties.toString());
1219        }
1220
1221
1222        if (!proxyChanged) {
1223            linkProperties.setHttpProxy(currentConfig.linkProperties.getHttpProxy());
1224        } else {
1225            currentConfig.proxySettings = newConfig.proxySettings;
1226            linkProperties.setHttpProxy(newConfig.linkProperties.getHttpProxy());
1227            log("proxy changed SSID = " + currentConfig.SSID);
1228            if (linkProperties.getHttpProxy() != null) {
1229                log(" proxyProperties: " + linkProperties.getHttpProxy().toString());
1230            }
1231        }
1232
1233        if (ipChanged || proxyChanged) {
1234            currentConfig.linkProperties = linkProperties;
1235            writeIpAndProxyConfigurations();
1236            sendConfiguredNetworksChangedBroadcast(currentConfig,
1237                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1238        }
1239        return new NetworkUpdateResult(ipChanged, proxyChanged);
1240    }
1241
1242    private void addIpSettingsFromConfig(LinkProperties linkProperties,
1243            WifiConfiguration config) {
1244        for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
1245            linkProperties.addLinkAddress(linkAddr);
1246        }
1247        for (RouteInfo route : config.linkProperties.getRoutes()) {
1248            linkProperties.addRoute(route);
1249        }
1250        for (InetAddress dns : config.linkProperties.getDnses()) {
1251            linkProperties.addDns(dns);
1252        }
1253    }
1254
1255    /**
1256     * Read the variables from the supplicant daemon that are needed to
1257     * fill in the WifiConfiguration object.
1258     *
1259     * @param config the {@link WifiConfiguration} object to be filled in.
1260     */
1261    private void readNetworkVariables(WifiConfiguration config) {
1262
1263        int netId = config.networkId;
1264        if (netId < 0)
1265            return;
1266
1267        /*
1268         * TODO: maybe should have a native method that takes an array of
1269         * variable names and returns an array of values. But we'd still
1270         * be doing a round trip to the supplicant daemon for each variable.
1271         */
1272        String value;
1273
1274        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
1275        if (!TextUtils.isEmpty(value)) {
1276            if (value.charAt(0) != '"') {
1277                config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
1278                //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
1279                //supplicant string
1280            } else {
1281                config.SSID = value;
1282            }
1283        } else {
1284            config.SSID = null;
1285        }
1286
1287        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
1288        if (!TextUtils.isEmpty(value)) {
1289            config.BSSID = value;
1290        } else {
1291            config.BSSID = null;
1292        }
1293
1294        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
1295        config.priority = -1;
1296        if (!TextUtils.isEmpty(value)) {
1297            try {
1298                config.priority = Integer.parseInt(value);
1299            } catch (NumberFormatException ignore) {
1300            }
1301        }
1302
1303        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
1304        config.hiddenSSID = false;
1305        if (!TextUtils.isEmpty(value)) {
1306            try {
1307                config.hiddenSSID = Integer.parseInt(value) != 0;
1308            } catch (NumberFormatException ignore) {
1309            }
1310        }
1311
1312        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
1313        config.wepTxKeyIndex = -1;
1314        if (!TextUtils.isEmpty(value)) {
1315            try {
1316                config.wepTxKeyIndex = Integer.parseInt(value);
1317            } catch (NumberFormatException ignore) {
1318            }
1319        }
1320
1321        for (int i = 0; i < 4; i++) {
1322            value = mWifiNative.getNetworkVariable(netId,
1323                    WifiConfiguration.wepKeyVarNames[i]);
1324            if (!TextUtils.isEmpty(value)) {
1325                config.wepKeys[i] = value;
1326            } else {
1327                config.wepKeys[i] = null;
1328            }
1329        }
1330
1331        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
1332        if (!TextUtils.isEmpty(value)) {
1333            config.preSharedKey = value;
1334        } else {
1335            config.preSharedKey = null;
1336        }
1337
1338        value = mWifiNative.getNetworkVariable(config.networkId,
1339                WifiConfiguration.Protocol.varName);
1340        if (!TextUtils.isEmpty(value)) {
1341            String vals[] = value.split(" ");
1342            for (String val : vals) {
1343                int index =
1344                    lookupString(val, WifiConfiguration.Protocol.strings);
1345                if (0 <= index) {
1346                    config.allowedProtocols.set(index);
1347                }
1348            }
1349        }
1350
1351        value = mWifiNative.getNetworkVariable(config.networkId,
1352                WifiConfiguration.KeyMgmt.varName);
1353        if (!TextUtils.isEmpty(value)) {
1354            String vals[] = value.split(" ");
1355            for (String val : vals) {
1356                int index =
1357                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
1358                if (0 <= index) {
1359                    config.allowedKeyManagement.set(index);
1360                }
1361            }
1362        }
1363
1364        value = mWifiNative.getNetworkVariable(config.networkId,
1365                WifiConfiguration.AuthAlgorithm.varName);
1366        if (!TextUtils.isEmpty(value)) {
1367            String vals[] = value.split(" ");
1368            for (String val : vals) {
1369                int index =
1370                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
1371                if (0 <= index) {
1372                    config.allowedAuthAlgorithms.set(index);
1373                }
1374            }
1375        }
1376
1377        value = mWifiNative.getNetworkVariable(config.networkId,
1378                WifiConfiguration.PairwiseCipher.varName);
1379        if (!TextUtils.isEmpty(value)) {
1380            String vals[] = value.split(" ");
1381            for (String val : vals) {
1382                int index =
1383                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
1384                if (0 <= index) {
1385                    config.allowedPairwiseCiphers.set(index);
1386                }
1387            }
1388        }
1389
1390        value = mWifiNative.getNetworkVariable(config.networkId,
1391                WifiConfiguration.GroupCipher.varName);
1392        if (!TextUtils.isEmpty(value)) {
1393            String vals[] = value.split(" ");
1394            for (String val : vals) {
1395                int index =
1396                    lookupString(val, WifiConfiguration.GroupCipher.strings);
1397                if (0 <= index) {
1398                    config.allowedGroupCiphers.set(index);
1399                }
1400            }
1401        }
1402
1403        if (config.enterpriseConfig == null) {
1404            config.enterpriseConfig = new WifiEnterpriseConfig();
1405        }
1406        HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
1407        for (String key : WifiEnterpriseConfig.getSupplicantKeys()) {
1408            value = mWifiNative.getNetworkVariable(netId, key);
1409            if (!TextUtils.isEmpty(value)) {
1410                enterpriseFields.put(key, removeDoubleQuotes(value));
1411            } else {
1412                enterpriseFields.put(key, WifiEnterpriseConfig.EMPTY_VALUE);
1413            }
1414        }
1415
1416        if (config.enterpriseConfig.migrateOldEapTlsNative(mWifiNative, netId)) {
1417            saveConfig();
1418        }
1419    }
1420
1421    private String removeDoubleQuotes(String string) {
1422        int length = string.length();
1423        if ((length > 1) && (string.charAt(0) == '"')
1424                && (string.charAt(length - 1) == '"')) {
1425            return string.substring(1, length - 1);
1426        }
1427        return string;
1428    }
1429
1430    private String convertToQuotedString(String string) {
1431        return "\"" + string + "\"";
1432    }
1433
1434    private String makeString(BitSet set, String[] strings) {
1435        StringBuffer buf = new StringBuffer();
1436        int nextSetBit = -1;
1437
1438        /* Make sure all set bits are in [0, strings.length) to avoid
1439         * going out of bounds on strings.  (Shouldn't happen, but...) */
1440        set = set.get(0, strings.length);
1441
1442        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
1443            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
1444        }
1445
1446        // remove trailing space
1447        if (set.cardinality() > 0) {
1448            buf.setLength(buf.length() - 1);
1449        }
1450
1451        return buf.toString();
1452    }
1453
1454    private int lookupString(String string, String[] strings) {
1455        int size = strings.length;
1456
1457        string = string.replace('-', '_');
1458
1459        for (int i = 0; i < size; i++)
1460            if (string.equals(strings[i]))
1461                return i;
1462
1463        // if we ever get here, we should probably add the
1464        // value to WifiConfiguration to reflect that it's
1465        // supported by the WPA supplicant
1466        loge("Failed to look-up a string: " + string);
1467
1468        return -1;
1469    }
1470
1471    /* Returns a unique for a given configuration */
1472    private static int configKey(WifiConfiguration config) {
1473        String key;
1474
1475        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1476            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
1477        } else if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
1478                config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1479            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
1480        } else if (config.wepKeys[0] != null) {
1481            key = config.SSID + "WEP";
1482        } else {
1483            key = config.SSID + KeyMgmt.strings[KeyMgmt.NONE];
1484        }
1485
1486        return key.hashCode();
1487    }
1488
1489    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1490        pw.println("WifiConfigStore");
1491        pw.println("mLastPriority " + mLastPriority);
1492        pw.println("Configured networks");
1493        for (WifiConfiguration conf : getConfiguredNetworks()) {
1494            pw.println(conf);
1495        }
1496        pw.println();
1497    }
1498
1499    public String getConfigFile() {
1500        return ipConfigFile;
1501    }
1502
1503    private void loge(String s) {
1504        Log.e(TAG, s);
1505    }
1506
1507    private void log(String s) {
1508        Log.d(TAG, s);
1509    }
1510}
1511