WifiConfigStore.java revision 96d15e49bb2d9f22b307b4c9cafd5f51b87667a1
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import android.content.Context;
20import android.content.Intent;
21import android.net.IpConfiguration;
22import android.net.IpConfiguration.IpAssignment;
23import android.net.IpConfiguration.ProxySettings;
24import android.net.LinkAddress;
25import android.net.LinkProperties;
26import android.net.NetworkInfo.DetailedState;
27import android.net.ProxyInfo;
28import android.net.RouteInfo;
29import android.net.wifi.WifiConfiguration;
30import android.net.wifi.WifiConfiguration.KeyMgmt;
31import android.net.wifi.WifiConfiguration.Status;
32import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
33
34import android.net.wifi.WifiEnterpriseConfig;
35import android.net.wifi.WifiManager;
36import android.net.wifi.WifiSsid;
37import android.net.wifi.WpsInfo;
38import android.net.wifi.WpsResult;
39import android.net.wifi.ScanResult;
40import android.net.wifi.WifiInfo;
41
42import android.os.Environment;
43import android.os.FileObserver;
44import android.os.Process;
45import android.os.SystemClock;
46import android.os.UserHandle;
47import android.security.Credentials;
48import android.security.KeyChain;
49import android.security.KeyStore;
50import android.text.TextUtils;
51import android.util.LocalLog;
52import android.util.Log;
53import android.util.SparseArray;
54
55import com.android.server.net.DelayedDiskWrite;
56import com.android.server.net.IpConfigStore;
57
58import java.io.BufferedReader;
59import java.io.BufferedInputStream;
60import java.io.DataInputStream;
61import java.io.DataOutputStream;
62import java.io.EOFException;
63import java.io.File;
64import java.io.FileDescriptor;
65import java.io.FileInputStream;
66import java.io.FileNotFoundException;
67import java.io.FileReader;
68import java.io.IOException;
69import java.io.PrintWriter;
70import java.math.BigInteger;
71import java.net.InetAddress;
72import java.nio.charset.Charset;
73import java.security.PrivateKey;
74import java.security.cert.Certificate;
75import java.security.cert.CertificateException;
76import java.text.SimpleDateFormat;
77import java.text.DateFormat;
78import java.util.*;
79
80/**
81 * This class provides the API to manage configured
82 * wifi networks. The API is not thread safe is being
83 * used only from WifiStateMachine.
84 *
85 * It deals with the following
86 * - Add/update/remove a WifiConfiguration
87 *   The configuration contains two types of information.
88 *     = IP and proxy configuration that is handled by WifiConfigStore and
89 *       is saved to disk on any change.
90 *
91 *       The format of configuration file is as follows:
92 *       <version>
93 *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
94 *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
95 *       ..
96 *
97 *       (key, value) pairs for a given network are grouped together and can
98 *       be in any order. A EOS at the end of a set of (key, value) pairs
99 *       indicates that the next set of (key, value) pairs are for a new
100 *       network. A network is identified by a unique ID_KEY. If there is no
101 *       ID_KEY in the (key, value) pairs, the data is discarded.
102 *
103 *       An invalid version on read would result in discarding the contents of
104 *       the file. On the next write, the latest version is written to file.
105 *
106 *       Any failures during read or write to the configuration file are ignored
107 *       without reporting to the user since the likelihood of these errors are
108 *       low and the impact on connectivity is low.
109 *
110 *     = SSID & security details that is pushed to the supplicant.
111 *       supplicant saves these details to the disk on calling
112 *       saveConfigCommand().
113 *
114 *       We have two kinds of APIs exposed:
115 *        > public API calls that provide fine grained control
116 *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
117 *          removeNetwork(). For these calls, the config is not persisted
118 *          to the disk. (TODO: deprecate these calls in WifiManager)
119 *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
120 *          These calls persist the supplicant config to disk.
121 *
122 * - Maintain a list of configured networks for quick access
123 *
124 */
125public class WifiConfigStore extends IpConfigStore {
126
127    private Context mContext;
128    private static final String TAG = "WifiConfigStore";
129    private static final boolean DBG = true;
130    private static boolean VDBG = false;
131
132    private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
133
134    /* configured networks with network id as the key */
135    private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
136            new HashMap<Integer, WifiConfiguration>();
137
138    /* A network id is a unique identifier for a network configured in the
139     * supplicant. Network ids are generated when the supplicant reads
140     * the configuration file at start and can thus change for networks.
141     * We store the IP configuration for networks along with a unique id
142     * that is generated from SSID and security type of the network. A mapping
143     * from the generated unique id to network id of the network is needed to
144     * map supplicant config to IP configuration. */
145    private HashMap<Integer, Integer> mNetworkIds =
146            new HashMap<Integer, Integer>();
147
148    /* Tracks the highest priority of configured networks */
149    private int mLastPriority = -1;
150
151    private static final String ipConfigFile = Environment.getDataDirectory() +
152            "/misc/wifi/ipconfig.txt";
153
154    private static final String networkHistoryConfigFile = Environment.getDataDirectory() +
155            "/misc/wifi/networkHistory.txt";
156
157    /* Network History Keys */
158    private static final String SSID_KEY = "SSID:  ";
159    private static final String CONFIG_KEY = "CONFIG:  ";
160    private static final String CHOICE_KEY = "CHOICE:  ";
161    private static final String LINK_KEY = "LINK:  ";
162    private static final String BSSID_KEY = "BSSID:  ";
163    private static final String BSSID_KEY_END = "/BSSID:  ";
164    private static final String RSSI_KEY = "RSSI:  ";
165    private static final String FREQ_KEY = "FREQ:  ";
166    private static final String DATE_KEY = "DATE:  ";
167    private static final String MILLI_KEY = "MILLI:  ";
168    private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI:  ";
169    private static final String NETWORK_ID_KEY = "ID:  ";
170    private static final String PRIORITY_KEY = "PRIORITY:  ";
171    private static final String DEFAULT_GW_KEY = "DEFAULT_GW:  ";
172    private static final String AUTH_KEY = "AUTH:  ";
173    private static final String SEPARATOR_KEY = "\n";
174    private static final String STATUS_KEY = "AUTO_JOIN_STATUS:  ";
175    private static final String SELF_ADDED_KEY = "SELF_ADDED:  ";
176    private static final String FAILURE_KEY = "FAILURE:  ";
177    private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD:  ";
178    private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION:  ";
179    private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY:  ";
180    private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY:  ";
181    private static final String UPDATE_UID_KEY = "UPDATE_UID:  ";
182    private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS:  ";
183    private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON:  ";
184    /* Enterprise configuration keys */
185    /**
186     * In old configurations, the "private_key" field was used. However, newer
187     * configurations use the key_id field with the engine_id set to "keystore".
188     * If this field is found in the configuration, the migration code is
189     * triggered.
190     */
191    public static final String OLD_PRIVATE_KEY_NAME = "private_key";
192
193    /** This represents an empty value of an enterprise field.
194     * NULL is used at wpa_supplicant to indicate an empty value
195     */
196    static final String EMPTY_VALUE = "NULL";
197
198    /** Internal use only */
199    private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] {
200            WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY,
201            WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY,
202            WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY,
203            WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY,
204            WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
205            WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY };
206
207    private final LocalLog mLocalLog;
208    private final WpaConfigFileObserver mFileObserver;
209
210    private WifiNative mWifiNative;
211    private final KeyStore mKeyStore = KeyStore.getInstance();
212
213    /*
214     * lastSelectedConfiguration is used to remember which network was selected last by the user.
215     * The connection to this network may not be successful, as well
216     * the selection (i.e. network priority) might not be persisted.
217     * WiFi state machine is the only object that sets this variable.
218     */
219    private String lastSelectedConfiguration = null;
220
221    WifiConfigStore(Context c, WifiNative wn) {
222        mContext = c;
223        mWifiNative = wn;
224
225        if (VDBG) {
226            mLocalLog = mWifiNative.getLocalLog();
227            mFileObserver = new WpaConfigFileObserver();
228            mFileObserver.startWatching();
229        } else {
230            mLocalLog = null;
231            mFileObserver = null;
232        }
233    }
234
235    void enableVerboseLogging(int verbose) {
236        if (verbose > 0) {
237            VDBG = true;
238        } else {
239            VDBG = false;
240        }
241    }
242
243    class WpaConfigFileObserver extends FileObserver {
244
245        public WpaConfigFileObserver() {
246            super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
247        }
248
249        @Override
250        public void onEvent(int event, String path) {
251            if (event == CLOSE_WRITE) {
252                File file = new File(SUPPLICANT_CONFIG_FILE);
253                if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
254            }
255        }
256    }
257
258
259    /**
260     * Fetch the list of configured networks
261     * and enable all stored networks in supplicant.
262     */
263    void loadAndEnableAllNetworks() {
264        if (DBG) log("Loading config and enabling all networks ");
265        loadConfiguredNetworks();
266        enableAllNetworks();
267    }
268
269    /**
270     * Fetch the list of currently configured networks
271     * @return List of networks
272     */
273    List<WifiConfiguration> getConfiguredNetworks() {
274        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
275        for(WifiConfiguration config : mConfiguredNetworks.values()) {
276            if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
277                //do not enumerate and return this configuration to any one,
278                //for instance WiFi Picker.
279                //instead treat it as unknown. the configuration can still be retrieved
280                //directly by the key or networkId
281                continue;
282            }
283            networks.add(new WifiConfiguration(config));
284        }
285        return networks;
286    }
287
288    /**
289     * Fetch the list of currently configured networks that were recently seen
290     *
291     * @return List of networks
292     */
293    List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) {
294        List<WifiConfiguration> networks = null;
295
296        for (WifiConfiguration config : mConfiguredNetworks.values()) {
297            if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
298                //do not enumerate and return this configuration to any one,
299                //instead treat it as unknown. the configuration can still be retrieved
300                //directly by the key or networkId
301                continue;
302            }
303
304            // calculate the RSSI for scan results that are more recent than milli
305            config.setVisibility(milli);
306
307            if (config.visibility == null) {
308                continue;
309            }
310            if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI &&
311                    config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) {
312                continue;
313            }
314            if (networks == null)
315                networks = new ArrayList<WifiConfiguration>();
316            if (copy) {
317                networks.add(new WifiConfiguration(config));
318            } else {
319                networks.add(config);
320            }
321        }
322        return networks;
323    }
324
325    /**
326     * get the Wificonfiguration for this netId
327     *
328     * @return Wificonfiguration
329     */
330    WifiConfiguration getWifiConfiguration(int netId) {
331        if (mConfiguredNetworks == null)
332            return null;
333        return mConfiguredNetworks.get(netId);
334    }
335
336    /**
337     * get the Wificonfiguration for this key
338     *
339     * @return Wificonfiguration
340     */
341    WifiConfiguration getWifiConfiguration(String key) {
342        if (key == null)
343            return null;
344        int hash = key.hashCode();
345        if (mNetworkIds == null)
346            return null;
347        Integer n = mNetworkIds.get(hash);
348        if (n == null)
349            return null;
350        int netId = n.intValue();
351        return getWifiConfiguration(netId);
352    }
353
354    /**
355     * enable all networks and save config. This will be a no-op if the list
356     * of configured networks indicates all networks as being enabled
357     */
358    void enableAllNetworks() {
359        boolean networkEnabledStateChanged = false;
360        for(WifiConfiguration config : mConfiguredNetworks.values()) {
361            if(config != null && config.status == Status.DISABLED
362                    && (config.autoJoinStatus <= WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED)) {
363                if(mWifiNative.enableNetwork(config.networkId, false)) {
364                    networkEnabledStateChanged = true;
365                    config.status = Status.ENABLED;
366                } else {
367                    loge("Enable network failed on " + config.networkId);
368                }
369            }
370        }
371
372        if (networkEnabledStateChanged) {
373            mWifiNative.saveConfig();
374            sendConfiguredNetworksChangedBroadcast();
375        }
376    }
377
378
379    /**
380     * Selects the specified network for connection. This involves
381     * updating the priority of all the networks and enabling the given
382     * network while disabling others.
383     *
384     * Selecting a network will leave the other networks disabled and
385     * a call to enableAllNetworks() needs to be issued upon a connection
386     * or a failure event from supplicant
387     *
388     * @param netId network to select for connection
389     * @return false if the network id is invalid
390     */
391    boolean selectNetwork(int netId) {
392        if (VDBG) localLog("selectNetwork", netId);
393        if (netId == INVALID_NETWORK_ID) return false;
394
395        // Reset the priority of each network at start or if it goes too high.
396        if (mLastPriority == -1 || mLastPriority > 1000000) {
397            for(WifiConfiguration config : mConfiguredNetworks.values()) {
398                if (config.networkId != INVALID_NETWORK_ID) {
399                    config.priority = 0;
400                    addOrUpdateNetworkNative(config);
401                }
402            }
403            mLastPriority = 0;
404        }
405
406        // Set to the highest priority and save the configuration.
407        WifiConfiguration config = new WifiConfiguration();
408        config.networkId = netId;
409        config.priority = ++mLastPriority;
410
411        addOrUpdateNetworkNative(config);
412        mWifiNative.saveConfig();
413
414        /* Enable the given network while disabling all other networks */
415        enableNetworkWithoutBroadcast(netId, true);
416
417       /* Avoid saving the config & sending a broadcast to prevent settings
418        * from displaying a disabled list of networks */
419        return true;
420    }
421
422    /**
423     * Add/update the specified configuration and save config
424     *
425     * @param config WifiConfiguration to be saved
426     * @return network update result
427     */
428    NetworkUpdateResult saveNetwork(WifiConfiguration config) {
429        WifiConfiguration conf;
430
431        // A new network cannot have null SSID
432        if (config == null || (config.networkId == INVALID_NETWORK_ID &&
433                config.SSID == null)) {
434            return new NetworkUpdateResult(INVALID_NETWORK_ID);
435        }
436        if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);
437        if (VDBG) {
438            loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()
439                    + " SSID=" + config.SSID
440                    + " Uid=" + Integer.toString(config.creatorUid)
441                    + "/" + Integer.toString(config.lastUpdateUid));
442        }
443        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
444        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
445        int netId = result.getNetworkId();
446
447        if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);
448
449        /* enable a new network */
450        if (newNetwork && netId != INVALID_NETWORK_ID) {
451            if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);
452
453            mWifiNative.enableNetwork(netId, false);
454            conf = mConfiguredNetworks.get(netId);
455            if (conf != null)
456                conf.status = Status.ENABLED;
457        }
458
459        conf = mConfiguredNetworks.get(netId);
460        if (conf != null) {
461            if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
462                if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);
463
464                // reenable autojoin, since new information has been provided
465                conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
466                enableNetworkWithoutBroadcast(conf.networkId, false);
467            }
468            if (VDBG) loge("WifiConfigStore: saveNetwork got config back netId="
469                    + Integer.toString(netId)
470                    + " uid=" + Integer.toString(config.creatorUid));
471        }
472
473        mWifiNative.saveConfig();
474        sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ?
475                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
476        return result;
477    }
478
479    void updateStatus(int netId, DetailedState state) {
480        if (netId != INVALID_NETWORK_ID) {
481            WifiConfiguration config = mConfiguredNetworks.get(netId);
482            if (config == null) return;
483            switch (state) {
484                case CONNECTED:
485                    config.status = Status.CURRENT;
486                    //we successfully connected, hence remove the blacklist
487                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
488                    break;
489                case DISCONNECTED:
490                    //If network is already disabled, keep the status
491                    if (config.status == Status.CURRENT) {
492                        config.status = Status.ENABLED;
493                    }
494                    break;
495                default:
496                    //do nothing, retain the existing state
497                    break;
498            }
499        }
500    }
501
502    /**
503     * Forget the specified network and save config
504     *
505     * @param netId network to forget
506     * @return {@code true} if it succeeds, {@code false} otherwise
507     */
508    boolean forgetNetwork(int netId) {
509        if (VDBG) localLog("forgetNetwork", netId);
510
511        boolean remove = removeConfigAndSendBroadcastIfNeeded(netId);
512        if (!remove) {
513            //success but we dont want to remove the network from supplicant conf file
514            return true;
515        }
516        if (mWifiNative.removeNetwork(netId)) {
517            mWifiNative.saveConfig();
518            return true;
519        } else {
520            loge("Failed to remove network " + netId);
521            return false;
522        }
523    }
524
525    /**
526     * Add/update a network. Note that there is no saveConfig operation.
527     * This function is retained for compatibility with the public
528     * API. The more powerful saveNetwork() is used by the
529     * state machine
530     *
531     * @param config wifi configuration to add/update
532     * @return network Id
533     */
534    int addOrUpdateNetwork(WifiConfiguration config) {
535        if (VDBG) localLog("addOrUpdateNetwork id=", config.networkId);
536        //adding unconditional message to chase b/15111865
537        Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId)
538                + " uid=" + Integer.toString(config.creatorUid)
539                + "/" + Integer.toString(config.lastUpdateUid));
540        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
541        if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
542            WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId());
543            if (conf != null) {
544                sendConfiguredNetworksChangedBroadcast(conf,
545                    result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
546                            WifiManager.CHANGE_REASON_CONFIG_CHANGE);
547            }
548        }
549        return result.getNetworkId();
550    }
551
552    /**
553     * Remove a network. Note that there is no saveConfig operation.
554     * This function is retained for compatibility with the public
555     * API. The more powerful forgetNetwork() is used by the
556     * state machine for network removal
557     *
558     * @param netId network to be removed
559     * @return {@code true} if it succeeds, {@code false} otherwise
560     */
561    boolean removeNetwork(int netId) {
562        if (VDBG) localLog("removeNetwork", netId);
563        boolean ret = mWifiNative.removeNetwork(netId);
564        if (ret) {
565            removeConfigAndSendBroadcastIfNeeded(netId);
566        }
567        return ret;
568    }
569
570    private boolean removeConfigAndSendBroadcastIfNeeded(int netId) {
571        boolean remove = true;
572        WifiConfiguration config = mConfiguredNetworks.get(netId);
573        if (config != null) {
574            if (VDBG) {
575                loge("removeNetwork " + Integer.toString(netId) + " key=" +
576                        config.configKey() + " config.id=" + Integer.toString(config.networkId));
577            }
578
579            // cancel the last user choice
580            if (config.configKey().equals(lastSelectedConfiguration)) {
581                lastSelectedConfiguration = null;
582            }
583
584            // Remove any associated keys
585            if (config.enterpriseConfig != null) {
586                removeKeys(config.enterpriseConfig);
587            }
588
589            if (config.didSelfAdd) {
590                if (config.peerWifiConfiguration != null) {
591                    for (WifiConfiguration peer : mConfiguredNetworks.values()) {
592                        if (config.peerWifiConfiguration.equals(peer.configKey())) {
593                            /* the configuration that trigger the add is still there */
594                            remove = false;
595                        }
596                    }
597                }
598            }
599
600            if (remove) {
601                mConfiguredNetworks.remove(netId);
602                mNetworkIds.remove(configKey(config));
603            } else {
604                /* we can't directly remove the configuration since we added it ourselves, because
605                 * that could cause the system to re-add it right away.
606                 * Instead black list it. It will be unblacklisted only thru a new add.
607                 */
608                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DELETED);
609                mWifiNative.disableNetwork(config.networkId);
610                remove = false;
611            }
612
613            writeIpAndProxyConfigurations();
614            sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
615            writeKnownNetworkHistory();
616        }
617        return remove;
618    }
619
620    /**
621     * Enable a network. Note that there is no saveConfig operation.
622     * This function is retained for compatibility with the public
623     * API. The more powerful selectNetwork()/saveNetwork() is used by the
624     * state machine for connecting to a network
625     *
626     * @param netId network to be enabled
627     * @return {@code true} if it succeeds, {@code false} otherwise
628     */
629    boolean enableNetwork(int netId, boolean disableOthers) {
630        boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
631        if (disableOthers) {
632            if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId);
633            sendConfiguredNetworksChangedBroadcast();
634        } else {
635            if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId);
636            WifiConfiguration enabledNetwork = null;
637            synchronized(mConfiguredNetworks) {
638                enabledNetwork = mConfiguredNetworks.get(netId);
639            }
640            // check just in case the network was removed by someone else.
641            if (enabledNetwork != null) {
642                sendConfiguredNetworksChangedBroadcast(enabledNetwork,
643                        WifiManager.CHANGE_REASON_CONFIG_CHANGE);
644            }
645        }
646        return ret;
647    }
648
649    boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
650        boolean ret = mWifiNative.enableNetwork(netId, disableOthers);
651
652        WifiConfiguration config = mConfiguredNetworks.get(netId);
653        if (config != null) config.status = Status.ENABLED;
654
655        if (disableOthers) {
656            markAllNetworksDisabledExcept(netId);
657        }
658        return ret;
659    }
660
661    void disableAllNetworks() {
662        if (VDBG) localLog("disableAllNetworks");
663        boolean networkDisabled = false;
664        for(WifiConfiguration config : mConfiguredNetworks.values()) {
665            if(config != null && config.status != Status.DISABLED) {
666                if(mWifiNative.disableNetwork(config.networkId)) {
667                    networkDisabled = true;
668                    config.status = Status.DISABLED;
669                } else {
670                    loge("Disable network failed on " + config.networkId);
671                }
672            }
673        }
674
675        if (networkDisabled) {
676            sendConfiguredNetworksChangedBroadcast();
677        }
678    }
679    /**
680     * Disable a network. Note that there is no saveConfig operation.
681     * @param netId network to be disabled
682     * @return {@code true} if it succeeds, {@code false} otherwise
683     */
684    boolean disableNetwork(int netId) {
685        return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
686    }
687
688    /**
689     * Disable a network. Note that there is no saveConfig operation.
690     * @param netId network to be disabled
691     * @param reason reason code network was disabled
692     * @return {@code true} if it succeeds, {@code false} otherwise
693     */
694    boolean disableNetwork(int netId, int reason) {
695        if (VDBG) localLog("disableNetwork", netId);
696        boolean ret = mWifiNative.disableNetwork(netId);
697        WifiConfiguration network = null;
698        WifiConfiguration config = mConfiguredNetworks.get(netId);
699
700        if (VDBG) {
701            if (config != null) {
702                loge("disableNetwork netId=" + Integer.toString(netId)
703                        + " SSID=" + config.SSID
704                        + " disabled=" + (config.status == Status.DISABLED)
705                        + " reason=" + Integer.toString(config.disableReason));
706            }
707        }
708        /* Only change the reason if the network was not previously disabled */
709        if (config != null && config.status != Status.DISABLED) {
710            config.status = Status.DISABLED;
711            config.disableReason = reason;
712            network = config;
713        }
714        if (network != null) {
715            sendConfiguredNetworksChangedBroadcast(network,
716                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
717        }
718        return ret;
719    }
720
721    /**
722     * Save the configured networks in supplicant to disk
723     * @return {@code true} if it succeeds, {@code false} otherwise
724     */
725    boolean saveConfig() {
726        return mWifiNative.saveConfig();
727    }
728
729    /**
730     * Start WPS pin method configuration with pin obtained
731     * from the access point
732     * @param config WPS configuration
733     * @return Wps result containing status and pin
734     */
735    WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
736        WpsResult result = new WpsResult();
737        if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
738            /* WPS leaves all networks disabled */
739            markAllNetworksDisabled();
740            result.status = WpsResult.Status.SUCCESS;
741        } else {
742            loge("Failed to start WPS pin method configuration");
743            result.status = WpsResult.Status.FAILURE;
744        }
745        return result;
746    }
747
748    /**
749     * Start WPS pin method configuration with pin obtained
750     * from the device
751     * @return WpsResult indicating status and pin
752     */
753    WpsResult startWpsWithPinFromDevice(WpsInfo config) {
754        WpsResult result = new WpsResult();
755        result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
756        /* WPS leaves all networks disabled */
757        if (!TextUtils.isEmpty(result.pin)) {
758            markAllNetworksDisabled();
759            result.status = WpsResult.Status.SUCCESS;
760        } else {
761            loge("Failed to start WPS pin method configuration");
762            result.status = WpsResult.Status.FAILURE;
763        }
764        return result;
765    }
766
767    /**
768     * Start WPS push button configuration
769     * @param config WPS configuration
770     * @return WpsResult indicating status and pin
771     */
772    WpsResult startWpsPbc(WpsInfo config) {
773        WpsResult result = new WpsResult();
774        if (mWifiNative.startWpsPbc(config.BSSID)) {
775            /* WPS leaves all networks disabled */
776            markAllNetworksDisabled();
777            result.status = WpsResult.Status.SUCCESS;
778        } else {
779            loge("Failed to start WPS push button configuration");
780            result.status = WpsResult.Status.FAILURE;
781        }
782        return result;
783    }
784
785    /**
786     * Fetch the link properties for a given network id
787     *
788     * @return LinkProperties for the given network id
789     */
790    LinkProperties getLinkProperties(int netId) {
791        WifiConfiguration config = mConfiguredNetworks.get(netId);
792        if (config != null) return new LinkProperties(config.getLinkProperties());
793        return null;
794    }
795
796    /**
797     * set IP configuration for a given network id
798     */
799    void setLinkProperties(int netId, LinkProperties linkProperties) {
800        WifiConfiguration config = mConfiguredNetworks.get(netId);
801        if (config != null) {
802            // add old proxy details - TODO - is this still needed?
803            if(config.getLinkProperties() != null) {
804                linkProperties.setHttpProxy(config.getLinkProperties().getHttpProxy());
805            }
806            config.setLinkProperties(linkProperties);
807        }
808    }
809
810    /**
811     * set default GW MAC address
812     */
813    void setDefaultGwMacAddress(int netId, String macAddress) {
814        WifiConfiguration config = mConfiguredNetworks.get(netId);
815        if (config != null) {
816            //update defaultGwMacAddress
817            config.defaultGwMacAddress = macAddress;
818        }
819    }
820
821
822    /**
823     * clear IP configuration for a given network id
824     * @param network id
825     */
826    void clearLinkProperties(int netId) {
827        WifiConfiguration config = mConfiguredNetworks.get(netId);
828        if (config != null && config.getLinkProperties() != null) {
829            // Clear everything except proxy
830            ProxyInfo proxy = config.getLinkProperties().getHttpProxy();
831            config.getLinkProperties().clear();
832            config.getLinkProperties().setHttpProxy(proxy);
833        }
834    }
835
836
837    /**
838     * Fetch the proxy properties for a given network id
839     * @param network id
840     * @return ProxyInfo for the network id
841     */
842    ProxyInfo getProxyProperties(int netId) {
843        LinkProperties linkProperties = getLinkProperties(netId);
844        if (linkProperties != null) {
845            return new ProxyInfo(linkProperties.getHttpProxy());
846        }
847        return null;
848    }
849
850    /**
851     * Return if the specified network is using static IP
852     * @param network id
853     * @return {@code true} if using static ip for netId
854     */
855    boolean isUsingStaticIp(int netId) {
856        WifiConfiguration config = mConfiguredNetworks.get(netId);
857        if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {
858            return true;
859        }
860        return false;
861    }
862
863    /**
864     * Should be called when a single network configuration is made.
865     * @param network The network configuration that changed.
866     * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
867     * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
868     */
869    private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
870            int reason) {
871        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
872        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
873        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
874        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
875        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
876        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
877    }
878
879    /**
880     * Should be called when multiple network configuration changes are made.
881     */
882    private void sendConfiguredNetworksChangedBroadcast() {
883        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
884        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
885        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
886        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
887    }
888
889    void loadConfiguredNetworks() {
890        String listStr = mWifiNative.listNetworks();
891        mLastPriority = 0;
892
893        mConfiguredNetworks.clear();
894        mNetworkIds.clear();
895
896        if (listStr == null)
897            return;
898
899        String[] lines = listStr.split("\n");
900
901        if (VDBG) {
902            loge("loadConfiguredNetworks: found " + Integer.toString(lines.length)
903                    + " networks", true);
904        }
905
906        // Skip the first line, which is a header
907        for (int i = 1; i < lines.length; i++) {
908            String[] result = lines[i].split("\t");
909            // network-id | ssid | bssid | flags
910            WifiConfiguration config = new WifiConfiguration();
911            try {
912                config.networkId = Integer.parseInt(result[0]);
913            } catch(NumberFormatException e) {
914                loge("Failed to read network-id '" + result[0] + "'");
915                continue;
916            }
917            if (result.length > 3) {
918                if (result[3].indexOf("[CURRENT]") != -1)
919                    config.status = WifiConfiguration.Status.CURRENT;
920                else if (result[3].indexOf("[DISABLED]") != -1)
921                    config.status = WifiConfiguration.Status.DISABLED;
922                else
923                    config.status = WifiConfiguration.Status.ENABLED;
924            } else {
925                config.status = WifiConfiguration.Status.ENABLED;
926            }
927            readNetworkVariables(config);
928            if (config.priority > mLastPriority) {
929                mLastPriority = config.priority;
930            }
931
932            config.setIpAssignment(IpAssignment.DHCP);
933            config.setProxySettings(ProxySettings.NONE);
934
935            if (mNetworkIds.containsKey(configKey(config))) {
936                // That SSID is already known, just ignore this duplicate entry
937                if (VDBG) localLog("discarded duplicate network ", config.networkId);
938            } else if(config.isValid()){
939                mConfiguredNetworks.put(config.networkId, config);
940                mNetworkIds.put(configKey(config), config.networkId);
941                if (VDBG) localLog("loaded configured network", config.networkId);
942            } else {
943                if (DBG) log("Ignoring loaded configured for network " + config.networkId
944                    + " because config are not valid");
945            }
946        }
947
948        readIpAndProxyConfigurations();
949        readNetworkHistory();
950
951        sendConfiguredNetworksChangedBroadcast();
952
953        if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks");
954
955        if (mNetworkIds.size() == 0) {
956            // no networks? Lets log if the wpa_supplicant.conf file contents
957            BufferedReader reader = null;
958            try {
959                reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
960                if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---");
961                for (String line = reader.readLine(); line != null; line = reader.readLine()) {
962                    if (VDBG) localLog(line);
963                }
964                if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---");
965            } catch (FileNotFoundException e) {
966                if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
967            } catch (IOException e) {
968                if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
969            } finally {
970                try {
971                    if (reader != null) {
972                        reader.close();
973                    }
974                } catch (IOException e) {
975                    // Just ignore the fact that we couldn't close
976                }
977            }
978        }
979    }
980
981    private String readNetworkVariableFromSupplicantFile(String ssid, String key) {
982        BufferedReader reader = null;
983        if (VDBG) loge("readNetworkVariableFromSupplicantFile ssid=[" + ssid + "] key=" + key);
984
985        String value = null;
986        try {
987            reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
988            boolean found = false;
989            boolean networkMatched = false;
990            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
991                if (VDBG) loge(line);
992
993                if (line.matches("[ \\t]*network=\\{")) {
994                    found = true;
995                    networkMatched = false;
996                } else if (line.matches("[ \\t]*\\{")) {
997                    found = false;
998                    networkMatched = false;
999                }
1000
1001                if (found) {
1002                    int index;
1003                    if ((index = line.indexOf("ssid=")) >= 0) {
1004                        String networkSSid = line.substring(index + 5);
1005                        if (networkSSid.regionMatches(0, ssid, 0, ssid.length())) {
1006                            networkMatched = true;
1007                        } else {
1008                            networkMatched = false;
1009                        }
1010                    }
1011
1012                    if (networkMatched) {
1013                        if ((index = line.indexOf(key + "=")) >= 0) {
1014
1015                            value = line.substring(index + key.length() + 1);
1016                            if (VDBG) loge("found key " + value);
1017
1018                        }
1019                    }
1020                }
1021            }
1022        } catch (FileNotFoundException e) {
1023            if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
1024        } catch (IOException e) {
1025            if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
1026        } finally {
1027            try {
1028                if (reader != null) {
1029                    reader.close();
1030                }
1031            } catch (IOException e) {
1032                // Just ignore the fact that we couldn't close
1033            }
1034        }
1035
1036        return value;
1037    }
1038
1039    /* Mark all networks except specified netId as disabled */
1040    private void markAllNetworksDisabledExcept(int netId) {
1041        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1042            if(config != null && config.networkId != netId) {
1043                if (config.status != Status.DISABLED) {
1044                    config.status = Status.DISABLED;
1045                    config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
1046                }
1047            }
1048        }
1049    }
1050
1051    private void markAllNetworksDisabled() {
1052        markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
1053    }
1054
1055    boolean needsUnlockedKeyStore() {
1056
1057        // Any network using certificates to authenticate access requires
1058        // unlocked key store; unless the certificates can be stored with
1059        // hardware encryption
1060
1061        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1062
1063            if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
1064                    && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1065
1066                if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
1067                    return true;
1068                }
1069            }
1070        }
1071
1072        return false;
1073    }
1074
1075    public void writeKnownNetworkHistory() {
1076        if (VDBG) {
1077            loge(" writeKnownNetworkHistory() num networks:" +
1078                    Integer.toString(mConfiguredNetworks.size()), true);
1079        }
1080
1081        /* Make a copy */
1082        final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
1083        for (WifiConfiguration config : mConfiguredNetworks.values()) {
1084            networks.add(new WifiConfiguration(config));
1085        }
1086
1087        mWriter.write(networkHistoryConfigFile, new DelayedDiskWrite.Writer() {
1088            public void onWriteCalled(DataOutputStream out) throws IOException {
1089                for (WifiConfiguration config : networks) {
1090                    //loge("onWriteCalled write SSID: " + config.SSID);
1091                   /* if (config.getLinkProperties() != null)
1092                        loge(" lp " + config.getLinkProperties().toString());
1093                    else
1094                        loge("attempt config w/o lp");
1095                    */
1096
1097                    if (VDBG) {
1098                        int num = 0;
1099                        if (config.connectChoices != null) {
1100                            num = config.connectChoices.size();
1101                        }
1102                        loge("saving network history: " + config.configKey()  + " gw: " +
1103                                config.defaultGwMacAddress + " autojoin status: " +
1104                                config.autoJoinStatus + " ephemeral=" + config.ephemeral
1105                                + " choices:" + Integer.toString(num));
1106                    }
1107                    if (config.ephemeral == true)
1108                        continue;
1109
1110                    if (config.isValid() == false)
1111                        continue;
1112
1113                    if (config.SSID == null) {
1114                        if (VDBG) {
1115                            loge("writeKnownNetworkHistory trying to write config with null SSID");
1116                        }
1117                        continue;
1118                    }
1119
1120                    out.writeUTF(CONFIG_KEY + config.configKey() + SEPARATOR_KEY);
1121
1122                    out.writeUTF(SSID_KEY + config.SSID + SEPARATOR_KEY);
1123
1124                    out.writeUTF(PRIORITY_KEY + Integer.toString(config.priority) + SEPARATOR_KEY);
1125                    out.writeUTF(STATUS_KEY + Integer.toString(config.autoJoinStatus)
1126                            + SEPARATOR_KEY);
1127                    out.writeUTF(SUPPLICANT_STATUS_KEY + Integer.toString(config.status)
1128                            + SEPARATOR_KEY);
1129                    out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY
1130                            + Integer.toString(config.disableReason)
1131                            + SEPARATOR_KEY);
1132                    out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId)
1133                            + SEPARATOR_KEY);
1134                    out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded)
1135                            + SEPARATOR_KEY);
1136                    out.writeUTF(DID_SELF_ADD_KEY + Boolean.toString(config.didSelfAdd)
1137                            + SEPARATOR_KEY);
1138                    if (config.peerWifiConfiguration != null) {
1139                        out.writeUTF(PEER_CONFIGURATION_KEY + config.peerWifiConfiguration
1140                                + SEPARATOR_KEY);
1141                    }
1142                    out.writeUTF(BLACKLIST_MILLI_KEY + Long.toString(config.blackListTimestamp)
1143                            + SEPARATOR_KEY);
1144                    out.writeUTF(CREATOR_UID_KEY + Integer.toString(config.creatorUid)
1145                            + SEPARATOR_KEY);
1146                    out.writeUTF(CONNECT_UID_KEY + Integer.toString(config.lastConnectUid)
1147                            + SEPARATOR_KEY);
1148                    out.writeUTF(UPDATE_UID_KEY + Integer.toString(config.lastUpdateUid)
1149                            + SEPARATOR_KEY);
1150                    String allowedKeyManagementString =
1151                            makeString(config.allowedKeyManagement,
1152                                    WifiConfiguration.KeyMgmt.strings);
1153                    out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY);
1154
1155                    if (config.connectChoices != null) {
1156                        for (String key : config.connectChoices.keySet()) {
1157                            out.writeUTF(CHOICE_KEY + key + SEPARATOR_KEY);
1158                        }
1159                    }
1160                    if (config.linkedConfigurations != null) {
1161                        for (String key : config.linkedConfigurations.keySet()) {
1162                            out.writeUTF(LINK_KEY + key + SEPARATOR_KEY);
1163                        }
1164                    }
1165
1166                    if (config.getLinkProperties() != null) {
1167                        String macAddress = config.defaultGwMacAddress;
1168                        if (macAddress != null) {
1169                            out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY);
1170                        }
1171                    }
1172
1173                    if (config.scanResultCache != null) {
1174                        for (ScanResult result : config.scanResultCache.values()) {
1175                            out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY);
1176
1177                            out.writeUTF(FREQ_KEY + Integer.toString(result.frequency)
1178                                    + SEPARATOR_KEY);
1179
1180                            out.writeUTF(RSSI_KEY + Integer.toString(result.level)
1181                                    + SEPARATOR_KEY);
1182
1183                            if (result.seen != 0) {
1184                                out.writeUTF(MILLI_KEY + Long.toString(result.seen)
1185                                        + SEPARATOR_KEY);
1186                            }
1187                            out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY);
1188                        }
1189                    }
1190                    if (config.lastFailure != null) {
1191                        out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY);
1192                    }
1193                    out.writeUTF(SEPARATOR_KEY);
1194                }
1195            }
1196
1197        });
1198    }
1199
1200    public void setLastSelectedConfiguration(int netId) {
1201        if (DBG) {
1202            loge("setLastSelectedConfiguration " + Integer.toString(netId));
1203        }
1204        if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
1205            lastSelectedConfiguration = null;
1206        } else {
1207            WifiConfiguration selected = getWifiConfiguration(netId);
1208            if (selected == null) {
1209                lastSelectedConfiguration = null;
1210            } else {
1211                lastSelectedConfiguration = selected.configKey();
1212                if (VDBG) {
1213                    loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration);
1214                }
1215            }
1216        }
1217    }
1218
1219    public String getLastSelectedConfiguration() {
1220        return lastSelectedConfiguration;
1221    }
1222
1223    private void readNetworkHistory() {
1224        if (VDBG) {
1225            loge("readNetworkHistory path:" + networkHistoryConfigFile, true);
1226        }
1227        DataInputStream in = null;
1228        try {
1229            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
1230                    networkHistoryConfigFile)));
1231            WifiConfiguration config = null;
1232            while (true) {
1233                int id = -1;
1234                String key = in.readUTF();
1235                String bssid = null;
1236                String ssid = null;
1237
1238                int freq = 0;
1239                long seen = 0;
1240                int rssi = WifiConfiguration.INVALID_RSSI;
1241                String caps = null;
1242                if (key.startsWith(CONFIG_KEY)) {
1243
1244                    if (config != null) {
1245                        config = null;
1246                    }
1247                    String configKey = key.replace(CONFIG_KEY, "");
1248                    configKey = configKey.replace(SEPARATOR_KEY, "");
1249                    // get the networkId for that config Key
1250                    Integer n = mNetworkIds.get(configKey.hashCode());
1251                    // skip reading that configuration data
1252                    // since we don't have a corresponding network ID
1253                    if (n == null) {
1254                        loge("readNetworkHistory didnt find netid for hash="
1255                                + Integer.toString(configKey.hashCode())
1256                                + " key: " + configKey);
1257                        continue;
1258                    }
1259                    config = mConfiguredNetworks.get(n);
1260                    if (config == null) {
1261                        loge("readNetworkHistory didnt find config for netid="
1262                                + n.toString()
1263                                + " key: " + configKey);
1264                    }
1265                    ssid = null;
1266                    bssid = null;
1267                    freq = 0;
1268                    seen = 0;
1269                    rssi = WifiConfiguration.INVALID_RSSI;
1270                    caps = null;
1271
1272                } else if (config != null) {
1273                    if (key.startsWith(SSID_KEY)) {
1274                        ssid = key.replace(SSID_KEY, "");
1275                        ssid = ssid.replace(SEPARATOR_KEY, "");
1276                        if (config.SSID != null && !config.SSID.equals(ssid)) {
1277                            loge("Error parsing network history file, mismatched SSIDs");
1278                            config = null; //error
1279                            ssid = null;
1280                        } else {
1281                            config.SSID = ssid;
1282                        }
1283                    }
1284
1285                    if (key.startsWith(DEFAULT_GW_KEY)) {
1286                        String gateway = key.replace(DEFAULT_GW_KEY, "");
1287                        gateway = gateway.replace(SEPARATOR_KEY, "");
1288                        config.defaultGwMacAddress = gateway;
1289                    }
1290
1291                    if (key.startsWith(STATUS_KEY)) {
1292                        String status = key.replace(STATUS_KEY, "");
1293                        status = status.replace(SEPARATOR_KEY, "");
1294                        config.autoJoinStatus = Integer.parseInt(status);
1295                    }
1296
1297                    /*
1298                    if (key.startsWith(SUPPLICANT_STATUS_KEY)) {
1299                        String status = key.replace(SUPPLICANT_STATUS_KEY, "");
1300                        status = status.replace(SEPARATOR_KEY, "");
1301                        config.status = Integer.parseInt(status);
1302                    }
1303
1304                    if (key.startsWith(SUPPLICANT_DISABLE_REASON_KEY)) {
1305                        String reason = key.replace(SUPPLICANT_DISABLE_REASON_KEY, "");
1306                        reason = reason.replace(SEPARATOR_KEY, "");
1307                        config.disableReason = Integer.parseInt(reason);
1308                    }*/
1309
1310                    if (key.startsWith(SELF_ADDED_KEY)) {
1311                        String selfAdded = key.replace(SELF_ADDED_KEY, "");
1312                        selfAdded = selfAdded.replace(SEPARATOR_KEY, "");
1313                        config.selfAdded = Boolean.parseBoolean(selfAdded);
1314                    }
1315
1316                    if (key.startsWith(DID_SELF_ADD_KEY)) {
1317                        String didSelfAdd = key.replace(DID_SELF_ADD_KEY, "");
1318                        didSelfAdd = didSelfAdd.replace(SEPARATOR_KEY, "");
1319                        config.didSelfAdd = Boolean.parseBoolean(didSelfAdd);
1320                    }
1321
1322                    if (key.startsWith(CREATOR_UID_KEY)) {
1323                        String uid = key.replace(CREATOR_UID_KEY, "");
1324                        uid = uid.replace(SEPARATOR_KEY, "");
1325                        config.creatorUid = Integer.parseInt(uid);
1326                    }
1327
1328                    if (key.startsWith(BLACKLIST_MILLI_KEY)) {
1329                        String milli = key.replace(BLACKLIST_MILLI_KEY, "");
1330                        milli = milli.replace(SEPARATOR_KEY, "");
1331                        config.blackListTimestamp = Long.parseLong(milli);
1332                    }
1333
1334                    if (key.startsWith(CONNECT_UID_KEY)) {
1335                        String uid = key.replace(CONNECT_UID_KEY, "");
1336                        uid = uid.replace(SEPARATOR_KEY, "");
1337                        config.lastConnectUid = Integer.parseInt(uid);
1338                    }
1339
1340                    if (key.startsWith(UPDATE_UID_KEY)) {
1341                        String uid = key.replace(UPDATE_UID_KEY, "");
1342                        uid = uid.replace(SEPARATOR_KEY, "");
1343                        config.lastUpdateUid = Integer.parseInt(uid);
1344                    }
1345
1346                    if (key.startsWith(FAILURE_KEY)) {
1347                        config.lastFailure = key.replace(FAILURE_KEY, "");
1348                        config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, "");
1349                    }
1350
1351                    if (key.startsWith(PEER_CONFIGURATION_KEY)) {
1352                        config.peerWifiConfiguration = key.replace(PEER_CONFIGURATION_KEY, "");
1353                        config.peerWifiConfiguration =
1354                                config.peerWifiConfiguration.replace(SEPARATOR_KEY, "");
1355                    }
1356
1357                    if (key.startsWith(CHOICE_KEY)) {
1358                        String configKey = key.replace(CHOICE_KEY, "");
1359                        configKey = configKey.replace(SEPARATOR_KEY, "");
1360                        if (config.connectChoices == null) {
1361                            config.connectChoices = new HashMap<String, Integer>();
1362                        }
1363                        config.connectChoices.put(configKey, -1);
1364                    }
1365
1366                    if (key.startsWith(LINK_KEY)) {
1367                        String configKey = key.replace(LINK_KEY, "");
1368                        configKey = configKey.replace(SEPARATOR_KEY, "");
1369                        if (config.linkedConfigurations == null) {
1370                            config.linkedConfigurations = new HashMap<String, Integer>();
1371                        }
1372                        if (config.linkedConfigurations != null) {
1373                            config.linkedConfigurations.put(configKey, -1);
1374                        }
1375                    }
1376
1377                    if (key.startsWith(BSSID_KEY)) {
1378                        if (key.startsWith(BSSID_KEY)) {
1379                            bssid = key.replace(BSSID_KEY, "");
1380                            bssid = bssid.replace(SEPARATOR_KEY, "");
1381                            freq = 0;
1382                            seen = 0;
1383                            rssi = WifiConfiguration.INVALID_RSSI;
1384                            caps = "";
1385                        }
1386
1387                        if (key.startsWith(RSSI_KEY)) {
1388                            String lvl = key.replace(RSSI_KEY, "");
1389                            lvl = lvl.replace(SEPARATOR_KEY, "");
1390                            rssi = Integer.parseInt(lvl);
1391                        }
1392
1393                        if (key.startsWith(FREQ_KEY)) {
1394                            String channel = key.replace(FREQ_KEY, "");
1395                            channel = channel.replace(SEPARATOR_KEY, "");
1396                            freq = Integer.parseInt(channel);
1397                        }
1398
1399                        if (key.startsWith(DATE_KEY)) {
1400                        /*
1401                         * when reading the configuration from file we don't update the date
1402                         * so as to avoid reading back stale or non-sensical data that would
1403                         * depend on network time.
1404                         * The date of a WifiConfiguration should only come from actual scan result.
1405                         *
1406                        String s = key.replace(FREQ_KEY, "");
1407                        seen = Integer.getInteger(s);
1408                        */
1409                        }
1410
1411                        if (key.startsWith(BSSID_KEY_END)) {
1412
1413                            if ((bssid != null) && (ssid != null)) {
1414
1415                                if (config.scanResultCache == null) {
1416                                    config.scanResultCache = new HashMap<String, ScanResult>();
1417                                }
1418                                WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
1419                                ScanResult result = new ScanResult(wssid, bssid,
1420                                        caps, rssi, freq, (long) 0);
1421                                result.seen = seen;
1422                                config.scanResultCache.put(bssid, result);
1423                            }
1424                        }
1425                    }
1426                }
1427            }
1428        } catch (EOFException ignore) {
1429            if (in != null) {
1430                try {
1431                    in.close();
1432                } catch (Exception e) {
1433                    loge("readNetworkHistory: Error reading file" + e);
1434                }
1435            }
1436        } catch (IOException e) {
1437            loge("readNetworkHistory: Error parsing configuration" + e);
1438        }
1439
1440        if(in!=null) {
1441            try {
1442                in.close();
1443            } catch (Exception e) {
1444                loge("readNetworkHistory: Error closing file" + e);
1445            }
1446        }
1447    }
1448
1449    private void writeIpAndProxyConfigurations() {
1450        final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
1451        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1452            if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) {
1453                networks.put(configKey(config), config.getIpConfiguration());
1454            }
1455        }
1456
1457        super.writeIpAndProxyConfigurations(ipConfigFile, networks);
1458    }
1459
1460    private void readIpAndProxyConfigurations() {
1461        SparseArray<IpConfiguration> networks = super.readIpAndProxyConfigurations(ipConfigFile);
1462
1463        if (networks.size() == 0) {
1464            // IpConfigStore.readIpAndProxyConfigurations has already logged an error.
1465            return;
1466        }
1467
1468        for (int i = 0; i < networks.size(); i++) {
1469            Integer id = (Integer) i;
1470            WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id));
1471
1472
1473            if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
1474                loge("configuration found for missing network, nid="+Integer.toString(id)
1475                        +", ignored, networks.size=" + Integer.toString(networks.size()));
1476            } else {
1477                config.setIpConfiguration(networks.valueAt(i));
1478            }
1479        }
1480    }
1481
1482    /*
1483     * Convert string to Hexadecimal before passing to wifi native layer
1484     * In native function "doCommand()" have trouble in converting Unicode character string to UTF8
1485     * conversion to hex is required because SSIDs can have space characters in them;
1486     * and that can confuses the supplicant because it uses space charaters as delimiters
1487     */
1488
1489    private String encodeSSID(String str){
1490        String tmp = removeDoubleQuotes(str);
1491        return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8"))));
1492    }
1493
1494    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
1495        /*
1496         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1497         * network configuration. Otherwise, the networkId should
1498         * refer to an existing configuration.
1499         */
1500
1501        if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
1502
1503        int netId = config.networkId;
1504        boolean newNetwork = false;
1505        // networkId of INVALID_NETWORK_ID means we want to create a new network
1506        if (netId == INVALID_NETWORK_ID) {
1507            Integer savedNetId = mNetworkIds.get(configKey(config));
1508            //paranoia: check if either we have a network Id or a WifiConfiguration
1509            //matching the one we are trying to add.
1510            if (savedNetId == null) {
1511                for (WifiConfiguration test : mConfiguredNetworks.values()) {
1512                    if (test.configKey().equals(config.configKey())) {
1513                        savedNetId = test.networkId;
1514                        loge("addOrUpdateNetworkNative " + config.configKey()
1515                                + " was found, but no network Id");
1516                        break;
1517                    }
1518                }
1519            }
1520            if (savedNetId != null) {
1521                netId = savedNetId;
1522            } else {
1523                newNetwork = true;
1524                netId = mWifiNative.addNetwork();
1525                if (netId < 0) {
1526                    loge("Failed to add a network!");
1527                    return new NetworkUpdateResult(INVALID_NETWORK_ID);
1528                } else {
1529                    loge("addOrUpdateNetworkNative created netId=" + netId);
1530                }
1531            }
1532        }
1533
1534        boolean updateFailed = true;
1535
1536        setVariables: {
1537
1538            if (config.SSID != null &&
1539                    !mWifiNative.setNetworkVariable(
1540                        netId,
1541                        WifiConfiguration.ssidVarName,
1542                        encodeSSID(config.SSID))) {
1543                loge("failed to set SSID: "+config.SSID);
1544                break setVariables;
1545            }
1546
1547            if (config.BSSID != null) {
1548                loge("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
1549                if (!mWifiNative.setNetworkVariable(
1550                        netId,
1551                        WifiConfiguration.bssidVarName,
1552                        config.BSSID)) {
1553                    loge("failed to set BSSID: " + config.BSSID);
1554                    break setVariables;
1555                }
1556            }
1557
1558            String allowedKeyManagementString =
1559                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
1560            if (config.allowedKeyManagement.cardinality() != 0 &&
1561                    !mWifiNative.setNetworkVariable(
1562                        netId,
1563                        WifiConfiguration.KeyMgmt.varName,
1564                        allowedKeyManagementString)) {
1565                loge("failed to set key_mgmt: "+
1566                        allowedKeyManagementString);
1567                break setVariables;
1568            }
1569
1570            String allowedProtocolsString =
1571                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
1572            if (config.allowedProtocols.cardinality() != 0 &&
1573                    !mWifiNative.setNetworkVariable(
1574                        netId,
1575                        WifiConfiguration.Protocol.varName,
1576                        allowedProtocolsString)) {
1577                loge("failed to set proto: "+
1578                        allowedProtocolsString);
1579                break setVariables;
1580            }
1581
1582            String allowedAuthAlgorithmsString =
1583                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
1584            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
1585                    !mWifiNative.setNetworkVariable(
1586                        netId,
1587                        WifiConfiguration.AuthAlgorithm.varName,
1588                        allowedAuthAlgorithmsString)) {
1589                loge("failed to set auth_alg: "+
1590                        allowedAuthAlgorithmsString);
1591                break setVariables;
1592            }
1593
1594            String allowedPairwiseCiphersString =
1595                    makeString(config.allowedPairwiseCiphers,
1596                    WifiConfiguration.PairwiseCipher.strings);
1597            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
1598                    !mWifiNative.setNetworkVariable(
1599                        netId,
1600                        WifiConfiguration.PairwiseCipher.varName,
1601                        allowedPairwiseCiphersString)) {
1602                loge("failed to set pairwise: "+
1603                        allowedPairwiseCiphersString);
1604                break setVariables;
1605            }
1606
1607            String allowedGroupCiphersString =
1608                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
1609            if (config.allowedGroupCiphers.cardinality() != 0 &&
1610                    !mWifiNative.setNetworkVariable(
1611                        netId,
1612                        WifiConfiguration.GroupCipher.varName,
1613                        allowedGroupCiphersString)) {
1614                loge("failed to set group: "+
1615                        allowedGroupCiphersString);
1616                break setVariables;
1617            }
1618
1619            // Prevent client screw-up by passing in a WifiConfiguration we gave it
1620            // by preventing "*" as a key.
1621            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
1622                    !mWifiNative.setNetworkVariable(
1623                        netId,
1624                        WifiConfiguration.pskVarName,
1625                        config.preSharedKey)) {
1626                loge("failed to set psk");
1627                break setVariables;
1628            }
1629
1630            boolean hasSetKey = false;
1631            if (config.wepKeys != null) {
1632                for (int i = 0; i < config.wepKeys.length; i++) {
1633                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
1634                    // by preventing "*" as a key.
1635                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
1636                        if (!mWifiNative.setNetworkVariable(
1637                                    netId,
1638                                    WifiConfiguration.wepKeyVarNames[i],
1639                                    config.wepKeys[i])) {
1640                            loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
1641                            break setVariables;
1642                        }
1643                        hasSetKey = true;
1644                    }
1645                }
1646            }
1647
1648            if (hasSetKey) {
1649                if (!mWifiNative.setNetworkVariable(
1650                            netId,
1651                            WifiConfiguration.wepTxKeyIdxVarName,
1652                            Integer.toString(config.wepTxKeyIndex))) {
1653                    loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
1654                    break setVariables;
1655                }
1656            }
1657
1658            if (!mWifiNative.setNetworkVariable(
1659                        netId,
1660                        WifiConfiguration.priorityVarName,
1661                        Integer.toString(config.priority))) {
1662                loge(config.SSID + ": failed to set priority: "
1663                        +config.priority);
1664                break setVariables;
1665            }
1666
1667            if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
1668                        netId,
1669                        WifiConfiguration.hiddenSSIDVarName,
1670                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
1671                loge(config.SSID + ": failed to set hiddenSSID: "+
1672                        config.hiddenSSID);
1673                break setVariables;
1674            }
1675
1676            if (config.requirePMF && !mWifiNative.setNetworkVariable(
1677                        netId,
1678                        WifiConfiguration.pmfVarName,
1679                        "2")) {
1680                loge(config.SSID + ": failed to set requirePMF: "+
1681                        config.requirePMF);
1682                break setVariables;
1683            }
1684
1685            if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable(
1686                    netId,
1687                    WifiConfiguration.updateIdentiferVarName,
1688                    config.updateIdentifier)) {
1689                loge(config.SSID + ": failed to set updateIdentifier: "+
1690                        config.updateIdentifier);
1691                break setVariables;
1692            }
1693
1694            if (config.enterpriseConfig != null &&
1695                    config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
1696
1697                WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
1698
1699                if (needsKeyStore(enterpriseConfig)) {
1700                    /**
1701                     * Keyguard settings may eventually be controlled by device policy.
1702                     * We check here if keystore is unlocked before installing
1703                     * credentials.
1704                     * TODO: Do we need a dialog here ?
1705                     */
1706                    if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
1707                        loge(config.SSID + ": key store is locked");
1708                        break setVariables;
1709                    }
1710
1711                    try {
1712                        /* config passed may include only fields being updated.
1713                         * In order to generate the key id, fetch uninitialized
1714                         * fields from the currently tracked configuration
1715                         */
1716                        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
1717                        String keyId = config.getKeyIdForCredentials(currentConfig);
1718
1719                        if (!installKeys(enterpriseConfig, keyId)) {
1720                            loge(config.SSID + ": failed to install keys");
1721                            break setVariables;
1722                        }
1723                    } catch (IllegalStateException e) {
1724                        loge(config.SSID + " invalid config for key installation");
1725                        break setVariables;
1726                    }
1727                }
1728
1729                HashMap<String, String> enterpriseFields = enterpriseConfig.getFields();
1730                for (String key : enterpriseFields.keySet()) {
1731                        String value = enterpriseFields.get(key);
1732                        if (key.equals("password") && value != null && value.equals("*")) {
1733                            //no need to try to set an obfuscated password, which will fail
1734                            continue;
1735                        }
1736                        if (!mWifiNative.setNetworkVariable(
1737                                    netId,
1738                                    key,
1739                                    value)) {
1740                            removeKeys(enterpriseConfig);
1741                            loge(config.SSID + ": failed to set " + key +
1742                                    ": " + value);
1743                            break setVariables;
1744                        }
1745                }
1746            }
1747            updateFailed = false;
1748        } //end of setVariables
1749
1750        if (updateFailed) {
1751            if (newNetwork) {
1752                mWifiNative.removeNetwork(netId);
1753                loge("Failed to set a network variable, removed network: " + netId);
1754            }
1755            return new NetworkUpdateResult(INVALID_NETWORK_ID);
1756        }
1757
1758        /* An update of the network variables requires reading them
1759         * back from the supplicant to update mConfiguredNetworks.
1760         * This is because some of the variables (SSID, wep keys &
1761         * passphrases) reflect different values when read back than
1762         * when written. For example, wep key is stored as * irrespective
1763         * of the value sent to the supplicant
1764         */
1765        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
1766        if (currentConfig == null) {
1767            currentConfig = new WifiConfiguration();
1768            currentConfig.setIpAssignment(IpAssignment.DHCP);
1769            currentConfig.setProxySettings(ProxySettings.NONE);
1770            currentConfig.networkId = netId;
1771            if (config != null) {
1772                //carry over the creation parameters
1773                currentConfig.selfAdded = config.selfAdded;
1774                currentConfig.didSelfAdd = config.didSelfAdd;
1775                currentConfig.lastConnectUid = config.lastConnectUid;
1776                currentConfig.lastUpdateUid = config.lastUpdateUid;
1777                currentConfig.creatorUid = config.creatorUid;
1778                currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
1779            }
1780            if (DBG) {
1781                loge("created new config netId=" + Integer.toString(netId)
1782                        + " uid=" + Integer.toString(currentConfig.creatorUid));
1783            }
1784        }
1785
1786        if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
1787            //make sure the configuration is not deleted anymore since we just
1788            //added or modified it.
1789            currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
1790            currentConfig.selfAdded = false;
1791            currentConfig.didSelfAdd = false;
1792        }
1793
1794        if (DBG) loge("will read network variables netId=" + Integer.toString(netId));
1795
1796        readNetworkVariables(currentConfig);
1797
1798        mConfiguredNetworks.put(netId, currentConfig);
1799        mNetworkIds.put(configKey(currentConfig), netId);
1800
1801        NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
1802        result.setIsNewNetwork(newNetwork);
1803        result.setNetworkId(netId);
1804        return result;
1805    }
1806
1807
1808    public void linkConfiguration(WifiConfiguration config) {
1809        for (WifiConfiguration link : mConfiguredNetworks.values()) {
1810            boolean doLink = false;
1811
1812            if (link.configKey().equals(config.configKey())) {
1813                continue;
1814            }
1815
1816            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
1817                continue;
1818            }
1819
1820            //autojoin will be allowed to dynamically jump from a linked configuration
1821            //to another, hence only link configurations that have equivalent level of security
1822            if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) {
1823                continue;
1824            }
1825
1826            if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) {
1827                //if both default GW are known, compare based on RSSI only if the GW is equal
1828                if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) {
1829
1830                    if (VDBG) {
1831                        loge("linkConfiguration link due to same gw" + link.SSID +
1832                                " and " + config.SSID + " GW " + config.defaultGwMacAddress);
1833                    }
1834                    doLink = true;
1835                }
1836            } else {
1837                // we do not know BOTH default gateways hence we will try to link
1838                // hoping that WifiConfigurations are indeed behind the same gateway
1839                // once both WifiConfiguration will have been tried we will know
1840                // the default gateway and revisit the choice of linking them
1841                if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 6)
1842                        && (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) {
1843                    String abssid = "";
1844                    String bbssid = "";
1845                    for (String key : config.scanResultCache.keySet()) {
1846                        abssid = key;
1847                    }
1848                    for (String key : link.scanResultCache.keySet()) {
1849                        bbssid = key;
1850                    }
1851                    if (VDBG) {
1852                        loge("linkConfiguration try to link due to DBDC BSSID match " + link.SSID +
1853                                " and " + config.SSID + " bssida " + abssid + " bssidb " + bbssid);
1854                    }
1855                    if (abssid.regionMatches(true, 0, bbssid, 0, 16)) {
1856                        //if first 16 ascii characters of BSSID matches, we assume this is a DBDC
1857                        doLink = true;
1858                    }
1859                }
1860            }
1861
1862            if (doLink) {
1863                if (VDBG) {
1864                    loge("linkConfiguration: will link " + link.SSID + " and " + config.SSID);
1865                }
1866                if (link.linkedConfigurations == null) {
1867                    link.linkedConfigurations = new HashMap<String, Integer>();
1868                }
1869                if (config.linkedConfigurations == null) {
1870                    config.linkedConfigurations = new HashMap<String, Integer>();
1871                }
1872                link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
1873                config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
1874            } else {
1875                //todo if they are linked, break the link
1876            }
1877        }
1878    }
1879
1880    /*
1881     * We try to link a scan result with a WifiConfiguration for which SSID and ket management dont match,
1882     * for instance, we try identify the 5GHz SSID of a DBDC AP, even though we know only of the 2.4GHz
1883     *
1884     * Obviously, this function is not optimal since it is used to compare every scan
1885     * result with every Saved WifiConfiguration, with a string.equals operation.
1886     * As a speed up, might be better to implement the mConfiguredNetworks store as a
1887     * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object
1888     * so as to speed this up. Also to prevent the tiny probability of hash collision.
1889     *
1890     */
1891    public WifiConfiguration associateWithConfiguration(ScanResult result) {
1892        String configKey = WifiConfiguration.configKey(result);
1893        if (configKey == null) {
1894            if (DBG) loge("associateWithConfiguration(): no config key " );
1895            return null;
1896        }
1897
1898        //need to compare with quoted string
1899        String SSID = "\"" + result.SSID + "\"";
1900
1901        WifiConfiguration config = null;
1902        for (WifiConfiguration link : mConfiguredNetworks.values()) {
1903            boolean doLink = false;
1904
1905            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.didSelfAdd) {
1906                //make sure we dont associate the scan result to a deleted config
1907                continue;
1908            }
1909
1910            if (configKey.equals(link.configKey())) {
1911                if (VDBG) loge("associateWithConfiguration(): found it!!! " + configKey );
1912                return link; //found it exactly
1913            }
1914
1915            if ((link.scanResultCache != null) && (link.scanResultCache.size() <= 4)) {
1916                String bssid = "";
1917                for (String key : link.scanResultCache.keySet()) {
1918                    bssid = key;
1919                }
1920
1921                if (result.BSSID.regionMatches(true, 0, bssid, 0, 16)
1922                        && SSID.regionMatches(false, 0, link.SSID, 0, 3)) {
1923                    // if first 16 ascii characters of BSSID matches, and first 3
1924                    // characters of SSID match, we assume this is a home setup
1925                    // and thus we will try to transfer the password from the known
1926                    // BSSID/SSID to the recently found BSSID/SSID
1927
1928                    //if (VDBG)
1929                    //    loge("associateWithConfiguration OK " );
1930                    doLink = true;
1931                }
1932            }
1933
1934            if (doLink) {
1935                //try to make a non verified WifiConfiguration, but only if the original
1936                //configuration was not self already added
1937                if (VDBG) {
1938                    loge("associateWithConfiguration: will create " +
1939                            result.SSID + " and associate it with: " + link.SSID);
1940                }
1941                config = wifiConfigurationFromScanResult(result);
1942                if (config != null) {
1943                    config.selfAdded = true;
1944                    config.didSelfAdd = true;
1945                    config.peerWifiConfiguration = link.configKey();
1946                    if (config.allowedKeyManagement.equals(link.allowedKeyManagement) &&
1947                            config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1948                        //transfer the credentials from the configuration we are linking from
1949                        String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
1950                        if (psk != null) {
1951                            config.preSharedKey = psk;
1952                            if (VDBG) {
1953                                if (config.preSharedKey != null)
1954                                    loge(" transfer PSK : " + config.preSharedKey);
1955                            }
1956
1957                            //link configurations
1958                            if (link.linkedConfigurations == null) {
1959                                link.linkedConfigurations = new HashMap<String, Integer>();
1960                            }
1961                            if (config.linkedConfigurations == null) {
1962                                config.linkedConfigurations = new HashMap<String, Integer>();
1963                            }
1964                            link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
1965                            config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
1966                        } else {
1967                            config = null;
1968                        }
1969                    } else {
1970                        config = null;
1971                    }
1972                }
1973            } else {
1974                //todo if they are linked, break the link
1975            }
1976        }
1977        return config;
1978    }
1979
1980    public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) {
1981        if (config == null)
1982            return null;
1983        long now_ms = System.currentTimeMillis();
1984
1985        HashSet<Integer> channels = new HashSet<Integer>();
1986
1987        //get channels for this configuration, if there are at least 2 BSSIDs
1988        if (config.scanResultCache == null && config.linkedConfigurations == null) {
1989            return null;
1990        }
1991
1992        if (VDBG) {
1993            StringBuilder dbg = new StringBuilder();
1994            dbg.append("makeChannelList age=" + Integer.toString(age) + " for " + config.configKey());
1995            if (config.scanResultCache != null) {
1996                dbg.append(" bssids=" + config.scanResultCache.size());
1997            }
1998            if (config.linkedConfigurations != null) {
1999                dbg.append(" linked=" + config.linkedConfigurations.size());
2000            }
2001            loge(dbg.toString());
2002        }
2003
2004        if (config.scanResultCache != null && config.scanResultCache.size() > 0) {
2005            for (ScanResult result : config.scanResultCache.values()) {
2006                if (VDBG) {
2007                    boolean test = (now_ms - result.seen) < age;
2008                    loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
2009                            + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
2010                }
2011                if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
2012                    channels.add(result.frequency);
2013                }
2014            }
2015        }
2016
2017        //get channels for linked configurations
2018        if (config.linkedConfigurations != null) {
2019            for (String key : config.linkedConfigurations.keySet()) {
2020                WifiConfiguration linked = getWifiConfiguration(key);
2021                if (linked == null)
2022                    continue;
2023                if (linked.scanResultCache == null) {
2024                    continue;
2025                }
2026                for (ScanResult result : linked.scanResultCache.values()) {
2027                    if (VDBG) {
2028                        loge("has link: " + result.BSSID
2029                                + " freq=" + Integer.toString(result.frequency)
2030                                + " age=" + Long.toString(now_ms - result.seen));
2031                    }
2032                    if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
2033                        channels.add(result.frequency);
2034                    }
2035                }
2036            }
2037        }
2038        return channels;
2039    }
2040
2041    public WifiConfiguration updateSavedNetworkHistory(ScanResult scanResult) {
2042        WifiConfiguration found = null;
2043        if (scanResult == null)
2044            return found;
2045
2046        //first step, look for this scan Result by SSID + Key Management
2047        String key = WifiConfiguration.configKey(scanResult);
2048        int hash = key.hashCode();
2049
2050        Integer netId = mNetworkIds.get(hash);
2051        if (netId == null) return null;
2052        WifiConfiguration config = mConfiguredNetworks.get(netId);
2053        if (config != null) {
2054           if (config.scanResultCache == null) {
2055                config.scanResultCache = new HashMap<String, ScanResult>();
2056           }
2057           if (config.scanResultCache == null) {
2058                return null;
2059           }
2060           //add the scan result to this WifiConfiguration
2061           config.scanResultCache.put(scanResult.BSSID, scanResult);
2062           mConfiguredNetworks.put(netId, config);
2063           linkConfiguration(config);
2064           found = config;
2065        }
2066
2067        if (VDBG) {
2068            config = mConfiguredNetworks.get(netId);
2069            if (config != null) {
2070                if (config.scanResultCache != null) {
2071                    loge("                    got known scan result " +
2072                            scanResult.BSSID + " key : " + key + " num: " +
2073                            Integer.toString(config.scanResultCache.size())
2074                            + " rssi=" + Integer.toString(scanResult.level));
2075                } else {
2076                    loge("                    got known scan result and no cache" +
2077                            scanResult.BSSID + " key : " + key);
2078                }
2079            }
2080        }
2081        return found;
2082
2083    }
2084
2085    /* Compare current and new configuration and write to file on change */
2086    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
2087            WifiConfiguration currentConfig,
2088            WifiConfiguration newConfig) {
2089        boolean ipChanged = false;
2090        boolean proxyChanged = false;
2091        LinkProperties linkProperties = null;
2092
2093        if (VDBG) {
2094            loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +
2095                    newConfig.SSID + " path: " + ipConfigFile);
2096        }
2097
2098
2099        switch (newConfig.getIpAssignment()) {
2100            case STATIC:
2101                Collection<LinkAddress> currentLinkAddresses = currentConfig.getLinkProperties()
2102                        .getLinkAddresses();
2103                Collection<LinkAddress> newLinkAddresses = newConfig.getLinkProperties()
2104                        .getLinkAddresses();
2105                Collection<InetAddress> currentDnses =
2106                        currentConfig.getLinkProperties().getDnsServers();
2107                Collection<InetAddress> newDnses = newConfig.getLinkProperties().getDnsServers();
2108                Collection<RouteInfo> currentRoutes = currentConfig.getLinkProperties().getRoutes();
2109                Collection<RouteInfo> newRoutes = newConfig.getLinkProperties().getRoutes();
2110
2111                boolean linkAddressesDiffer =
2112                        (currentLinkAddresses.size() != newLinkAddresses.size()) ||
2113                        !currentLinkAddresses.containsAll(newLinkAddresses);
2114                boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) ||
2115                        !currentDnses.containsAll(newDnses);
2116                boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) ||
2117                        !currentRoutes.containsAll(newRoutes);
2118
2119                if ((currentConfig.getIpAssignment() != newConfig.getIpAssignment()) ||
2120                        linkAddressesDiffer ||
2121                        dnsesDiffer ||
2122                        routesDiffer) {
2123                    ipChanged = true;
2124                }
2125                break;
2126            case DHCP:
2127                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
2128                    ipChanged = true;
2129                }
2130                break;
2131            case UNASSIGNED:
2132                /* Ignore */
2133                break;
2134            default:
2135                loge("Ignore invalid ip assignment during write");
2136                break;
2137        }
2138
2139        switch (newConfig.getProxySettings()) {
2140            case STATIC:
2141            case PAC:
2142                ProxyInfo newHttpProxy = newConfig.getLinkProperties().getHttpProxy();
2143                ProxyInfo currentHttpProxy = currentConfig.getLinkProperties().getHttpProxy();
2144
2145                if (newHttpProxy != null) {
2146                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
2147                } else {
2148                    proxyChanged = (currentHttpProxy != null);
2149                }
2150                break;
2151            case NONE:
2152                if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
2153                    proxyChanged = true;
2154                }
2155                break;
2156            case UNASSIGNED:
2157                /* Ignore */
2158                break;
2159            default:
2160                loge("Ignore invalid proxy configuration during write");
2161                break;
2162        }
2163
2164        if (!ipChanged) {
2165            linkProperties = copyIpSettingsFromConfig(currentConfig);
2166        } else {
2167            currentConfig.setIpAssignment(newConfig.getIpAssignment());
2168            linkProperties = copyIpSettingsFromConfig(newConfig);
2169            log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
2170                    linkProperties.toString());
2171        }
2172
2173
2174        if (!proxyChanged) {
2175            linkProperties.setHttpProxy(currentConfig.getLinkProperties().getHttpProxy());
2176        } else {
2177            currentConfig.setProxySettings(newConfig.getProxySettings());
2178            linkProperties.setHttpProxy(newConfig.getLinkProperties().getHttpProxy());
2179            log("proxy changed SSID = " + currentConfig.SSID);
2180            if (linkProperties.getHttpProxy() != null) {
2181                log(" proxyProperties: " + linkProperties.getHttpProxy().toString());
2182            }
2183        }
2184
2185        if (ipChanged || proxyChanged) {
2186            currentConfig.setLinkProperties(linkProperties);
2187            writeIpAndProxyConfigurations();
2188            sendConfiguredNetworksChangedBroadcast(currentConfig,
2189                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
2190        }
2191        return new NetworkUpdateResult(ipChanged, proxyChanged);
2192    }
2193
2194    private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) {
2195        LinkProperties linkProperties = new LinkProperties();
2196        linkProperties.setInterfaceName(config.getLinkProperties().getInterfaceName());
2197        for (LinkAddress linkAddr : config.getLinkProperties().getLinkAddresses()) {
2198            linkProperties.addLinkAddress(linkAddr);
2199        }
2200        for (RouteInfo route : config.getLinkProperties().getRoutes()) {
2201            linkProperties.addRoute(route);
2202        }
2203        for (InetAddress dns : config.getLinkProperties().getDnsServers()) {
2204            linkProperties.addDnsServer(dns);
2205        }
2206        return linkProperties;
2207    }
2208
2209    /**
2210     * Read the variables from the supplicant daemon that are needed to
2211     * fill in the WifiConfiguration object.
2212     *
2213     * @param config the {@link WifiConfiguration} object to be filled in.
2214     */
2215    private void readNetworkVariables(WifiConfiguration config) {
2216
2217        int netId = config.networkId;
2218        if (netId < 0)
2219            return;
2220
2221        /*
2222         * TODO: maybe should have a native method that takes an array of
2223         * variable names and returns an array of values. But we'd still
2224         * be doing a round trip to the supplicant daemon for each variable.
2225         */
2226        String value;
2227
2228        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
2229        if (!TextUtils.isEmpty(value)) {
2230            if (value.charAt(0) != '"') {
2231                config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
2232                //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
2233                //supplicant string
2234            } else {
2235                config.SSID = value;
2236            }
2237        } else {
2238            config.SSID = null;
2239        }
2240
2241        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
2242        if (!TextUtils.isEmpty(value)) {
2243            config.BSSID = value;
2244        } else {
2245            config.BSSID = null;
2246        }
2247
2248        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
2249        config.priority = -1;
2250        if (!TextUtils.isEmpty(value)) {
2251            try {
2252                config.priority = Integer.parseInt(value);
2253            } catch (NumberFormatException ignore) {
2254            }
2255        }
2256
2257        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
2258        config.hiddenSSID = false;
2259        if (!TextUtils.isEmpty(value)) {
2260            try {
2261                config.hiddenSSID = Integer.parseInt(value) != 0;
2262            } catch (NumberFormatException ignore) {
2263            }
2264        }
2265
2266        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
2267        config.wepTxKeyIndex = -1;
2268        if (!TextUtils.isEmpty(value)) {
2269            try {
2270                config.wepTxKeyIndex = Integer.parseInt(value);
2271            } catch (NumberFormatException ignore) {
2272            }
2273        }
2274
2275        for (int i = 0; i < 4; i++) {
2276            value = mWifiNative.getNetworkVariable(netId,
2277                    WifiConfiguration.wepKeyVarNames[i]);
2278            if (!TextUtils.isEmpty(value)) {
2279                config.wepKeys[i] = value;
2280            } else {
2281                config.wepKeys[i] = null;
2282            }
2283        }
2284
2285        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
2286        if (!TextUtils.isEmpty(value)) {
2287            config.preSharedKey = value;
2288        } else {
2289            config.preSharedKey = null;
2290        }
2291
2292        value = mWifiNative.getNetworkVariable(config.networkId,
2293                WifiConfiguration.Protocol.varName);
2294        if (!TextUtils.isEmpty(value)) {
2295            String vals[] = value.split(" ");
2296            for (String val : vals) {
2297                int index =
2298                    lookupString(val, WifiConfiguration.Protocol.strings);
2299                if (0 <= index) {
2300                    config.allowedProtocols.set(index);
2301                }
2302            }
2303        }
2304
2305        value = mWifiNative.getNetworkVariable(config.networkId,
2306                WifiConfiguration.KeyMgmt.varName);
2307        if (!TextUtils.isEmpty(value)) {
2308            String vals[] = value.split(" ");
2309            for (String val : vals) {
2310                int index =
2311                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
2312                if (0 <= index) {
2313                    config.allowedKeyManagement.set(index);
2314                }
2315            }
2316        }
2317
2318        value = mWifiNative.getNetworkVariable(config.networkId,
2319                WifiConfiguration.AuthAlgorithm.varName);
2320        if (!TextUtils.isEmpty(value)) {
2321            String vals[] = value.split(" ");
2322            for (String val : vals) {
2323                int index =
2324                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
2325                if (0 <= index) {
2326                    config.allowedAuthAlgorithms.set(index);
2327                }
2328            }
2329        }
2330
2331        value = mWifiNative.getNetworkVariable(config.networkId,
2332                WifiConfiguration.PairwiseCipher.varName);
2333        if (!TextUtils.isEmpty(value)) {
2334            String vals[] = value.split(" ");
2335            for (String val : vals) {
2336                int index =
2337                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
2338                if (0 <= index) {
2339                    config.allowedPairwiseCiphers.set(index);
2340                }
2341            }
2342        }
2343
2344        value = mWifiNative.getNetworkVariable(config.networkId,
2345                WifiConfiguration.GroupCipher.varName);
2346        if (!TextUtils.isEmpty(value)) {
2347            String vals[] = value.split(" ");
2348            for (String val : vals) {
2349                int index =
2350                    lookupString(val, WifiConfiguration.GroupCipher.strings);
2351                if (0 <= index) {
2352                    config.allowedGroupCiphers.set(index);
2353                }
2354            }
2355        }
2356
2357        if (config.enterpriseConfig == null) {
2358            config.enterpriseConfig = new WifiEnterpriseConfig();
2359        }
2360        HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
2361        for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) {
2362            value = mWifiNative.getNetworkVariable(netId, key);
2363            if (!TextUtils.isEmpty(value)) {
2364                enterpriseFields.put(key, removeDoubleQuotes(value));
2365            } else {
2366                enterpriseFields.put(key, EMPTY_VALUE);
2367            }
2368        }
2369
2370        if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) {
2371            saveConfig();
2372        }
2373
2374        migrateCerts(config.enterpriseConfig);
2375        // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore);
2376    }
2377
2378    private static String removeDoubleQuotes(String string) {
2379        int length = string.length();
2380        if ((length > 1) && (string.charAt(0) == '"')
2381                && (string.charAt(length - 1) == '"')) {
2382            return string.substring(1, length - 1);
2383        }
2384        return string;
2385    }
2386
2387    private static String makeString(BitSet set, String[] strings) {
2388        StringBuffer buf = new StringBuffer();
2389        int nextSetBit = -1;
2390
2391        /* Make sure all set bits are in [0, strings.length) to avoid
2392         * going out of bounds on strings.  (Shouldn't happen, but...) */
2393        set = set.get(0, strings.length);
2394
2395        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
2396            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
2397        }
2398
2399        // remove trailing space
2400        if (set.cardinality() > 0) {
2401            buf.setLength(buf.length() - 1);
2402        }
2403
2404        return buf.toString();
2405    }
2406
2407    private int lookupString(String string, String[] strings) {
2408        int size = strings.length;
2409
2410        string = string.replace('-', '_');
2411
2412        for (int i = 0; i < size; i++)
2413            if (string.equals(strings[i]))
2414                return i;
2415
2416        // if we ever get here, we should probably add the
2417        // value to WifiConfiguration to reflect that it's
2418        // supported by the WPA supplicant
2419        loge("Failed to look-up a string: " + string);
2420
2421        return -1;
2422    }
2423
2424    /* return the allowed key management based on a scan result */
2425
2426    public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
2427        WifiConfiguration config = new WifiConfiguration();
2428
2429        config.SSID = "\"" + result.SSID + "\"";
2430
2431        if (VDBG) {
2432            loge("WifiConfiguration from scan results " +
2433                    config.SSID + " cap " + result.capabilities);
2434        }
2435        if (result.capabilities.contains("WEP")) {
2436            config.allowedKeyManagement.set(KeyMgmt.NONE);
2437            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //?
2438            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
2439        }
2440
2441        if (result.capabilities.contains("PSK")) {
2442            config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
2443        }
2444
2445        if (result.capabilities.contains("EAP")) {
2446            //this is probably wrong, as we don't have a way to enter the enterprise config
2447            config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
2448            config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
2449        }
2450
2451        config.scanResultCache = new HashMap<String, ScanResult>();
2452        if (config.scanResultCache == null)
2453            return null;
2454        config.scanResultCache.put(result.BSSID, result);
2455
2456        return config;
2457    }
2458
2459
2460    /* Returns a unique for a given configuration */
2461    private static int configKey(WifiConfiguration config) {
2462        String key = config.configKey();
2463        return key.hashCode();
2464    }
2465
2466    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2467        pw.println("WifiConfigStore");
2468        pw.println("mLastPriority " + mLastPriority);
2469        pw.println("Configured networks");
2470        for (WifiConfiguration conf : getConfiguredNetworks()) {
2471            pw.println(conf);
2472        }
2473        pw.println();
2474
2475        if (mLocalLog != null) {
2476            pw.println("WifiConfigStore - Log Begin ----");
2477            mLocalLog.dump(fd, pw, args);
2478            pw.println("WifiConfigStore - Log End ----");
2479        }
2480    }
2481
2482    public String getConfigFile() {
2483        return ipConfigFile;
2484    }
2485
2486    protected void loge(String s) {
2487        loge(s, false);
2488    }
2489
2490    protected void loge(String s, boolean stack) {
2491        if (stack) {
2492            Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
2493                    + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
2494                    + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
2495                    + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
2496        } else {
2497            Log.e(TAG, s);
2498        }
2499    }
2500
2501    protected void log(String s) {
2502        Log.d(TAG, s);
2503    }
2504
2505    private void localLog(String s) {
2506        if (mLocalLog != null) {
2507            mLocalLog.log(s);
2508        }
2509    }
2510
2511    private void localLog(String s, int netId) {
2512        if (mLocalLog == null) {
2513            return;
2514        }
2515
2516        WifiConfiguration config;
2517        synchronized(mConfiguredNetworks) {
2518            config = mConfiguredNetworks.get(netId);
2519        }
2520
2521        if (config != null) {
2522            mLocalLog.log(s + " " + config.getPrintableSsid());
2523        } else {
2524            mLocalLog.log(s + " " + netId);
2525        }
2526    }
2527
2528    // Certificate and private key management for EnterpriseConfig
2529    static boolean needsKeyStore(WifiEnterpriseConfig config) {
2530        // Has no keys to be installed
2531        if (config.getClientCertificate() == null && config.getCaCertificate() == null)
2532            return false;
2533        return true;
2534    }
2535
2536    static boolean isHardwareBackedKey(PrivateKey key) {
2537        return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
2538    }
2539
2540    static boolean hasHardwareBackedKey(Certificate certificate) {
2541        return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
2542    }
2543
2544    static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
2545        String client = config.getClientCertificateAlias();
2546        if (!TextUtils.isEmpty(client)) {
2547            // a valid client certificate is configured
2548
2549            // BUGBUG: keyStore.get() never returns certBytes; because it is not
2550            // taking WIFI_UID as a parameter. It always looks for certificate
2551            // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
2552            // all certificates need software keystore until we get the get() API
2553            // fixed.
2554
2555            return true;
2556        }
2557
2558        /*
2559        try {
2560
2561            if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
2562                    .USER_CERTIFICATE + client);
2563
2564            CertificateFactory factory = CertificateFactory.getInstance("X.509");
2565            if (factory == null) {
2566                Slog.e(TAG, "Error getting certificate factory");
2567                return;
2568            }
2569
2570            byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
2571            if (certBytes != null) {
2572                Certificate cert = (X509Certificate) factory.generateCertificate(
2573                        new ByteArrayInputStream(certBytes));
2574
2575                if (cert != null) {
2576                    mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
2577
2578                    if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
2579                            .USER_CERTIFICATE + client);
2580                    if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
2581                            "does not need" ) + " software key store");
2582                } else {
2583                    Slog.d(TAG, "could not generate certificate");
2584                }
2585            } else {
2586                Slog.e(TAG, "Could not load client certificate " + Credentials
2587                        .USER_CERTIFICATE + client);
2588                mNeedsSoftwareKeystore = true;
2589            }
2590
2591        } catch(CertificateException e) {
2592            Slog.e(TAG, "Could not read certificates");
2593            mCaCert = null;
2594            mClientCertificate = null;
2595        }
2596        */
2597
2598        return false;
2599    }
2600
2601    /** called when CS ask WiFistateMachine to disconnect the current network
2602     * because the score is bad.
2603     */
2604    void handleBadNetworkDisconnectReport(int netId, WifiInfo info) {
2605        /* TODO verify the bad network is current */
2606        WifiConfiguration config = mConfiguredNetworks.get(netId);
2607        if (config != null) {
2608            if ((info.getRssi() < WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_24
2609                    && info.is24GHz()) || (info.getRssi() <
2610                            WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_5 && info.is5GHz())) {
2611                //we got disconnected and RSSI was bad, so disable light
2612                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
2613                        + WifiConfiguration.UNWANTED_BLACKLIST_SOFT_BUMP);
2614                loge("handleBadNetworkDisconnectReport (+4) "
2615                        + Integer.toString(netId) + " " + info);
2616            } else {
2617                //we got disabled but RSSI is good, so disable hard
2618                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
2619                        + WifiConfiguration.UNWANTED_BLACKLIST_HARD_BUMP);
2620                loge("handleBadNetworkDisconnectReport (+8) "
2621                        + Integer.toString(netId) + " " + info);
2622            }
2623        }
2624    }
2625
2626    void handleSSIDStateChange(int netId, boolean enabled, String message) {
2627        WifiConfiguration config = mConfiguredNetworks.get(netId);
2628        if (config != null) {
2629            if (enabled) {
2630                loge("SSID re-enabled for  " + config.configKey() +
2631                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
2632                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
2633                //TODO: really I don't know if re-enabling is right but we should err on the side of trying to connect
2634                //TODO: even if the attempt will fail
2635                if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
2636                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
2637                }
2638            } else {
2639                loge("SSID temp disabled for  " + config.configKey() +
2640                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
2641                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
2642                if (message != null) {
2643                    loge(" wpa_supplicant message=" + message);
2644                }
2645                if (config.selfAdded && config.lastConnected == 0) {
2646                    //this is a network we self added, and we never succeeded,
2647                    //the user did not create this network and never entered its credentials, so we want
2648                    //to be very aggressive in disabling it completely.
2649                    disableNetwork(config.networkId, WifiConfiguration.DISABLED_AUTH_FAILURE);
2650                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
2651                    config.disableReason = WifiConfiguration.DISABLED_AUTH_FAILURE;
2652                } else {
2653                    if (message != null) {
2654                        if (message.contains("WRONG_KEY")
2655                                || message.contains("AUTH_FAILED")) {
2656                            //This configuration has received an auth failure, so disable it
2657                            //temporarily because we don't want auto-join to try it out.
2658                            //this network may be re-enabled by the "usual"
2659                            // enableAllNetwork function
2660                            //TODO: resolve interpretation of WRONG_KEY and AUTH_FAILURE:
2661                            //TODO: if we could count on the wrong_ley or auth_failure message to be correct
2662                            //TODO: then we could just mark the configuration as DISABLED_ON_AUTH_FAILURE
2663                            //TODO: and the configuration will stay there until user enter new credentials
2664                            //TODO: It is not the case however, so instead  of disabling, let's start
2665                            //TODO: blacklisting hard
2666                            if (config.autoJoinStatus <=
2667                                    WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
2668                                //4 auth failure will reach 128 and disable permanently
2669                                //autoJoinStatus: 0 -> 4 -> 20 -> 84 -> 128
2670                                config.setAutoJoinStatus(4 + config.autoJoinStatus * 4);
2671                                if (config.autoJoinStatus > WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)
2672                                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
2673                            }
2674                            if (DBG) {
2675                                loge("blacklisted " + config.configKey() + " to "
2676                                        + Integer.toString(config.autoJoinStatus));
2677                            }
2678                        }
2679                        message.replace("\n", "");
2680                        message.replace("\r", "");
2681                        config.lastFailure = message;
2682                    }
2683                }
2684            }
2685        }
2686    }
2687
2688    boolean installKeys(WifiEnterpriseConfig config, String name) {
2689        boolean ret = true;
2690        String privKeyName = Credentials.USER_PRIVATE_KEY + name;
2691        String userCertName = Credentials.USER_CERTIFICATE + name;
2692        String caCertName = Credentials.CA_CERTIFICATE + name;
2693        if (config.getClientCertificate() != null) {
2694            byte[] privKeyData = config.getClientPrivateKey().getEncoded();
2695            if (isHardwareBackedKey(config.getClientPrivateKey())) {
2696                // Hardware backed key store is secure enough to store keys un-encrypted, this
2697                // removes the need for user to punch a PIN to get access to these keys
2698                if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store");
2699                ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID,
2700                        KeyStore.FLAG_NONE);
2701            } else {
2702                // Software backed key store is NOT secure enough to store keys un-encrypted.
2703                // Save keys encrypted so they are protected with user's PIN. User will
2704                // have to unlock phone before being able to use these keys and connect to
2705                // networks.
2706                if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store");
2707                ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
2708                        KeyStore.FLAG_ENCRYPTED);
2709            }
2710            if (ret == false) {
2711                return ret;
2712            }
2713
2714            ret = putCertInKeyStore(userCertName, config.getClientCertificate());
2715            if (ret == false) {
2716                // Remove private key installed
2717                mKeyStore.delKey(privKeyName, Process.WIFI_UID);
2718                return ret;
2719            }
2720        }
2721
2722        if (config.getCaCertificate() != null) {
2723            ret = putCertInKeyStore(caCertName, config.getCaCertificate());
2724            if (ret == false) {
2725                if (config.getClientCertificate() != null) {
2726                    // Remove client key+cert
2727                    mKeyStore.delKey(privKeyName, Process.WIFI_UID);
2728                    mKeyStore.delete(userCertName, Process.WIFI_UID);
2729                }
2730                return ret;
2731            }
2732        }
2733
2734        // Set alias names
2735        if (config.getClientCertificate() != null) {
2736            config.setClientCertificateAlias(name);
2737            config.resetClientKeyEntry();
2738        }
2739
2740        if (config.getCaCertificate() != null) {
2741            config.setCaCertificateAlias(name);
2742            config.resetCaCertificate();
2743        }
2744
2745        return ret;
2746    }
2747
2748    private boolean putCertInKeyStore(String name, Certificate cert) {
2749        try {
2750            byte[] certData = Credentials.convertToPem(cert);
2751            if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore");
2752            return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
2753
2754        } catch (IOException e1) {
2755            return false;
2756        } catch (CertificateException e2) {
2757            return false;
2758        }
2759    }
2760
2761    void removeKeys(WifiEnterpriseConfig config) {
2762        String client = config.getClientCertificateAlias();
2763        // a valid client certificate is configured
2764        if (!TextUtils.isEmpty(client)) {
2765            if (DBG) Log.d(TAG, "removing client private key and user cert");
2766            mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
2767            mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
2768        }
2769
2770        String ca = config.getCaCertificateAlias();
2771        // a valid ca certificate is configured
2772        if (!TextUtils.isEmpty(ca)) {
2773            if (DBG) Log.d(TAG, "removing CA cert");
2774            mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
2775        }
2776    }
2777
2778
2779    /** Migrates the old style TLS config to the new config style. This should only be used
2780     * when restoring an old wpa_supplicant.conf or upgrading from a previous
2781     * platform version.
2782     * @return true if the config was updated
2783     * @hide
2784     */
2785    boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) {
2786        String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME);
2787        /*
2788         * If the old configuration value is not present, then there is nothing
2789         * to do.
2790         */
2791        if (TextUtils.isEmpty(oldPrivateKey)) {
2792            return false;
2793        } else {
2794            // Also ignore it if it's empty quotes.
2795            oldPrivateKey = removeDoubleQuotes(oldPrivateKey);
2796            if (TextUtils.isEmpty(oldPrivateKey)) {
2797                return false;
2798            }
2799        }
2800
2801        config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE);
2802        config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY,
2803                WifiEnterpriseConfig.ENGINE_ID_KEYSTORE);
2804
2805        /*
2806        * The old key started with the keystore:// URI prefix, but we don't
2807        * need that anymore. Trim it off if it exists.
2808        */
2809        final String keyName;
2810        if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) {
2811            keyName = new String(
2812                    oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length()));
2813        } else {
2814            keyName = oldPrivateKey;
2815        }
2816        config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName);
2817
2818        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY,
2819                config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""));
2820
2821        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY,
2822                config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""));
2823
2824        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY,
2825                config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""));
2826
2827        // Remove old private_key string so we don't run this again.
2828        mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE);
2829
2830        return true;
2831    }
2832
2833    /** Migrate certs from global pool to wifi UID if not already done */
2834    void migrateCerts(WifiEnterpriseConfig config) {
2835        String client = config.getClientCertificateAlias();
2836        // a valid client certificate is configured
2837        if (!TextUtils.isEmpty(client)) {
2838            if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) {
2839                mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1,
2840                        Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
2841                mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1,
2842                        Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
2843            }
2844        }
2845
2846        String ca = config.getCaCertificateAlias();
2847        // a valid ca certificate is configured
2848        if (!TextUtils.isEmpty(ca)) {
2849            if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) {
2850                mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1,
2851                        Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
2852            }
2853        }
2854    }
2855
2856}
2857