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