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