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