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