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