WifiConfigStore.java revision 25ee2d5d30434712b28aef6eec9460035101e493
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;
58import com.android.internal.R;
59import com.android.server.wifi.anqp.ANQPElement;
60import com.android.server.wifi.anqp.Constants;
61import com.android.server.wifi.hotspot2.ANQPData;
62import com.android.server.wifi.hotspot2.AnqpCache;
63import com.android.server.wifi.hotspot2.Chronograph;
64import com.android.server.wifi.hotspot2.NetworkDetail;
65import com.android.server.wifi.hotspot2.PasspointMatch;
66import com.android.server.wifi.hotspot2.PasspointMatchInfo;
67import com.android.server.wifi.hotspot2.SupplicantBridge;
68import com.android.server.wifi.hotspot2.omadm.MOManager;
69import com.android.server.wifi.hotspot2.pps.Credential;
70import com.android.server.wifi.hotspot2.pps.HomeSP;
71
72import java.io.BufferedReader;
73import java.io.BufferedInputStream;
74import java.io.DataInputStream;
75import java.io.DataOutputStream;
76import java.io.EOFException;
77import java.io.File;
78import java.io.FileDescriptor;
79import java.io.FileInputStream;
80import java.io.FileNotFoundException;
81import java.io.FileReader;
82import java.io.IOException;
83import java.io.PrintWriter;
84import java.math.BigInteger;
85import java.nio.charset.Charset;
86import java.security.PrivateKey;
87import java.security.cert.Certificate;
88import java.security.cert.CertificateException;
89import java.text.SimpleDateFormat;
90import java.text.DateFormat;
91import java.util.concurrent.atomic.AtomicBoolean;
92import java.util.concurrent.atomic.AtomicInteger;
93import java.util.regex.Matcher;
94import java.util.regex.Pattern;
95import java.util.*;
96import java.util.zip.Checksum;
97import java.util.zip.CRC32;
98
99
100/**
101 * This class provides the API to manage configured
102 * wifi networks. The API is not thread safe is being
103 * used only from WifiStateMachine.
104 *
105 * It deals with the following
106 * - Add/update/remove a WifiConfiguration
107 *   The configuration contains two types of information.
108 *     = IP and proxy configuration that is handled by WifiConfigStore and
109 *       is saved to disk on any change.
110 *
111 *       The format of configuration file is as follows:
112 *       <version>
113 *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
114 *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
115 *       ..
116 *
117 *       (key, value) pairs for a given network are grouped together and can
118 *       be in any order. A EOS at the end of a set of (key, value) pairs
119 *       indicates that the next set of (key, value) pairs are for a new
120 *       network. A network is identified by a unique ID_KEY. If there is no
121 *       ID_KEY in the (key, value) pairs, the data is discarded.
122 *
123 *       An invalid version on read would result in discarding the contents of
124 *       the file. On the next write, the latest version is written to file.
125 *
126 *       Any failures during read or write to the configuration file are ignored
127 *       without reporting to the user since the likelihood of these errors are
128 *       low and the impact on connectivity is low.
129 *
130 *     = SSID & security details that is pushed to the supplicant.
131 *       supplicant saves these details to the disk on calling
132 *       saveConfigCommand().
133 *
134 *       We have two kinds of APIs exposed:
135 *        > public API calls that provide fine grained control
136 *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
137 *          removeNetwork(). For these calls, the config is not persisted
138 *          to the disk. (TODO: deprecate these calls in WifiManager)
139 *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
140 *          These calls persist the supplicant config to disk.
141 *
142 * - Maintain a list of configured networks for quick access
143 *
144 */
145public class WifiConfigStore extends IpConfigStore {
146
147    private Context mContext;
148    private static final String TAG = "WifiConfigStore";
149    private static final boolean DBG = true;
150    private static boolean VDBG = false;
151    private static boolean VVDBG = false;
152
153    private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
154    private static final String PPS_FILE = "/data/misc/wifi/PerProviderSubscription.conf";
155
156    /* configured networks with network id as the key */
157    private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
158            new HashMap<Integer, WifiConfiguration>();
159
160    /* A network id is a unique identifier for a network configured in the
161     * supplicant. Network ids are generated when the supplicant reads
162     * the configuration file at start and can thus change for networks.
163     * We store the IP configuration for networks along with a unique id
164     * that is generated from SSID and security type of the network. A mapping
165     * from the generated unique id to network id of the network is needed to
166     * map supplicant config to IP configuration. */
167    private HashMap<Integer, Integer> mNetworkIds =
168            new HashMap<Integer, Integer>();
169
170
171    /* Stores a map from NetworkId to HomeSP - homeSP being a specification of
172     * passpoint network. This helps tie the triplet { WifiConfiguration, NetworkId, HomeSP }
173     * which should be used in conjunction with each other
174     */
175    private HashMap<Integer, HomeSP> mConfiguredHomeSPs = new HashMap<Integer, HomeSP>();
176
177
178    static class ScanDetailCache {
179        private WifiConfiguration mConfig;
180        private HashMap<String, ScanDetail> mMap;
181
182        ScanDetailCache(WifiConfiguration config) {
183            mConfig = config;
184            mMap = new HashMap();
185        }
186
187        void put(ScanDetail scanDetail) {
188            mMap.put(scanDetail.getBSSIDString(), scanDetail);
189        }
190
191        ScanResult get(String bssid) {
192            ScanDetail scanDetail = getScanDetail(bssid);
193            return scanDetail == null ? null : scanDetail.getScanResult();
194        }
195
196        ScanDetail getScanDetail(String bssid) {
197            return mMap.get(bssid);
198        }
199
200        void remove(String bssid) {
201            mMap.remove(bssid);
202        }
203
204        int size() {
205            return mMap.size();
206        }
207
208        boolean isEmpty() {
209            return size() == 0;
210        }
211
212        ScanDetail getFirst() {
213            Iterator<ScanDetail> it = mMap.values().iterator();
214            return it.hasNext() ? it.next() : null;
215        }
216
217        Collection<String> keySet() {
218            return mMap.keySet();
219        }
220
221        Collection<ScanDetail> values() {
222            return mMap.values();
223        }
224
225        public void trim(int num) {
226            int currentSize = mMap.size();
227            if (currentSize <= num) {
228                return; // Nothing to trim
229            }
230            ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values());
231            if (list.size() != 0) {
232                // Sort by descending timestamp
233                Collections.sort(list, new Comparator() {
234                    public int compare(Object o1, Object o2) {
235                        ScanDetail a = (ScanDetail)o1;
236                        ScanDetail b = (ScanDetail)o2;
237                        if (a.getSeen() > b.getSeen()) {
238                            return 1;
239                        }
240                        if (a.getSeen() < b.getSeen()) {
241                            return -1;
242                        }
243                        return a.getBSSIDString().compareTo(b.getBSSIDString());
244                    }
245                });
246            }
247            for (int i = 0; i < currentSize - num ; i++) {
248                // Remove oldest results from scan cache
249                ScanDetail result = list.get(i);
250                mMap.remove(result.getBSSIDString());
251            }
252        }
253
254        /* @hide */
255        private ArrayList<ScanDetail> sort() {
256            ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values());
257            if (list.size() != 0) {
258                Collections.sort(list, new Comparator() {
259                    public int compare(Object o1, Object o2) {
260                        ScanResult a = ((ScanDetail)o1).getScanResult();
261                        ScanResult b = ((ScanDetail)o2).getScanResult();
262                        if (a.numIpConfigFailures > b.numIpConfigFailures) {
263                            return 1;
264                        }
265                        if (a.numIpConfigFailures < b.numIpConfigFailures) {
266                            return -1;
267                        }
268                        if (a.seen > b.seen) {
269                            return -1;
270                        }
271                        if (a.seen < b.seen) {
272                            return 1;
273                        }
274                        if (a.level > b.level) {
275                            return -1;
276                        }
277                        if (a.level < b.level) {
278                            return 1;
279                        }
280                        return a.BSSID.compareTo(b.BSSID);
281                    }
282                });
283            }
284            return list;
285        }
286
287        public WifiConfiguration.Visibility getVisibility(long age) {
288            WifiConfiguration.Visibility status = new WifiConfiguration.Visibility();
289
290            long now_ms = System.currentTimeMillis();
291            for(ScanDetail scanDetail : values()) {
292                ScanResult result = scanDetail.getScanResult();
293                if (scanDetail.getSeen() == 0)
294                    continue;
295
296                if (result.is5GHz()) {
297                    //strictly speaking: [4915, 5825]
298                    //number of known BSSID on 5GHz band
299                    status.num5 = status.num5 + 1;
300                } else if (result.is24GHz()) {
301                    //strictly speaking: [2412, 2482]
302                    //number of known BSSID on 2.4Ghz band
303                    status.num24 = status.num24 + 1;
304                }
305
306                if ((now_ms - result.seen) > age) continue;
307
308                if (result.is5GHz()) {
309                    if (result.level > status.rssi5) {
310                        status.rssi5 = result.level;
311                        status.age5 = result.seen;
312                        status.BSSID5 = result.BSSID;
313                    }
314                } else if (result.is24GHz()) {
315                    if (result.level > status.rssi24) {
316                        status.rssi24 = result.level;
317                        status.age24 = result.seen;
318                        status.BSSID24 = result.BSSID;
319                    }
320                }
321            }
322
323            return status;
324        }
325
326
327
328        @Override
329        public String toString() {
330            StringBuilder sbuf = new StringBuilder();
331            sbuf.append("Scan Cache:  ").append('\n');
332
333            ArrayList<ScanDetail> list = sort();
334            long now_ms = System.currentTimeMillis();
335            if (list.size() > 0) {
336                for (ScanDetail scanDetail : list) {
337                    ScanResult result = scanDetail.getScanResult();
338                    long milli = now_ms - scanDetail.getSeen();
339                    long ageSec = 0;
340                    long ageMin = 0;
341                    long ageHour = 0;
342                    long ageMilli = 0;
343                    long ageDay = 0;
344                    if (now_ms > scanDetail.getSeen() && scanDetail.getSeen() > 0) {
345                        ageMilli = milli % 1000;
346                        ageSec   = (milli / 1000) % 60;
347                        ageMin   = (milli / (60*1000)) % 60;
348                        ageHour  = (milli / (60*60*1000)) % 24;
349                        ageDay   = (milli / (24*60*60*1000));
350                    }
351                    sbuf.append("{").append(result.BSSID).append(",").append(result.frequency);
352                    sbuf.append(",").append(String.format("%3d", result.level));
353                    if (result.autoJoinStatus > 0) {
354                        sbuf.append(",st=").append(result.autoJoinStatus);
355                    }
356                    if (ageSec > 0 || ageMilli > 0) {
357                        sbuf.append(String.format(",%4d.%02d.%02d.%02d.%03dms", ageDay,
358                                ageHour, ageMin, ageSec, ageMilli));
359                    }
360                    if (result.numIpConfigFailures > 0) {
361                        sbuf.append(",ipfail=");
362                        sbuf.append(result.numIpConfigFailures);
363                    }
364                    sbuf.append("} ");
365                }
366                sbuf.append('\n');
367            }
368
369            return sbuf.toString();
370        }
371
372    }
373
374
375    /* Stores a map of NetworkId to ScanCache */
376    private HashMap<Integer, ScanDetailCache> mScanDetailCaches;
377
378    /**
379     * Framework keeps a list of (the CRC32 hashes of) all SSIDs that where deleted by user,
380     * so as, framework knows not to re-add those SSIDs automatically to the Saved networks
381     */
382    private Set<Long> mDeletedSSIDs = new HashSet<Long>();
383
384    /**
385     * Framework keeps a list of ephemeral SSIDs that where deleted by user,
386     * so as, framework knows not to autojoin again those SSIDs based on scorer input.
387     * The list is never cleared up.
388     *
389     * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
390     */
391    public Set<String> mDeletedEphemeralSSIDs = new HashSet<String>();
392
393    /* Tracks the highest priority of configured networks */
394    private int mLastPriority = -1;
395
396    private static final String ipConfigFile = Environment.getDataDirectory() +
397            "/misc/wifi/ipconfig.txt";
398
399    private static final String networkHistoryConfigFile = Environment.getDataDirectory() +
400            "/misc/wifi/networkHistory.txt";
401
402    private static final String autoJoinConfigFile = Environment.getDataDirectory() +
403            "/misc/wifi/autojoinconfig.txt";
404
405    /* Network History Keys */
406    private static final String SSID_KEY = "SSID:  ";
407    private static final String CONFIG_KEY = "CONFIG:  ";
408    private static final String CHOICE_KEY = "CHOICE:  ";
409    private static final String LINK_KEY = "LINK:  ";
410    private static final String BSSID_KEY = "BSSID:  ";
411    private static final String BSSID_KEY_END = "/BSSID:  ";
412    private static final String RSSI_KEY = "RSSI:  ";
413    private static final String FREQ_KEY = "FREQ:  ";
414    private static final String DATE_KEY = "DATE:  ";
415    private static final String MILLI_KEY = "MILLI:  ";
416    private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI:  ";
417    private static final String NETWORK_ID_KEY = "ID:  ";
418    private static final String PRIORITY_KEY = "PRIORITY:  ";
419    private static final String DEFAULT_GW_KEY = "DEFAULT_GW:  ";
420    private static final String AUTH_KEY = "AUTH:  ";
421    private static final String SEPARATOR_KEY = "\n";
422    private static final String STATUS_KEY = "AUTO_JOIN_STATUS:  ";
423    private static final String BSSID_STATUS_KEY = "BSSID_STATUS:  ";
424    private static final String SELF_ADDED_KEY = "SELF_ADDED:  ";
425    private static final String FAILURE_KEY = "FAILURE:  ";
426    private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD:  ";
427    private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION:  ";
428    private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY:  ";
429    private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY:  ";
430    private static final String UPDATE_UID_KEY = "UPDATE_UID:  ";
431    private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS:  ";
432    private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON:  ";
433    private static final String FQDN_KEY = "FQDN:  ";
434    private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES:  ";
435    private static final String NUM_IP_CONFIG_FAILURES_KEY = "IP_CONFIG_FAILURES:  ";
436    private static final String NUM_AUTH_FAILURES_KEY = "AUTH_FAILURES:  ";
437    private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE:  ";
438    private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH:  ";
439    private static final String VALIDATED_INTERNET_ACCESS_KEY = "VALIDATED_INTERNET_ACCESS:  ";
440    private static final String NO_INTERNET_ACCESS_REPORTS_KEY = "NO_INTERNET_ACCESS_REPORTS :   ";
441    private static final String EPHEMERAL_KEY = "EPHEMERAL:   ";
442    private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION:  ";
443    private static final String DELETED_CRC32_KEY = "DELETED_CRC32:  ";
444    private static final String DELETED_EPHEMERAL_KEY = "DELETED_EPHEMERAL:  ";
445
446    private static final String JOIN_ATTEMPT_BOOST_KEY = "JOIN_ATTEMPT_BOOST:  ";
447    private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY
448            = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G:  ";
449    private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY
450            = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G:  ";
451    private static final String THRESHOLD_UNBLACKLIST_HARD_5G_KEY
452            = "THRESHOLD_UNBLACKLIST_HARD_5G:  ";
453    private static final String THRESHOLD_UNBLACKLIST_SOFT_5G_KEY
454            = "THRESHOLD_UNBLACKLIST_SOFT_5G:  ";
455    private static final String THRESHOLD_UNBLACKLIST_HARD_24G_KEY
456            = "THRESHOLD_UNBLACKLIST_HARD_24G:  ";
457    private static final String THRESHOLD_UNBLACKLIST_SOFT_24G_KEY
458            = "THRESHOLD_UNBLACKLIST_SOFT_24G:  ";
459    private static final String THRESHOLD_GOOD_RSSI_5_KEY
460            = "THRESHOLD_GOOD_RSSI_5:  ";
461    private static final String THRESHOLD_LOW_RSSI_5_KEY
462            = "THRESHOLD_LOW_RSSI_5:  ";
463    private static final String THRESHOLD_BAD_RSSI_5_KEY
464            = "THRESHOLD_BAD_RSSI_5:  ";
465    private static final String THRESHOLD_GOOD_RSSI_24_KEY
466            = "THRESHOLD_GOOD_RSSI_24:  ";
467    private static final String THRESHOLD_LOW_RSSI_24_KEY
468            = "THRESHOLD_LOW_RSSI_24:  ";
469    private static final String THRESHOLD_BAD_RSSI_24_KEY
470            = "THRESHOLD_BAD_RSSI_24:  ";
471
472    private static final String THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY
473            = "THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING:   ";
474    private static final String THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY
475            = "THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING:   ";
476
477    private static final String THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY
478            = "THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS:   ";
479    private static final String THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY
480            = "THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS:   ";
481
482    private static final String THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY
483            = "THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS:   ";
484    private static final String THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY
485            = "THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS:   ";
486
487    private static final String MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY
488            = "MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS:   ";
489    private static final String MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY
490            = "MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS:   ";
491
492    private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY =
493            "A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW:   ";
494    private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
495            "A_BAND_PREFERENCE_RSSI_THRESHOLD:   ";
496    private static final String G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
497            "G_BAND_PREFERENCE_RSSI_THRESHOLD:   ";
498
499    private static final String ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY
500            = "ENABLE_AUTOJOIN_WHILE_ASSOCIATED:   ";
501
502    private static final String ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY
503            = "ASSOCIATED_PARTIAL_SCAN_PERIOD:   ";
504    private static final String ASSOCIATED_FULL_SCAN_BACKOFF_KEY
505            = "ASSOCIATED_FULL_SCAN_BACKOFF_PERIOD:   ";
506    private static final String ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY
507            = "ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED:   ";
508    private static final String ONLY_LINK_SAME_CREDENTIAL_CONFIGURATIONS_KEY
509            = "ONLY_LINK_SAME_CREDENTIAL_CONFIGURATIONS:   ";
510
511    private static final String ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY
512            = "ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED:   ";
513
514    // The three below configurations are mainly for power stats and CPU usage tracking
515    // allowing to incrementally disable framework features
516    private static final String ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED_KEY
517            = "ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED:   ";
518    private static final String ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY
519            = "ENABLE_AUTO_JOIN_WHILE_ASSOCIATED:   ";
520    private static final String ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY
521            = "ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED:   ";
522    private static final String ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY
523            = "ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY:   ";
524
525    private static final String idStringVarName = "id_str";
526
527    // The Wifi verbose log is provided as a way to persist the verbose logging settings
528    // for testing purpose.
529    // It is not intended for normal use.
530    private static final String WIFI_VERBOSE_LOGS_KEY
531            = "WIFI_VERBOSE_LOGS:   ";
532
533    // As we keep deleted PSK WifiConfiguration for a while, the PSK of
534    // those deleted WifiConfiguration is set to this random unused PSK
535    private static final String DELETED_CONFIG_PSK = "Mjkd86jEMGn79KhKll298Uu7-deleted";
536
537    public int maxTxPacketForFullScans = 8;
538    public int maxRxPacketForFullScans = 16;
539
540    public int maxTxPacketForPartialScans = 40;
541    public int maxRxPacketForPartialScans = 80;
542
543    public int associatedFullScanMaxIntervalMilli = 300000;
544
545    // Sane value for roam blacklisting (not switching to a network if already associated)
546    // 2 days
547    public int networkSwitchingBlackListPeriodMilli = 2 * 24 * 60 * 60 * 1000;
548
549    public int bandPreferenceBoostFactor5 = 5; // Boost by 5 dB per dB above threshold
550    public int bandPreferencePenaltyFactor5 = 2; // Penalize by 2 dB per dB below threshold
551
552    public int badLinkSpeed24 = 6;
553    public int badLinkSpeed5 = 12;
554    public int goodLinkSpeed24 = 24;
555    public int goodLinkSpeed5 = 36;
556
557    public int maxAuthErrorsToBlacklist = 4;
558    public int maxConnectionErrorsToBlacklist = 4;
559    public int wifiConfigBlacklistMinTimeMilli = 1000 * 60 * 5;
560
561    // How long a disconnected config remain considered as the last user selection
562    public int wifiConfigLastSelectionHysteresis = 1000 * 60 * 3;
563
564    // Boost RSSI values of associated networks
565    public int associatedHysteresisHigh = +14;
566    public int associatedHysteresisLow = +8;
567
568    boolean showNetworks = true; // TODO set this back to false, used for debugging 17516271
569
570    public boolean roamOnAny = false;
571    public boolean onlyLinkSameCredentialConfigurations = true;
572
573    public boolean enableLinkDebouncing = true;
574    public boolean enable5GHzPreference = true;
575    public boolean enableWifiCellularHandoverUserTriggeredAdjustment = true;
576
577    public int currentNetworkBoost = 25;
578    public int scanResultRssiLevelPatchUp = -85;
579
580    public static final int maxNumScanCacheEntries = 128;
581
582
583    public final AtomicBoolean enableAutoJoinWhenAssociated = new AtomicBoolean(true);
584    public final AtomicBoolean enableFullBandScanWhenAssociated = new AtomicBoolean(true);
585    public final AtomicBoolean enableAutoJoinScanWhenAssociated = new AtomicBoolean(true);
586    public final AtomicBoolean enableChipWakeUpWhenAssociated = new AtomicBoolean(true);
587    public final AtomicBoolean enableRssiPollWhenAssociated = new AtomicBoolean(true);
588    public final AtomicInteger thresholdInitialAutoJoinAttemptMin5RSSI = new AtomicInteger(WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5);
589    public final AtomicInteger thresholdInitialAutoJoinAttemptMin24RSSI = new AtomicInteger(WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24);
590    public final AtomicInteger thresholdUnblacklistThreshold5Hard = new AtomicInteger();
591    public final AtomicInteger thresholdUnblacklistThreshold5Soft = new AtomicInteger();
592    public final AtomicInteger thresholdUnblacklistThreshold24Hard = new AtomicInteger();
593    public final AtomicInteger thresholdUnblacklistThreshold24Soft = new AtomicInteger();
594    public final AtomicInteger thresholdGoodRssi5 = new AtomicInteger(WifiConfiguration.GOOD_RSSI_5);
595    public final AtomicInteger thresholdLowRssi5 = new AtomicInteger(WifiConfiguration.LOW_RSSI_5);
596    public final AtomicInteger thresholdBadRssi5 = new AtomicInteger(WifiConfiguration.BAD_RSSI_5);
597    public final AtomicInteger thresholdGoodRssi24 = new AtomicInteger(WifiConfiguration.GOOD_RSSI_24);
598    public final AtomicInteger thresholdLowRssi24 = new AtomicInteger(WifiConfiguration.LOW_RSSI_24);
599    public final AtomicInteger thresholdBadRssi24 = new AtomicInteger(WifiConfiguration.BAD_RSSI_24);
600    public final AtomicInteger maxTxPacketForNetworkSwitching = new AtomicInteger(40);
601    public final AtomicInteger maxRxPacketForNetworkSwitching = new AtomicInteger(80);
602    public final AtomicInteger enableVerboseLogging = new AtomicInteger(0);
603    public final AtomicInteger bandPreferenceBoostThreshold5 = new AtomicInteger(WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD);
604    public final AtomicInteger associatedPartialScanPeriodMilli = new AtomicInteger();
605    public final AtomicInteger associatedFullScanBackoff = new AtomicInteger(12); // Will be divided by 8 by WifiStateMachine
606    public final AtomicInteger bandPreferencePenaltyThreshold5 = new AtomicInteger(WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD);
607    public final AtomicInteger alwaysEnableScansWhileAssociated = new AtomicInteger(0);
608    public final AtomicInteger maxNumPassiveChannelsForPartialScans = new AtomicInteger(2);
609    public final AtomicInteger maxNumActiveChannelsForPartialScans = new AtomicInteger(6);
610
611    private static final Map<String, Object> sKeyMap = new HashMap<>();
612
613    /**
614     * Regex pattern for extracting a connect choice.
615     * Matches a strings like the following:
616     * <configKey>=([0:9]+)
617     */
618    private static Pattern mConnectChoice =
619            Pattern.compile("(.*)=([0-9]+)");
620
621
622    /* Enterprise configuration keys */
623    /**
624     * In old configurations, the "private_key" field was used. However, newer
625     * configurations use the key_id field with the engine_id set to "keystore".
626     * If this field is found in the configuration, the migration code is
627     * triggered.
628     */
629    public static final String OLD_PRIVATE_KEY_NAME = "private_key";
630
631    /**
632     * This represents an empty value of an enterprise field.
633     * NULL is used at wpa_supplicant to indicate an empty value
634     */
635    static final String EMPTY_VALUE = "NULL";
636
637    // Internal use only
638    private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] {
639            WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY,
640            WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY,
641            WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY,
642            WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY,
643            WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
644            WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY };
645
646
647    /**
648     * If Connectivity Service has triggered an unwanted network disconnect
649     */
650    public long lastUnwantedNetworkDisconnectTimestamp = 0;
651
652    /**
653     * The maximum number of times we will retry a connection to an access point
654     * for which we have failed in acquiring an IP address from DHCP. A value of
655     * N means that we will make N+1 connection attempts in all.
656     * <p>
657     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
658     * value if a Settings value is not present.
659     */
660    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
661
662
663    private final LocalLog mLocalLog;
664    private final WpaConfigFileObserver mFileObserver;
665
666    private WifiNative mWifiNative;
667    private final KeyStore mKeyStore = KeyStore.getInstance();
668
669    /**
670     * The lastSelectedConfiguration is used to remember which network
671     * was selected last by the user.
672     * The connection to this network may not be successful, as well
673     * the selection (i.e. network priority) might not be persisted.
674     * WiFi state machine is the only object that sets this variable.
675     */
676    private String lastSelectedConfiguration = null;
677
678    private final AnqpCache mAnqpCache;
679    private final SupplicantBridge mSupplicantBridge;
680
681    WifiConfigStore(Context c, WifiNative wn) {
682        mContext = c;
683        mWifiNative = wn;
684
685        // A map for value setting in readAutoJoinConfig() - replacing the replicated code.
686        sKeyMap.put(ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY, enableAutoJoinWhenAssociated);
687        sKeyMap.put(ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY, enableFullBandScanWhenAssociated);
688        sKeyMap.put(ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED_KEY, enableAutoJoinScanWhenAssociated);
689        sKeyMap.put(ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY, enableChipWakeUpWhenAssociated);
690        sKeyMap.put(ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY, enableRssiPollWhenAssociated);
691        sKeyMap.put(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY, thresholdInitialAutoJoinAttemptMin5RSSI);
692        sKeyMap.put(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY, thresholdInitialAutoJoinAttemptMin24RSSI);
693        sKeyMap.put(THRESHOLD_UNBLACKLIST_HARD_5G_KEY, thresholdUnblacklistThreshold5Hard);
694        sKeyMap.put(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY, thresholdUnblacklistThreshold5Soft);
695        sKeyMap.put(THRESHOLD_UNBLACKLIST_HARD_24G_KEY, thresholdUnblacklistThreshold24Hard);
696        sKeyMap.put(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY, thresholdUnblacklistThreshold24Soft);
697        sKeyMap.put(THRESHOLD_GOOD_RSSI_5_KEY, thresholdGoodRssi5);
698        sKeyMap.put(THRESHOLD_LOW_RSSI_5_KEY, thresholdLowRssi5);
699        sKeyMap.put(THRESHOLD_BAD_RSSI_5_KEY, thresholdBadRssi5);
700        sKeyMap.put(THRESHOLD_GOOD_RSSI_24_KEY, thresholdGoodRssi24);
701        sKeyMap.put(THRESHOLD_LOW_RSSI_24_KEY, thresholdLowRssi24);
702        sKeyMap.put(THRESHOLD_BAD_RSSI_24_KEY, thresholdBadRssi24);
703        sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY, maxTxPacketForNetworkSwitching);
704        sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY, maxRxPacketForNetworkSwitching);
705        sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY, maxTxPacketForNetworkSwitching);
706        sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY, maxRxPacketForNetworkSwitching);
707        sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY, maxTxPacketForNetworkSwitching);
708        sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY, maxRxPacketForNetworkSwitching);
709        sKeyMap.put(WIFI_VERBOSE_LOGS_KEY, enableVerboseLogging);
710        sKeyMap.put(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, bandPreferenceBoostThreshold5);
711        sKeyMap.put(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY, associatedPartialScanPeriodMilli);
712        sKeyMap.put(ASSOCIATED_FULL_SCAN_BACKOFF_KEY, associatedFullScanBackoff);
713        sKeyMap.put(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, bandPreferencePenaltyThreshold5);
714        sKeyMap.put(ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY, alwaysEnableScansWhileAssociated);
715        sKeyMap.put(MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, maxNumPassiveChannelsForPartialScans);
716        sKeyMap.put(MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, maxNumActiveChannelsForPartialScans);
717
718        if (showNetworks) {
719            mLocalLog = mWifiNative.getLocalLog();
720            mFileObserver = new WpaConfigFileObserver();
721            mFileObserver.startWatching();
722        } else {
723            mLocalLog = null;
724            mFileObserver = null;
725        }
726
727        associatedPartialScanPeriodMilli.set(mContext.getResources().getInteger(
728                R.integer.config_wifi_framework_associated_scan_interval));
729        loge("associatedPartialScanPeriodMilli set to " + associatedPartialScanPeriodMilli);
730
731        onlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
732                R.bool.config_wifi_only_link_same_credential_configurations);
733        maxNumActiveChannelsForPartialScans.set(mContext.getResources().getInteger(
734                R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels));
735        maxNumPassiveChannelsForPartialScans.set(mContext.getResources().getInteger(
736                R.integer.config_wifi_framework_associated_partial_scan_max_num_passive_channels));
737        associatedFullScanMaxIntervalMilli = mContext.getResources().getInteger(
738                R.integer.config_wifi_framework_associated_full_scan_max_interval);
739        associatedFullScanBackoff.set(mContext.getResources().getInteger(
740                R.integer.config_wifi_framework_associated_full_scan_backoff));
741        enableLinkDebouncing = mContext.getResources().getBoolean(
742                R.bool.config_wifi_enable_disconnection_debounce);
743
744        enable5GHzPreference = mContext.getResources().getBoolean(
745                R.bool.config_wifi_enable_5GHz_preference);
746
747        bandPreferenceBoostFactor5 = mContext.getResources().getInteger(
748                R.integer.config_wifi_framework_5GHz_preference_boost_factor);
749        bandPreferencePenaltyFactor5 = mContext.getResources().getInteger(
750                R.integer.config_wifi_framework_5GHz_preference_penalty_factor);
751
752        bandPreferencePenaltyThreshold5.set(mContext.getResources().getInteger(
753                R.integer.config_wifi_framework_5GHz_preference_penalty_threshold));
754        bandPreferenceBoostThreshold5.set(mContext.getResources().getInteger(
755                R.integer.config_wifi_framework_5GHz_preference_boost_threshold));
756
757        associatedHysteresisHigh = mContext.getResources().getInteger(
758                R.integer.config_wifi_framework_current_association_hysteresis_high);
759        associatedHysteresisLow = mContext.getResources().getInteger(
760                R.integer.config_wifi_framework_current_association_hysteresis_low);
761
762        thresholdBadRssi5.set(mContext.getResources().getInteger(
763                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz));
764        thresholdLowRssi5.set(mContext.getResources().getInteger(
765                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz));
766        thresholdGoodRssi5.set(mContext.getResources().getInteger(
767                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz));
768        thresholdBadRssi24.set(mContext.getResources().getInteger(
769                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz));
770        thresholdLowRssi24.set(mContext.getResources().getInteger(
771                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz));
772        thresholdGoodRssi24.set(mContext.getResources().getInteger(
773                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz));
774
775        enableWifiCellularHandoverUserTriggeredAdjustment = mContext.getResources().getBoolean(
776                R.bool.config_wifi_framework_cellular_handover_enable_user_triggered_adjustment);
777
778        badLinkSpeed24 = mContext.getResources().getInteger(
779                R.integer.config_wifi_framework_wifi_score_bad_link_speed_24);
780        badLinkSpeed5 = mContext.getResources().getInteger(
781                R.integer.config_wifi_framework_wifi_score_bad_link_speed_5);
782        goodLinkSpeed24 = mContext.getResources().getInteger(
783                R.integer.config_wifi_framework_wifi_score_good_link_speed_24);
784        goodLinkSpeed5 = mContext.getResources().getInteger(
785                R.integer.config_wifi_framework_wifi_score_good_link_speed_5);
786
787        maxAuthErrorsToBlacklist = mContext.getResources().getInteger(
788                R.integer.config_wifi_framework_max_auth_errors_to_blacklist);
789        maxConnectionErrorsToBlacklist = mContext.getResources().getInteger(
790                R.integer.config_wifi_framework_max_connection_errors_to_blacklist);
791        wifiConfigBlacklistMinTimeMilli = mContext.getResources().getInteger(
792                R.integer.config_wifi_framework_network_black_list_min_time_milli);
793
794
795        enableAutoJoinScanWhenAssociated.set(mContext.getResources().getBoolean(
796                R.bool.config_wifi_framework_enable_associated_autojoin_scan));
797
798        enableAutoJoinWhenAssociated.set(mContext.getResources().getBoolean(
799                R.bool.config_wifi_framework_enable_associated_network_selection));
800
801        currentNetworkBoost = mContext.getResources().getInteger(
802                R.integer.config_wifi_framework_current_network_boost);
803
804        scanResultRssiLevelPatchUp = mContext.getResources().getInteger(
805                R.integer.config_wifi_framework_scan_result_rssi_level_patchup_value);
806
807        networkSwitchingBlackListPeriodMilli = mContext.getResources().getInteger(
808                R.integer.config_wifi_network_switching_blacklist_time);
809
810        Chronograph chronograph = new Chronograph();
811        mAnqpCache = new AnqpCache(chronograph);
812        mSupplicantBridge = new SupplicantBridge(mWifiNative, this);
813        mScanDetailCaches = new HashMap();
814    }
815
816    void enableVerboseLogging(int verbose) {
817        enableVerboseLogging.set(verbose);
818        if (verbose > 0) {
819            VDBG = true;
820            showNetworks = true;
821        } else {
822            VDBG = false;
823        }
824        if (verbose > 1) {
825            VVDBG = true;
826        } else {
827            VVDBG = false;
828        }
829    }
830
831    class WpaConfigFileObserver extends FileObserver {
832
833        public WpaConfigFileObserver() {
834            super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
835        }
836
837        @Override
838        public void onEvent(int event, String path) {
839            if (event == CLOSE_WRITE) {
840                File file = new File(SUPPLICANT_CONFIG_FILE);
841                if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
842            }
843        }
844    }
845
846
847    /**
848     * Fetch the list of configured networks
849     * and enable all stored networks in supplicant.
850     */
851    void loadAndEnableAllNetworks() {
852        if (DBG) log("Loading config and enabling all networks ");
853        loadConfiguredNetworks();
854        enableAllNetworks();
855    }
856
857    int getConfiguredNetworksSize() {
858        return mConfiguredNetworks.size();
859    }
860
861    private List<WifiConfiguration> getConfiguredNetworks(Map<String, String> pskMap) {
862        List<WifiConfiguration> networks = new ArrayList<>();
863        for(WifiConfiguration config : mConfiguredNetworks.values()) {
864            WifiConfiguration newConfig = new WifiConfiguration(config);
865            // When updating this condition, update WifiStateMachine's CONNECT_NETWORK handler to
866            // correctly handle updating existing configs that are filtered out here.
867            if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || config.ephemeral) {
868                // Do not enumerate and return this configuration to any one,
869                // for instance WiFi Picker.
870                // instead treat it as unknown. the configuration can still be retrieved
871                // directly by the key or networkId
872                continue;
873            }
874
875            if (pskMap != null && config.allowedKeyManagement != null
876                    && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
877                    && pskMap.containsKey(config.SSID)) {
878                newConfig.preSharedKey = pskMap.get(config.SSID);
879            }
880            networks.add(newConfig);
881        }
882        return networks;
883    }
884
885    /**
886     * Fetch the list of currently configured networks
887     * @return List of networks
888     */
889    List<WifiConfiguration> getConfiguredNetworks() {
890        return getConfiguredNetworks(null);
891    }
892
893    /**
894     * Fetch the list of currently configured networks, filled with real preSharedKeys
895     * @return List of networks
896     */
897    List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
898        Map<String, String> pskMap = getCredentialsBySsidMap();
899        return getConfiguredNetworks(pskMap);
900    }
901
902    /**
903     * Fetch the preSharedKeys for all networks.
904     * @return a map from Ssid to preSharedKey.
905     */
906    private Map<String, String> getCredentialsBySsidMap() {
907        return readNetworkVariablesFromSupplicantFile("psk");
908    }
909
910    int getConfiguredNetworkSize() {
911        if (mConfiguredNetworks == null)
912            return 0;
913        return mConfiguredNetworks.size();
914    }
915
916    /**
917     * Fetch the list of currently configured networks that were recently seen
918     *
919     * @return List of networks
920     */
921    List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) {
922        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
923
924        for (WifiConfiguration config : mConfiguredNetworks.values()) {
925            if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || config.ephemeral) {
926                // Do not enumerate and return this configuration to any one,
927                // instead treat it as unknown. the configuration can still be retrieved
928                // directly by the key or networkId
929                continue;
930            }
931
932            // Calculate the RSSI for scan results that are more recent than milli
933            ScanDetailCache cache = getScanDetailCache(config);
934            if (cache == null) {
935                continue;
936            }
937            config.setVisibility(cache.getVisibility(milli));
938            if (config.visibility == null) {
939                continue;
940            }
941            if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI &&
942                    config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) {
943                continue;
944            }
945            if (copy) {
946                networks.add(new WifiConfiguration(config));
947            } else {
948                networks.add(config);
949            }
950        }
951        return networks;
952    }
953
954    /**
955     *  Update the configuration and BSSID with latest RSSI value.
956     */
957    void updateConfiguration(WifiInfo info) {
958        WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
959        if (config != null && getScanDetailCache(config) != null) {
960            ScanDetail scanDetail = getScanDetailCache(config).getScanDetail(info.getBSSID());
961            if (scanDetail != null) {
962                ScanResult result = scanDetail.getScanResult();
963                long previousSeen = result.seen;
964                int previousRssi = result.level;
965
966                // Update the scan result
967                scanDetail.setSeen();
968                result.level = info.getRssi();
969
970                // Average the RSSI value
971                result.averageRssi(previousRssi, previousSeen,
972                        WifiAutoJoinController.mScanResultMaximumAge);
973                if (VDBG) {
974                    loge("updateConfiguration freq=" + result.frequency
975                        + " BSSID=" + result.BSSID
976                        + " RSSI=" + result.level
977                        + " " + config.configKey());
978                }
979            }
980        }
981    }
982
983    /**
984     * get the Wificonfiguration for this netId
985     *
986     * @return Wificonfiguration
987     */
988    WifiConfiguration getWifiConfiguration(int netId) {
989        if (mConfiguredNetworks == null)
990            return null;
991        return mConfiguredNetworks.get(netId);
992    }
993
994    /**
995     * Get the Wificonfiguration for this key
996     * @return Wificonfiguration
997     */
998    WifiConfiguration getWifiConfiguration(String key) {
999        if (key == null)
1000            return null;
1001        int hash = key.hashCode();
1002        if (mNetworkIds == null)
1003            return null;
1004        Integer n = mNetworkIds.get(hash);
1005        if (n == null)
1006            return null;
1007        int netId = n.intValue();
1008        return getWifiConfiguration(netId);
1009    }
1010
1011    /**
1012     * Enable all networks and save config. This will be a no-op if the list
1013     * of configured networks indicates all networks as being enabled
1014     */
1015    void enableAllNetworks() {
1016        long now = System.currentTimeMillis();
1017        boolean networkEnabledStateChanged = false;
1018
1019        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1020
1021            if(config != null && config.status == Status.DISABLED && !config.ephemeral
1022                    && (config.autoJoinStatus
1023                    <= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)) {
1024
1025                // Wait for 5 minutes before reenabling config that have known, repeated connection
1026                // or DHCP failures
1027                if (config.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE
1028                        || config.disableReason == WifiConfiguration.DISABLED_ASSOCIATION_REJECT
1029                        || config.disableReason == WifiConfiguration.DISABLED_AUTH_FAILURE) {
1030                    if (config.blackListTimestamp != 0
1031                           && now > config.blackListTimestamp
1032                           && (now - config.blackListTimestamp) < wifiConfigBlacklistMinTimeMilli) {
1033                        continue;
1034                    }
1035                }
1036
1037                if(mWifiNative.enableNetwork(config.networkId, false)) {
1038                    networkEnabledStateChanged = true;
1039                    config.status = Status.ENABLED;
1040
1041                    // Reset the blacklist condition
1042                    config.numConnectionFailures = 0;
1043                    config.numIpConfigFailures = 0;
1044                    config.numAuthFailures = 0;
1045
1046                    // Reenable the wifi configuration
1047                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
1048                } else {
1049                    loge("Enable network failed on " + config.networkId);
1050                }
1051            }
1052        }
1053
1054        if (networkEnabledStateChanged) {
1055            mWifiNative.saveConfig();
1056            sendConfiguredNetworksChangedBroadcast();
1057        }
1058    }
1059
1060    private boolean setNetworkPriorityNative(int netId, int priority) {
1061        return mWifiNative.setNetworkVariable(netId,
1062                WifiConfiguration.priorityVarName, Integer.toString(priority));
1063    }
1064
1065    private boolean setSSIDNative(int netId, String ssid) {
1066        return mWifiNative.setNetworkVariable(netId, WifiConfiguration.ssidVarName,
1067                encodeSSID(ssid));
1068    }
1069
1070    /**
1071     * Selects the specified network for connection. This involves
1072     * updating the priority of all the networks and enabling the given
1073     * network while disabling others.
1074     *
1075     * Selecting a network will leave the other networks disabled and
1076     * a call to enableAllNetworks() needs to be issued upon a connection
1077     * or a failure event from supplicant
1078     *
1079     * @param netId network to select for connection
1080     * @return false if the network id is invalid
1081     */
1082    boolean selectNetwork(WifiConfiguration config, boolean updatePriorities) {
1083        if (VDBG) localLog("selectNetwork", config.networkId);
1084        if (config.networkId == INVALID_NETWORK_ID) return false;
1085
1086        // Reset the priority of each network at start or if it goes too high.
1087        boolean saveNetworkHistory = updatePriorities;
1088        if (mLastPriority == -1 || mLastPriority > 1000000) {
1089            for(WifiConfiguration config2 : mConfiguredNetworks.values()) {
1090                if (updatePriorities) {
1091                    if (config2.networkId != INVALID_NETWORK_ID) {
1092                        config2.priority = 0;
1093                        setNetworkPriorityNative(config2.networkId, config.priority);
1094                    }
1095                }
1096                if (config2.dirty) {
1097                    saveNetworkHistory = true;
1098                }
1099            }
1100            mLastPriority = 0;
1101        }
1102
1103        // Set to the highest priority and save the configuration.
1104        if (updatePriorities) {
1105            config.priority = ++mLastPriority;
1106            setNetworkPriorityNative(config.networkId, config.priority);
1107        }
1108
1109        if (config.isPasspoint()) {
1110            /* need to slap on the SSID of selected bssid to work */
1111            if (getScanDetailCache(config).size() != 0) {
1112                ScanDetail result = getScanDetailCache(config).getFirst();
1113                if (result == null) {
1114                    loge("Could not find scan result for " + config.BSSID);
1115                } else {
1116                    log("Setting SSID for " + config.networkId + " to" + result.getSSID());
1117                    setSSIDNative(config.networkId, result.getSSID());
1118                }
1119
1120            } else {
1121                loge("Could not find bssid for " + config);
1122            }
1123        }
1124
1125        if (updatePriorities)
1126            mWifiNative.saveConfig();
1127        else
1128            mWifiNative.selectNetwork(config.networkId);
1129
1130        if (saveNetworkHistory) {
1131            /* TODO: we should remove this from here; selectNetwork *
1132             * shouldn't have this side effect of saving data       */
1133            writeKnownNetworkHistory(false);
1134        }
1135
1136        /* Enable the given network while disabling all other networks */
1137        enableNetworkWithoutBroadcast(config.networkId, true);
1138
1139       /* Avoid saving the config & sending a broadcast to prevent settings
1140        * from displaying a disabled list of networks */
1141        return true;
1142    }
1143
1144    /**
1145     * Add/update the specified configuration and save config
1146     *
1147     * @param config WifiConfiguration to be saved
1148     * @return network update result
1149     */
1150    NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) {
1151        WifiConfiguration conf;
1152
1153        // A new network cannot have null SSID
1154        if (config == null || (config.networkId == INVALID_NETWORK_ID &&
1155                config.SSID == null)) {
1156            return new NetworkUpdateResult(INVALID_NETWORK_ID);
1157        }
1158        if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);
1159        if (VDBG) {
1160            loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()
1161                    + " SSID=" + config.SSID
1162                    + " Uid=" + Integer.toString(config.creatorUid)
1163                    + "/" + Integer.toString(config.lastUpdateUid));
1164        }
1165
1166        if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
1167            if (VDBG) {
1168                loge("WifiConfigStore: removed from ephemeral blacklist: " + config.SSID);
1169            }
1170            // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call
1171            // below, since we're creating/modifying a config.
1172        }
1173
1174        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
1175        NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
1176        int netId = result.getNetworkId();
1177
1178        if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);
1179
1180        /* enable a new network */
1181        if (newNetwork && netId != INVALID_NETWORK_ID) {
1182            if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);
1183
1184            mWifiNative.enableNetwork(netId, false);
1185            conf = mConfiguredNetworks.get(netId);
1186            if (conf != null)
1187                conf.status = Status.ENABLED;
1188        }
1189
1190        conf = mConfiguredNetworks.get(netId);
1191        if (conf != null) {
1192            if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
1193                if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);
1194
1195                // reenable autojoin, since new information has been provided
1196                conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
1197                enableNetworkWithoutBroadcast(conf.networkId, false);
1198            }
1199            if (VDBG) {
1200                loge("WifiConfigStore: saveNetwork got config back netId="
1201                        + Integer.toString(netId)
1202                        + " uid=" + Integer.toString(config.creatorUid));
1203            }
1204        }
1205
1206        mWifiNative.saveConfig();
1207        sendConfiguredNetworksChangedBroadcast(conf, result.isNewNetwork() ?
1208                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1209        return result;
1210    }
1211
1212    /**
1213     * Firmware is roaming away from this BSSID, and this BSSID was on 5GHz, and it's RSSI was good,
1214     * this means we have a situation where we would want to remain on this BSSID but firmware
1215     * is not successful at it.
1216     * This situation is observed on a small number of Access Points, b/17960587
1217     * In that situation, blacklist this BSSID really hard so as framework will not attempt to
1218     * roam to it for the next 8 hours. We do not to keep flipping between 2.4 and 5GHz band..
1219     * TODO: review the blacklisting strategy so as to make it softer and adaptive
1220     * @param info
1221     */
1222    void driverRoamedFrom(WifiInfo info) {
1223        if (info != null
1224            && info.getBSSID() != null
1225            && ScanResult.is5GHz(info.getFrequency())
1226            && info.getRssi() > (bandPreferenceBoostThreshold5.get() + 3)) {
1227            WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
1228            if (config != null) {
1229                if (getScanDetailCache(config) != null) {
1230                    ScanResult result = getScanDetailCache(config).get(info.getBSSID());
1231                    if (result != null) {
1232                        result.setAutoJoinStatus(ScanResult.AUTO_ROAM_DISABLED + 1);
1233                    }
1234                }
1235            }
1236        }
1237    }
1238
1239    void noteRoamingFailure(WifiConfiguration config, int reason) {
1240        if (config == null) return;
1241        config.lastRoamingFailure = System.currentTimeMillis();
1242        config.roamingFailureBlackListTimeMilli
1243                = 2 * (config.roamingFailureBlackListTimeMilli + 1000);
1244        if (config.roamingFailureBlackListTimeMilli
1245                > networkSwitchingBlackListPeriodMilli) {
1246            config.roamingFailureBlackListTimeMilli =
1247                    networkSwitchingBlackListPeriodMilli;
1248        }
1249        config.lastRoamingFailureReason = reason;
1250    }
1251
1252    void saveWifiConfigBSSID(WifiConfiguration config) {
1253        // Sanity check the config is valid
1254        if (config == null || (config.networkId == INVALID_NETWORK_ID &&
1255                config.SSID == null)) {
1256            return;
1257        }
1258
1259        // If an app specified a BSSID then dont over-write it
1260        if (config.BSSID != null && config.BSSID != "any") {
1261            return;
1262        }
1263
1264        // If autojoin specified a BSSID then write it in the network block
1265        if (config.autoJoinBSSID != null) {
1266            loge("saveWifiConfigBSSID Setting BSSID for " + config.configKey()
1267                    + " to " + config.autoJoinBSSID);
1268            if (!mWifiNative.setNetworkVariable(
1269                    config.networkId,
1270                    WifiConfiguration.bssidVarName,
1271                    config.autoJoinBSSID)) {
1272                loge("failed to set BSSID: " + config.autoJoinBSSID);
1273            } else if (config.autoJoinBSSID.equals("any")) {
1274                // Paranoia, we just want to make sure that we restore the config to normal
1275                mWifiNative.saveConfig();
1276            }
1277        }
1278    }
1279
1280
1281    void updateStatus(int netId, DetailedState state) {
1282        if (netId != INVALID_NETWORK_ID) {
1283            WifiConfiguration config = mConfiguredNetworks.get(netId);
1284            if (config == null) return;
1285            switch (state) {
1286                case CONNECTED:
1287                    config.status = Status.CURRENT;
1288                    //we successfully connected, hence remove the blacklist
1289                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
1290                    break;
1291                case DISCONNECTED:
1292                    //If network is already disabled, keep the status
1293                    if (config.status == Status.CURRENT) {
1294                        config.status = Status.ENABLED;
1295                    }
1296                    break;
1297                default:
1298                    //do nothing, retain the existing state
1299                    break;
1300            }
1301        }
1302    }
1303
1304
1305    /**
1306     * Disable an ephemeral SSID for the purpose of auto-joining thru scored.
1307     * This SSID will never be scored anymore.
1308     * The only way to "un-disable it" is if the user create a network for that SSID and then
1309     * forget it.
1310     *
1311     * @param SSID caller must ensure that the SSID passed thru this API match
1312     *            the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
1313     * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
1314     *         disconnect if this is the current network.
1315     */
1316    WifiConfiguration disableEphemeralNetwork(String SSID) {
1317        if (SSID == null) {
1318            return null;
1319        }
1320
1321        WifiConfiguration foundConfig = null;
1322
1323        mDeletedEphemeralSSIDs.add(SSID);
1324        loge("Forget ephemeral SSID " + SSID + " num=" + mDeletedEphemeralSSIDs.size());
1325
1326        for (WifiConfiguration config : mConfiguredNetworks.values()) {
1327            if (SSID.equals(config.SSID) && config.ephemeral) {
1328                loge("Found ephemeral config in disableEphemeralNetwork: " + config.networkId);
1329                foundConfig = config;
1330            }
1331        }
1332
1333        // Force a write, because the mDeletedEphemeralSSIDs list has changed even though the
1334        // configurations may not have.
1335        writeKnownNetworkHistory(true);
1336
1337        return foundConfig;
1338    }
1339
1340    /**
1341     * Forget the specified network and save config
1342     *
1343     * @param netId network to forget
1344     * @return {@code true} if it succeeds, {@code false} otherwise
1345     */
1346    boolean forgetNetwork(int netId) {
1347        if (showNetworks) localLog("forgetNetwork", netId);
1348
1349        boolean remove = removeConfigAndSendBroadcastIfNeeded(netId);
1350        if (!remove) {
1351            //success but we dont want to remove the network from supplicant conf file
1352            return true;
1353        }
1354        if (mWifiNative.removeNetwork(netId)) {
1355            mWifiNative.saveConfig();
1356            return true;
1357        } else {
1358            loge("Failed to remove network " + netId);
1359            return false;
1360        }
1361    }
1362
1363    /**
1364     * Add/update a network. Note that there is no saveConfig operation.
1365     * This function is retained for compatibility with the public
1366     * API. The more powerful saveNetwork() is used by the
1367     * state machine
1368     *
1369     * @param config wifi configuration to add/update
1370     * @return network Id
1371     */
1372    int addOrUpdateNetwork(WifiConfiguration config, int uid) {
1373        if (showNetworks) localLog("addOrUpdateNetwork id=", config.networkId);
1374        //adding unconditional message to chase b/15111865
1375        Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId)
1376                + " uid=" + Integer.toString(config.creatorUid)
1377                + "/" + Integer.toString(config.lastUpdateUid));
1378
1379        if (config.isPasspoint()) {
1380            /* create a temporary SSID with providerFriendlyName */
1381            Long csum = getChecksum(config.FQDN);
1382            config.SSID = csum.toString();
1383        }
1384
1385        NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
1386        if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
1387            WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId());
1388            if (conf != null) {
1389                sendConfiguredNetworksChangedBroadcast(conf,
1390                    result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
1391                            WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1392            }
1393        }
1394
1395        return result.getNetworkId();
1396    }
1397
1398    /**
1399     * Remove a network. Note that there is no saveConfig operation.
1400     * This function is retained for compatibility with the public
1401     * API. The more powerful forgetNetwork() is used by the
1402     * state machine for network removal
1403     *
1404     * @param netId network to be removed
1405     * @return {@code true} if it succeeds, {@code false} otherwise
1406     */
1407    boolean removeNetwork(int netId) {
1408        if (showNetworks) localLog("removeNetwork", netId);
1409        boolean ret = mWifiNative.removeNetwork(netId);
1410        if (ret) {
1411            removeConfigAndSendBroadcastIfNeeded(netId);
1412        }
1413        return ret;
1414    }
1415
1416
1417    static private Long getChecksum(String source) {
1418        Checksum csum = new CRC32();
1419        csum.update(source.getBytes(), 0, source.getBytes().length);
1420        return csum.getValue();
1421    }
1422
1423    private boolean removeConfigAndSendBroadcastIfNeeded(int netId) {
1424        WifiConfiguration config = mConfiguredNetworks.get(netId);
1425        if (config != null) {
1426            if (VDBG) {
1427                loge("removeNetwork " + Integer.toString(netId) + " key=" +
1428                        config.configKey() + " config.id=" + Integer.toString(config.networkId));
1429            }
1430
1431            // cancel the last user choice
1432            if (config.configKey().equals(lastSelectedConfiguration)) {
1433                lastSelectedConfiguration = null;
1434            }
1435
1436            // Remove any associated keys
1437            if (config.enterpriseConfig != null) {
1438                removeKeys(config.enterpriseConfig);
1439            }
1440
1441            if (config.selfAdded || config.linkedConfigurations != null
1442                    || config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1443                if (!TextUtils.isEmpty(config.SSID)) {
1444                    /* Remember that we deleted this PSK SSID */
1445                    if (config.SSID != null) {
1446                        Long csum = getChecksum(config.SSID);
1447                        mDeletedSSIDs.add(csum);
1448                        loge("removeNetwork " + Integer.toString(netId)
1449                                + " key=" + config.configKey()
1450                                + " config.id=" + Integer.toString(config.networkId)
1451                                + "  crc=" + csum);
1452                    } else {
1453                        loge("removeNetwork " + Integer.toString(netId)
1454                                + " key=" + config.configKey()
1455                                + " config.id=" + Integer.toString(config.networkId));
1456                    }
1457                }
1458            }
1459
1460            mConfiguredNetworks.remove(netId);
1461            mNetworkIds.remove(configKey(config));
1462            mScanDetailCaches.remove(netId);
1463
1464            writeIpAndProxyConfigurations();
1465            sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
1466            writeKnownNetworkHistory(true);
1467        }
1468        return true;
1469    }
1470
1471    /**
1472     * Enable a network. Note that there is no saveConfig operation.
1473     * This function is retained for compatibility with the public
1474     * API. The more powerful selectNetwork()/saveNetwork() is used by the
1475     * state machine for connecting to a network
1476     *
1477     * @param netId network to be enabled
1478     * @return {@code true} if it succeeds, {@code false} otherwise
1479     */
1480    boolean enableNetwork(int netId, boolean disableOthers) {
1481        boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
1482        if (disableOthers) {
1483            if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId);
1484            sendConfiguredNetworksChangedBroadcast();
1485        } else {
1486            if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId);
1487            WifiConfiguration enabledNetwork = null;
1488            synchronized(mConfiguredNetworks) {
1489                enabledNetwork = mConfiguredNetworks.get(netId);
1490            }
1491            // check just in case the network was removed by someone else.
1492            if (enabledNetwork != null) {
1493                sendConfiguredNetworksChangedBroadcast(enabledNetwork,
1494                        WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1495            }
1496        }
1497        return ret;
1498    }
1499
1500    boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
1501        boolean ret = mWifiNative.enableNetwork(netId, disableOthers);
1502
1503        WifiConfiguration config = mConfiguredNetworks.get(netId);
1504        if (config != null) config.status = Status.ENABLED;
1505
1506        if (disableOthers) {
1507            markAllNetworksDisabledExcept(netId);
1508        }
1509        return ret;
1510    }
1511
1512    void disableAllNetworks() {
1513        if (VDBG) localLog("disableAllNetworks");
1514        boolean networkDisabled = false;
1515        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1516            if(config != null && config.status != Status.DISABLED) {
1517                if(mWifiNative.disableNetwork(config.networkId)) {
1518                    networkDisabled = true;
1519                    config.status = Status.DISABLED;
1520                } else {
1521                    loge("Disable network failed on " + config.networkId);
1522                }
1523            }
1524        }
1525
1526        if (networkDisabled) {
1527            sendConfiguredNetworksChangedBroadcast();
1528        }
1529    }
1530    /**
1531     * Disable a network. Note that there is no saveConfig operation.
1532     * @param netId network to be disabled
1533     * @return {@code true} if it succeeds, {@code false} otherwise
1534     */
1535    boolean disableNetwork(int netId) {
1536        return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
1537    }
1538
1539    /**
1540     * Disable a network. Note that there is no saveConfig operation.
1541     * @param netId network to be disabled
1542     * @param reason reason code network was disabled
1543     * @return {@code true} if it succeeds, {@code false} otherwise
1544     */
1545    boolean disableNetwork(int netId, int reason) {
1546        if (VDBG) localLog("disableNetwork", netId);
1547        boolean ret = mWifiNative.disableNetwork(netId);
1548        WifiConfiguration network = null;
1549        WifiConfiguration config = mConfiguredNetworks.get(netId);
1550
1551        if (VDBG) {
1552            if (config != null) {
1553                loge("disableNetwork netId=" + Integer.toString(netId)
1554                        + " SSID=" + config.SSID
1555                        + " disabled=" + (config.status == Status.DISABLED)
1556                        + " reason=" + Integer.toString(config.disableReason));
1557            }
1558        }
1559        /* Only change the reason if the network was not previously disabled
1560        /* and the reason is not DISABLED_BY_WIFI_MANAGER, that is, if a 3rd party
1561         * set its configuration as disabled, then leave it disabled */
1562        if (config != null) {
1563            if (config.status != Status.DISABLED
1564                && config.disableReason != WifiConfiguration.DISABLED_BY_WIFI_MANAGER) {
1565                config.status = Status.DISABLED;
1566                config.disableReason = reason;
1567                network = config;
1568            }
1569            if (reason == WifiConfiguration.DISABLED_BY_WIFI_MANAGER) {
1570                // Make sure autojoin wont reenable this configuration without further user
1571                // intervention
1572                config.status = Status.DISABLED;
1573                config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_DISABLED_USER_ACTION;
1574            }
1575        }
1576        if (network != null) {
1577            sendConfiguredNetworksChangedBroadcast(network,
1578                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1579        }
1580        return ret;
1581    }
1582
1583    /**
1584     * Save the configured networks in supplicant to disk
1585     * @return {@code true} if it succeeds, {@code false} otherwise
1586     */
1587    boolean saveConfig() {
1588        return mWifiNative.saveConfig();
1589    }
1590
1591    /**
1592     * Start WPS pin method configuration with pin obtained
1593     * from the access point
1594     * @param config WPS configuration
1595     * @return Wps result containing status and pin
1596     */
1597    WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
1598        WpsResult result = new WpsResult();
1599        if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
1600            /* WPS leaves all networks disabled */
1601            markAllNetworksDisabled();
1602            result.status = WpsResult.Status.SUCCESS;
1603        } else {
1604            loge("Failed to start WPS pin method configuration");
1605            result.status = WpsResult.Status.FAILURE;
1606        }
1607        return result;
1608    }
1609
1610    /**
1611     * Start WPS pin method configuration with pin obtained
1612     * from the device
1613     * @return WpsResult indicating status and pin
1614     */
1615    WpsResult startWpsWithPinFromDevice(WpsInfo config) {
1616        WpsResult result = new WpsResult();
1617        result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
1618        /* WPS leaves all networks disabled */
1619        if (!TextUtils.isEmpty(result.pin)) {
1620            markAllNetworksDisabled();
1621            result.status = WpsResult.Status.SUCCESS;
1622        } else {
1623            loge("Failed to start WPS pin method configuration");
1624            result.status = WpsResult.Status.FAILURE;
1625        }
1626        return result;
1627    }
1628
1629    /**
1630     * Start WPS push button configuration
1631     * @param config WPS configuration
1632     * @return WpsResult indicating status and pin
1633     */
1634    WpsResult startWpsPbc(WpsInfo config) {
1635        WpsResult result = new WpsResult();
1636        if (mWifiNative.startWpsPbc(config.BSSID)) {
1637            /* WPS leaves all networks disabled */
1638            markAllNetworksDisabled();
1639            result.status = WpsResult.Status.SUCCESS;
1640        } else {
1641            loge("Failed to start WPS push button configuration");
1642            result.status = WpsResult.Status.FAILURE;
1643        }
1644        return result;
1645    }
1646
1647    /**
1648     * Fetch the static IP configuration for a given network id
1649     */
1650    StaticIpConfiguration getStaticIpConfiguration(int netId) {
1651        WifiConfiguration config = mConfiguredNetworks.get(netId);
1652        if (config != null) {
1653            return config.getStaticIpConfiguration();
1654        }
1655        return null;
1656    }
1657
1658    /**
1659     * Set the static IP configuration for a given network id
1660     */
1661    void setStaticIpConfiguration(int netId, StaticIpConfiguration staticIpConfiguration) {
1662        WifiConfiguration config = mConfiguredNetworks.get(netId);
1663        if (config != null) {
1664            config.setStaticIpConfiguration(staticIpConfiguration);
1665        }
1666    }
1667
1668    /**
1669     * set default GW MAC address
1670     */
1671    void setDefaultGwMacAddress(int netId, String macAddress) {
1672        WifiConfiguration config = mConfiguredNetworks.get(netId);
1673        if (config != null) {
1674            //update defaultGwMacAddress
1675            config.defaultGwMacAddress = macAddress;
1676        }
1677    }
1678
1679
1680    /**
1681     * Fetch the proxy properties for a given network id
1682     * @param network id
1683     * @return ProxyInfo for the network id
1684     */
1685    ProxyInfo getProxyProperties(int netId) {
1686        WifiConfiguration config = mConfiguredNetworks.get(netId);
1687        if (config != null) {
1688            return config.getHttpProxy();
1689        }
1690        return null;
1691    }
1692
1693    /**
1694     * Return if the specified network is using static IP
1695     * @param network id
1696     * @return {@code true} if using static ip for netId
1697     */
1698    boolean isUsingStaticIp(int netId) {
1699        WifiConfiguration config = mConfiguredNetworks.get(netId);
1700        if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {
1701            return true;
1702        }
1703        return false;
1704    }
1705
1706    /**
1707     * Should be called when a single network configuration is made.
1708     * @param network The network configuration that changed.
1709     * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
1710     * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
1711     */
1712    private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
1713            int reason) {
1714        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
1715        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1716        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
1717        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
1718        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
1719        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1720    }
1721
1722    /**
1723     * Should be called when multiple network configuration changes are made.
1724     */
1725    private void sendConfiguredNetworksChangedBroadcast() {
1726        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
1727        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1728        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
1729        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1730    }
1731
1732    void loadConfiguredNetworks() {
1733
1734        mLastPriority = 0;
1735
1736        mConfiguredNetworks.clear();
1737        mNetworkIds.clear();
1738
1739        int last_id = -1;
1740        boolean done = false;
1741        while (!done) {
1742
1743            String listStr = mWifiNative.listNetworks(last_id);
1744            if (listStr == null)
1745                return;
1746
1747            String[] lines = listStr.split("\n");
1748
1749            if (showNetworks) {
1750                localLog("WifiConfigStore: loadConfiguredNetworks:  ");
1751                for (String net : lines) {
1752                    localLog(net);
1753                }
1754            }
1755
1756            // Skip the first line, which is a header
1757            for (int i = 1; i < lines.length; i++) {
1758                String[] result = lines[i].split("\t");
1759                // network-id | ssid | bssid | flags
1760                WifiConfiguration config = new WifiConfiguration();
1761                try {
1762                    config.networkId = Integer.parseInt(result[0]);
1763                    last_id = config.networkId;
1764                } catch(NumberFormatException e) {
1765                    loge("Failed to read network-id '" + result[0] + "'");
1766                    continue;
1767                }
1768                if (result.length > 3) {
1769                    if (result[3].indexOf("[CURRENT]") != -1)
1770                        config.status = WifiConfiguration.Status.CURRENT;
1771                    else if (result[3].indexOf("[DISABLED]") != -1)
1772                        config.status = WifiConfiguration.Status.DISABLED;
1773                    else
1774                        config.status = WifiConfiguration.Status.ENABLED;
1775                } else {
1776                    config.status = WifiConfiguration.Status.ENABLED;
1777                }
1778
1779                readNetworkVariables(config);
1780
1781                Checksum csum = new CRC32();
1782                if (config.SSID != null) {
1783                    csum.update(config.SSID.getBytes(), 0, config.SSID.getBytes().length);
1784                    long d = csum.getValue();
1785                    if (mDeletedSSIDs.contains(d)) {
1786                        loge(" got CRC for SSID " + config.SSID + " -> " + d + ", was deleted");
1787                    }
1788                }
1789
1790                if (config.priority > mLastPriority) {
1791                    mLastPriority = config.priority;
1792                }
1793
1794                config.setIpAssignment(IpAssignment.DHCP);
1795                config.setProxySettings(ProxySettings.NONE);
1796
1797                if (mNetworkIds.containsKey(configKey(config))) {
1798                    // That SSID is already known, just ignore this duplicate entry
1799                    if (showNetworks) localLog("discarded duplicate network ", config.networkId);
1800                } else if(config.isValid()){
1801                    mConfiguredNetworks.put(config.networkId, config);
1802                    mNetworkIds.put(configKey(config), config.networkId);
1803                    if (showNetworks) localLog("loaded configured network", config.networkId);
1804                } else {
1805                    if (showNetworks) log("Ignoring loaded configured for network " + config.networkId
1806                        + " because config are not valid");
1807                }
1808            }
1809
1810            done = (lines.length == 1);
1811        }
1812
1813        readPasspointConfig();
1814        readIpAndProxyConfigurations();
1815        readNetworkHistory();
1816        readAutoJoinConfig();
1817
1818        sendConfiguredNetworksChangedBroadcast();
1819
1820        if (showNetworks) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks");
1821
1822        if (mNetworkIds.size() == 0) {
1823            // no networks? Lets log if the wpa_supplicant.conf file contents
1824            BufferedReader reader = null;
1825            try {
1826                reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
1827                if (DBG) {
1828                    localLog("--- Begin wpa_supplicant.conf Contents ---", true);
1829                    for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1830                        localLog(line, true);
1831                    }
1832                    localLog("--- End wpa_supplicant.conf Contents ---", true);
1833                }
1834            } catch (FileNotFoundException e) {
1835                localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e, true);
1836            } catch (IOException e) {
1837                localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e, true);
1838            } finally {
1839                try {
1840                    if (reader != null) {
1841                        reader.close();
1842                    }
1843                } catch (IOException e) {
1844                    // Just ignore the fact that we couldn't close
1845                }
1846            }
1847        }
1848    }
1849
1850    private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
1851        Map<String, String> result = new HashMap<>();
1852        BufferedReader reader = null;
1853        if (VDBG) loge("readNetworkVariablesFromSupplicantFile key=" + key);
1854
1855        try {
1856            reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
1857            boolean found = false;
1858            String networkSsid = null;
1859            String value = null;
1860
1861            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1862
1863                if (line.matches("[ \\t]*network=\\{")) {
1864                    found = true;
1865                    networkSsid = null;
1866                    value = null;
1867                } else if (line.matches("[ \\t]*\\}")) {
1868                    found = false;
1869                    networkSsid = null;
1870                    value = null;
1871                }
1872
1873                if (found) {
1874                    String trimmedLine = line.trim();
1875                    if (trimmedLine.startsWith("ssid=")) {
1876                        networkSsid = trimmedLine.substring(5);
1877                    } else if (trimmedLine.startsWith(key + "=")) {
1878                        value = trimmedLine.substring(key.length() + 1);
1879                    }
1880
1881                    if (networkSsid != null && value != null) {
1882                        result.put(networkSsid, value);
1883                    }
1884                }
1885            }
1886        } catch (FileNotFoundException e) {
1887            if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
1888        } catch (IOException e) {
1889            if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
1890        } finally {
1891            try {
1892                if (reader != null) {
1893                    reader.close();
1894                }
1895            } catch (IOException e) {
1896                // Just ignore the fact that we couldn't close
1897            }
1898        }
1899
1900        return result;
1901    }
1902
1903    private String readNetworkVariableFromSupplicantFile(String ssid, String key) {
1904        long start = SystemClock.elapsedRealtimeNanos();
1905        Map<String, String> data = readNetworkVariablesFromSupplicantFile(key);
1906        long end = SystemClock.elapsedRealtimeNanos();
1907
1908        if (VDBG) {
1909            loge("readNetworkVariableFromSupplicantFile ssid=[" + ssid + "] key=" + key
1910                    + " duration=" + (long)(end - start));
1911        }
1912        return data.get(ssid);
1913    }
1914
1915    /* Mark all networks except specified netId as disabled */
1916    private void markAllNetworksDisabledExcept(int netId) {
1917        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1918            if(config != null && config.networkId != netId) {
1919                if (config.status != Status.DISABLED) {
1920                    config.status = Status.DISABLED;
1921                    config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
1922                }
1923            }
1924        }
1925    }
1926
1927    private void markAllNetworksDisabled() {
1928        markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
1929    }
1930
1931    boolean needsUnlockedKeyStore() {
1932
1933        // Any network using certificates to authenticate access requires
1934        // unlocked key store; unless the certificates can be stored with
1935        // hardware encryption
1936
1937        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1938
1939            if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
1940                    && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1941
1942                if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
1943                    return true;
1944                }
1945            }
1946        }
1947
1948        return false;
1949    }
1950
1951    void readPasspointConfig() {
1952        File file = new File(PPS_FILE);
1953
1954        MOManager moManager;
1955        List<HomeSP> homeSPs;
1956        try {
1957            moManager = new MOManager(file);
1958            homeSPs = moManager.loadAllSPs();
1959        } catch (IOException e) {
1960            loge("Could not read " + PPS_FILE + " : " + e);
1961            return;
1962        } catch (NullPointerException e) {
1963            loge("Could not read " + PPS_FILE + " : " + e);
1964            return;
1965        }
1966
1967        mConfiguredHomeSPs.clear();
1968
1969        log("read " + homeSPs.size() + " from " + PPS_FILE);
1970
1971        for (HomeSP homeSp : homeSPs) {
1972            String fqdn = homeSp.getFQDN();
1973            String chksum = Long.toString(getChecksum(fqdn));
1974            log("Looking for " + chksum + " for " + fqdn);
1975            for (WifiConfiguration config : mConfiguredNetworks.values()) {
1976                log("Testing " + config.SSID);
1977
1978                String id_str = mWifiNative.getNetworkVariable(config.networkId, idStringVarName);
1979                if (id_str != null && id_str.equals(chksum) && config.enterpriseConfig != null) {
1980                    log("Matched " + id_str);
1981                    config.FQDN = fqdn;
1982                    config.providerFriendlyName = homeSp.getFriendlyName();
1983                    config.roamingConsortiumIds = new HashSet<Long>();
1984                    for (Long roamingConsortium : homeSp.getRoamingConsortiums()) {
1985                        config.roamingConsortiumIds.add(roamingConsortium);
1986                    }
1987                    config.enterpriseConfig.setPlmn(homeSp.getCredential().getImsi());
1988                    config.enterpriseConfig.setRealm(homeSp.getCredential().getRealm());
1989                    mConfiguredHomeSPs.put(config.networkId, homeSp);
1990                }
1991            }
1992        }
1993
1994        log("loaded " + mConfiguredHomeSPs.size() + " passpoint configs");
1995    }
1996
1997    public void writePasspointConfigs() {
1998        mWriter.write(PPS_FILE, new DelayedDiskWrite.Writer() {
1999            @Override
2000            public void onWriteCalled(DataOutputStream out) throws IOException {
2001                log("saving " + mConfiguredHomeSPs.size() + " in " + PPS_FILE + " ...");
2002                File file = new File(PPS_FILE);
2003
2004                MOManager moManager;
2005                try {
2006                    moManager = new MOManager(file);
2007                    moManager.saveAllSps(mConfiguredHomeSPs.values());
2008                } catch (IOException e) {
2009                    loge("Could not write " + PPS_FILE + " : " + e);
2010                }
2011            }
2012        });
2013    }
2014
2015    public void writeKnownNetworkHistory(boolean force) {
2016        boolean needUpdate = force;
2017
2018        /* Make a copy */
2019        final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
2020        for (WifiConfiguration config : mConfiguredNetworks.values()) {
2021            networks.add(new WifiConfiguration(config));
2022            if (config.dirty == true) {
2023                loge(" rewrite network history for " + config.configKey());
2024                config.dirty = false;
2025                needUpdate = true;
2026            }
2027        }
2028        if (VDBG) {
2029            loge(" writeKnownNetworkHistory() num networks:" +
2030                    mConfiguredNetworks.size() + " needWrite=" + needUpdate);
2031        }
2032        if (needUpdate == false) {
2033            return;
2034        }
2035        mWriter.write(networkHistoryConfigFile, new DelayedDiskWrite.Writer() {
2036            public void onWriteCalled(DataOutputStream out) throws IOException {
2037                for (WifiConfiguration config : networks) {
2038                    //loge("onWriteCalled write SSID: " + config.SSID);
2039                   /* if (config.getLinkProperties() != null)
2040                        loge(" lp " + config.getLinkProperties().toString());
2041                    else
2042                        loge("attempt config w/o lp");
2043                    */
2044
2045                    if (VDBG) {
2046                        int num = 0;
2047                        int numlink = 0;
2048                        if (config.connectChoices != null) {
2049                            num = config.connectChoices.size();
2050                        }
2051                        if (config.linkedConfigurations != null) {
2052                            numlink = config.linkedConfigurations.size();
2053                        }
2054                        loge("saving network history: " + config.configKey()  + " gw: " +
2055                                config.defaultGwMacAddress + " autojoin-status: " +
2056                                config.autoJoinStatus + " ephemeral=" + config.ephemeral
2057                                + " choices:" + Integer.toString(num)
2058                                + " link:" + Integer.toString(numlink)
2059                                + " status:" + Integer.toString(config.status)
2060                                + " nid:" + Integer.toString(config.networkId));
2061                    }
2062
2063                    if (config.isValid() == false)
2064                        continue;
2065
2066                    if (config.SSID == null) {
2067                        if (VDBG) {
2068                            loge("writeKnownNetworkHistory trying to write config with null SSID");
2069                        }
2070                        continue;
2071                    }
2072                    if (VDBG) {
2073                        loge("writeKnownNetworkHistory write config " + config.configKey());
2074                    }
2075                    out.writeUTF(CONFIG_KEY + config.configKey() + SEPARATOR_KEY);
2076
2077                    out.writeUTF(SSID_KEY + config.SSID + SEPARATOR_KEY);
2078                    out.writeUTF(FQDN_KEY + config.FQDN + SEPARATOR_KEY);
2079
2080                    out.writeUTF(PRIORITY_KEY + Integer.toString(config.priority) + SEPARATOR_KEY);
2081                    out.writeUTF(STATUS_KEY + Integer.toString(config.autoJoinStatus)
2082                            + SEPARATOR_KEY);
2083                    out.writeUTF(SUPPLICANT_STATUS_KEY + Integer.toString(config.status)
2084                            + SEPARATOR_KEY);
2085                    out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY
2086                            + Integer.toString(config.disableReason)
2087                            + SEPARATOR_KEY);
2088                    out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId)
2089                            + SEPARATOR_KEY);
2090                    out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded)
2091                            + SEPARATOR_KEY);
2092                    out.writeUTF(DID_SELF_ADD_KEY + Boolean.toString(config.didSelfAdd)
2093                            + SEPARATOR_KEY);
2094                    out.writeUTF(NO_INTERNET_ACCESS_REPORTS_KEY
2095                            + Integer.toString(config.numNoInternetAccessReports)
2096                            + SEPARATOR_KEY);
2097                    out.writeUTF(VALIDATED_INTERNET_ACCESS_KEY
2098                            + Boolean.toString(config.validatedInternetAccess)
2099                            + SEPARATOR_KEY);
2100                    out.writeUTF(EPHEMERAL_KEY
2101                            + Boolean.toString(config.ephemeral)
2102                            + SEPARATOR_KEY);
2103                    if (config.peerWifiConfiguration != null) {
2104                        out.writeUTF(PEER_CONFIGURATION_KEY + config.peerWifiConfiguration
2105                                + SEPARATOR_KEY);
2106                    }
2107                    out.writeUTF(NUM_CONNECTION_FAILURES_KEY
2108                            + Integer.toString(config.numConnectionFailures)
2109                            + SEPARATOR_KEY);
2110                    out.writeUTF(NUM_AUTH_FAILURES_KEY
2111                            + Integer.toString(config.numAuthFailures)
2112                            + SEPARATOR_KEY);
2113                    out.writeUTF(NUM_IP_CONFIG_FAILURES_KEY
2114                            + Integer.toString(config.numIpConfigFailures)
2115                            + SEPARATOR_KEY);
2116                    out.writeUTF(SCORER_OVERRIDE_KEY + Integer.toString(config.numScorerOverride)
2117                            + SEPARATOR_KEY);
2118                    out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY
2119                            + Integer.toString(config.numScorerOverrideAndSwitchedNetwork)
2120                            + SEPARATOR_KEY);
2121                    out.writeUTF(NUM_ASSOCIATION_KEY
2122                            + Integer.toString(config.numAssociation)
2123                            + SEPARATOR_KEY);
2124                    out.writeUTF(JOIN_ATTEMPT_BOOST_KEY
2125                            + Integer.toString(config.autoJoinUseAggressiveJoinAttemptThreshold)
2126                            + SEPARATOR_KEY);
2127                    //out.writeUTF(BLACKLIST_MILLI_KEY + Long.toString(config.blackListTimestamp)
2128                    //        + SEPARATOR_KEY);
2129                    out.writeUTF(CREATOR_UID_KEY + Integer.toString(config.creatorUid)
2130                            + SEPARATOR_KEY);
2131                    out.writeUTF(CONNECT_UID_KEY + Integer.toString(config.lastConnectUid)
2132                            + SEPARATOR_KEY);
2133                    out.writeUTF(UPDATE_UID_KEY + Integer.toString(config.lastUpdateUid)
2134                            + SEPARATOR_KEY);
2135                    String allowedKeyManagementString =
2136                            makeString(config.allowedKeyManagement,
2137                                    WifiConfiguration.KeyMgmt.strings);
2138                    out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY);
2139
2140                    if (config.connectChoices != null) {
2141                        for (String key : config.connectChoices.keySet()) {
2142                            Integer choice = config.connectChoices.get(key);
2143                            out.writeUTF(CHOICE_KEY + key + "="
2144                                    + choice.toString() + SEPARATOR_KEY);
2145                        }
2146                    }
2147                    if (config.linkedConfigurations != null) {
2148                        loge("writeKnownNetworkHistory write linked "
2149                                + config.linkedConfigurations.size());
2150
2151                        for (String key : config.linkedConfigurations.keySet()) {
2152                            out.writeUTF(LINK_KEY + key + SEPARATOR_KEY);
2153                        }
2154                    }
2155
2156                    String macAddress = config.defaultGwMacAddress;
2157                    if (macAddress != null) {
2158                        out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY);
2159                    }
2160
2161                    if (getScanDetailCache(config) != null) {
2162                        for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
2163                            ScanResult result = scanDetail.getScanResult();
2164                            out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY);
2165
2166                            out.writeUTF(FREQ_KEY + Integer.toString(result.frequency)
2167                                    + SEPARATOR_KEY);
2168
2169                            out.writeUTF(RSSI_KEY + Integer.toString(result.level)
2170                                    + SEPARATOR_KEY);
2171
2172                            out.writeUTF(BSSID_STATUS_KEY
2173                                    + Integer.toString(result.autoJoinStatus)
2174                                    + SEPARATOR_KEY);
2175
2176                            //if (result.seen != 0) {
2177                            //    out.writeUTF(MILLI_KEY + Long.toString(result.seen)
2178                            //            + SEPARATOR_KEY);
2179                            //}
2180                            out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY);
2181                        }
2182                    }
2183                    if (config.lastFailure != null) {
2184                        out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY);
2185                    }
2186                    out.writeUTF(SEPARATOR_KEY);
2187                    // Add extra blank lines for clarity
2188                    out.writeUTF(SEPARATOR_KEY);
2189                    out.writeUTF(SEPARATOR_KEY);
2190                }
2191                if (mDeletedSSIDs != null && mDeletedSSIDs.size() > 0) {
2192                    for (Long i : mDeletedSSIDs) {
2193                        out.writeUTF(DELETED_CRC32_KEY);
2194                        out.writeUTF(String.valueOf(i));
2195                        out.writeUTF(SEPARATOR_KEY);
2196                    }
2197                }
2198                if (mDeletedEphemeralSSIDs != null && mDeletedEphemeralSSIDs.size() > 0) {
2199                    for (String ssid : mDeletedEphemeralSSIDs) {
2200                        out.writeUTF(DELETED_EPHEMERAL_KEY);
2201                        out.writeUTF(ssid);
2202                        out.writeUTF(SEPARATOR_KEY);
2203                    }
2204                }
2205            }
2206        });
2207    }
2208
2209    public void setLastSelectedConfiguration(int netId) {
2210        if (VDBG) {
2211            loge("setLastSelectedConfiguration " + Integer.toString(netId));
2212        }
2213        if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
2214            lastSelectedConfiguration = null;
2215        } else {
2216            WifiConfiguration selected = getWifiConfiguration(netId);
2217            if (selected == null) {
2218                lastSelectedConfiguration = null;
2219            } else {
2220                lastSelectedConfiguration = selected.configKey();
2221                selected.numConnectionFailures = 0;
2222                selected.numIpConfigFailures = 0;
2223                selected.numAuthFailures = 0;
2224                selected.numNoInternetAccessReports = 0;
2225                if (VDBG) {
2226                    loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration);
2227                }
2228            }
2229        }
2230    }
2231
2232    public String getLastSelectedConfiguration() {
2233        return lastSelectedConfiguration;
2234    }
2235
2236    public boolean isLastSelectedConfiguration(WifiConfiguration config) {
2237        return (lastSelectedConfiguration != null
2238                && config != null
2239                && lastSelectedConfiguration.equals(config.configKey()));
2240    }
2241
2242    private void readNetworkHistory() {
2243        if (showNetworks) {
2244            localLog("readNetworkHistory() path:" + networkHistoryConfigFile);
2245        }
2246        DataInputStream in = null;
2247        try {
2248            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
2249                    networkHistoryConfigFile)));
2250            WifiConfiguration config = null;
2251            while (true) {
2252                int id = -1;
2253                String key = in.readUTF();
2254                String bssid = null;
2255                String ssid = null;
2256
2257                int freq = 0;
2258                int status = 0;
2259                long seen = 0;
2260                int rssi = WifiConfiguration.INVALID_RSSI;
2261                String caps = null;
2262                if (key.startsWith(CONFIG_KEY)) {
2263
2264                    if (config != null) {
2265                        config = null;
2266                    }
2267                    String configKey = key.replace(CONFIG_KEY, "");
2268                    configKey = configKey.replace(SEPARATOR_KEY, "");
2269                    // get the networkId for that config Key
2270                    Integer n = mNetworkIds.get(configKey.hashCode());
2271                    // skip reading that configuration data
2272                    // since we don't have a corresponding network ID
2273                    if (n == null) {
2274                        localLog("readNetworkHistory didnt find netid for hash="
2275                                + Integer.toString(configKey.hashCode())
2276                                + " key: " + configKey);
2277                        continue;
2278                    }
2279                    config = mConfiguredNetworks.get(n);
2280                    if (config == null) {
2281                        localLog("readNetworkHistory didnt find config for netid="
2282                                + n.toString()
2283                                + " key: " + configKey);
2284                    }
2285                    status = 0;
2286                    ssid = null;
2287                    bssid = null;
2288                    freq = 0;
2289                    seen = 0;
2290                    rssi = WifiConfiguration.INVALID_RSSI;
2291                    caps = null;
2292
2293                } else if (config != null) {
2294                    if (key.startsWith(SSID_KEY)) {
2295                        ssid = key.replace(SSID_KEY, "");
2296                        ssid = ssid.replace(SEPARATOR_KEY, "");
2297                        if (config.SSID != null && !config.SSID.equals(ssid)) {
2298                            loge("Error parsing network history file, mismatched SSIDs");
2299                            config = null; //error
2300                            ssid = null;
2301                        } else {
2302                            config.SSID = ssid;
2303                        }
2304                    }
2305
2306                    if (key.startsWith(FQDN_KEY)) {
2307                        String fqdn = key.replace(FQDN_KEY, "");
2308                        fqdn = fqdn.replace(SEPARATOR_KEY, "");
2309                        config.FQDN = fqdn;
2310                    }
2311
2312                    if (key.startsWith(DEFAULT_GW_KEY)) {
2313                        String gateway = key.replace(DEFAULT_GW_KEY, "");
2314                        gateway = gateway.replace(SEPARATOR_KEY, "");
2315                        config.defaultGwMacAddress = gateway;
2316                    }
2317
2318                    if (key.startsWith(STATUS_KEY)) {
2319                        String st = key.replace(STATUS_KEY, "");
2320                        st = st.replace(SEPARATOR_KEY, "");
2321                        config.autoJoinStatus = Integer.parseInt(st);
2322                    }
2323
2324                    if (key.startsWith(SUPPLICANT_DISABLE_REASON_KEY)) {
2325                        String reason = key.replace(SUPPLICANT_DISABLE_REASON_KEY, "");
2326                        reason = reason.replace(SEPARATOR_KEY, "");
2327                        config.disableReason = Integer.parseInt(reason);
2328                    }
2329
2330                    if (key.startsWith(SELF_ADDED_KEY)) {
2331                        String selfAdded = key.replace(SELF_ADDED_KEY, "");
2332                        selfAdded = selfAdded.replace(SEPARATOR_KEY, "");
2333                        config.selfAdded = Boolean.parseBoolean(selfAdded);
2334                    }
2335
2336                    if (key.startsWith(DID_SELF_ADD_KEY)) {
2337                        String didSelfAdd = key.replace(DID_SELF_ADD_KEY, "");
2338                        didSelfAdd = didSelfAdd.replace(SEPARATOR_KEY, "");
2339                        config.didSelfAdd = Boolean.parseBoolean(didSelfAdd);
2340                    }
2341
2342                    if (key.startsWith(NO_INTERNET_ACCESS_REPORTS_KEY)) {
2343                        String access = key.replace(NO_INTERNET_ACCESS_REPORTS_KEY, "");
2344                        access = access.replace(SEPARATOR_KEY, "");
2345                        config.numNoInternetAccessReports = Integer.parseInt(access);
2346                    }
2347
2348                    if (key.startsWith(VALIDATED_INTERNET_ACCESS_KEY)) {
2349                        String access = key.replace(VALIDATED_INTERNET_ACCESS_KEY, "");
2350                        access = access.replace(SEPARATOR_KEY, "");
2351                        config.validatedInternetAccess = Boolean.parseBoolean(access);
2352                    }
2353
2354                    if (key.startsWith(EPHEMERAL_KEY)) {
2355                        String access = key.replace(EPHEMERAL_KEY, "");
2356                        access = access.replace(SEPARATOR_KEY, "");
2357                        config.ephemeral = Boolean.parseBoolean(access);
2358                    }
2359
2360                    if (key.startsWith(CREATOR_UID_KEY)) {
2361                        String uid = key.replace(CREATOR_UID_KEY, "");
2362                        uid = uid.replace(SEPARATOR_KEY, "");
2363                        config.creatorUid = Integer.parseInt(uid);
2364                    }
2365
2366                    if (key.startsWith(BLACKLIST_MILLI_KEY)) {
2367                        String milli = key.replace(BLACKLIST_MILLI_KEY, "");
2368                        milli = milli.replace(SEPARATOR_KEY, "");
2369                        config.blackListTimestamp = Long.parseLong(milli);
2370                    }
2371
2372                    if (key.startsWith(NUM_CONNECTION_FAILURES_KEY)) {
2373                        String num = key.replace(NUM_CONNECTION_FAILURES_KEY, "");
2374                        num = num.replace(SEPARATOR_KEY, "");
2375                        config.numConnectionFailures = Integer.parseInt(num);
2376                    }
2377
2378                    if (key.startsWith(NUM_IP_CONFIG_FAILURES_KEY)) {
2379                        String num = key.replace(NUM_IP_CONFIG_FAILURES_KEY, "");
2380                        num = num.replace(SEPARATOR_KEY, "");
2381                        config.numIpConfigFailures = Integer.parseInt(num);
2382                    }
2383
2384                    if (key.startsWith(NUM_AUTH_FAILURES_KEY)) {
2385                        String num = key.replace(NUM_AUTH_FAILURES_KEY, "");
2386                        num = num.replace(SEPARATOR_KEY, "");
2387                        config.numIpConfigFailures = Integer.parseInt(num);
2388                    }
2389
2390                    if (key.startsWith(SCORER_OVERRIDE_KEY)) {
2391                        String num = key.replace(SCORER_OVERRIDE_KEY, "");
2392                        num = num.replace(SEPARATOR_KEY, "");
2393                        config.numScorerOverride = Integer.parseInt(num);
2394                    }
2395
2396                    if (key.startsWith(SCORER_OVERRIDE_AND_SWITCH_KEY)) {
2397                        String num = key.replace(SCORER_OVERRIDE_AND_SWITCH_KEY, "");
2398                        num = num.replace(SEPARATOR_KEY, "");
2399                        config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(num);
2400                    }
2401
2402                    if (key.startsWith(NUM_ASSOCIATION_KEY)) {
2403                        String num = key.replace(NUM_ASSOCIATION_KEY, "");
2404                        num = num.replace(SEPARATOR_KEY, "");
2405                        config.numAssociation = Integer.parseInt(num);
2406                    }
2407
2408                    if (key.startsWith(JOIN_ATTEMPT_BOOST_KEY)) {
2409                        String num = key.replace(JOIN_ATTEMPT_BOOST_KEY, "");
2410                        num = num.replace(SEPARATOR_KEY, "");
2411                        config.autoJoinUseAggressiveJoinAttemptThreshold = Integer.parseInt(num);
2412                    }
2413
2414                    if (key.startsWith(CONNECT_UID_KEY)) {
2415                        String uid = key.replace(CONNECT_UID_KEY, "");
2416                        uid = uid.replace(SEPARATOR_KEY, "");
2417                        config.lastConnectUid = Integer.parseInt(uid);
2418                    }
2419
2420                    if (key.startsWith(UPDATE_UID_KEY)) {
2421                        String uid = key.replace(UPDATE_UID_KEY, "");
2422                        uid = uid.replace(SEPARATOR_KEY, "");
2423                        config.lastUpdateUid = Integer.parseInt(uid);
2424                    }
2425
2426                    if (key.startsWith(FAILURE_KEY)) {
2427                        config.lastFailure = key.replace(FAILURE_KEY, "");
2428                        config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, "");
2429                    }
2430
2431                    if (key.startsWith(PEER_CONFIGURATION_KEY)) {
2432                        config.peerWifiConfiguration = key.replace(PEER_CONFIGURATION_KEY, "");
2433                        config.peerWifiConfiguration =
2434                                config.peerWifiConfiguration.replace(SEPARATOR_KEY, "");
2435                    }
2436
2437                    if (key.startsWith(CHOICE_KEY)) {
2438                        String choiceStr = key.replace(CHOICE_KEY, "");
2439                        choiceStr = choiceStr.replace(SEPARATOR_KEY, "");
2440                        String configKey = "";
2441                        int choice = 0;
2442                        Matcher match = mConnectChoice.matcher(choiceStr);
2443                        if (!match.find()) {
2444                            if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " +
2445                                    " Couldnt match pattern : " + choiceStr);
2446                        } else {
2447                            configKey = match.group(1);
2448                            try {
2449                                choice = Integer.parseInt(match.group(2));
2450                            } catch (NumberFormatException e) {
2451                                choice = 0;
2452                            }
2453                            if (choice > 0) {
2454                                if (config.connectChoices == null) {
2455                                    config.connectChoices = new HashMap<String, Integer>();
2456                                }
2457                                config.connectChoices.put(configKey, choice);
2458                            }
2459                        }
2460                    }
2461
2462                    if (key.startsWith(LINK_KEY)) {
2463                        String configKey = key.replace(LINK_KEY, "");
2464                        configKey = configKey.replace(SEPARATOR_KEY, "");
2465                        if (config.linkedConfigurations == null) {
2466                            config.linkedConfigurations = new HashMap<String, Integer>();
2467                        }
2468                        if (config.linkedConfigurations != null) {
2469                            config.linkedConfigurations.put(configKey, -1);
2470                        }
2471                    }
2472
2473                    if (key.startsWith(BSSID_KEY)) {
2474                        if (key.startsWith(BSSID_KEY)) {
2475                            bssid = key.replace(BSSID_KEY, "");
2476                            bssid = bssid.replace(SEPARATOR_KEY, "");
2477                            freq = 0;
2478                            seen = 0;
2479                            rssi = WifiConfiguration.INVALID_RSSI;
2480                            caps = "";
2481                            status = 0;
2482                        }
2483
2484                        if (key.startsWith(RSSI_KEY)) {
2485                            String lvl = key.replace(RSSI_KEY, "");
2486                            lvl = lvl.replace(SEPARATOR_KEY, "");
2487                            rssi = Integer.parseInt(lvl);
2488                        }
2489
2490                        if (key.startsWith(BSSID_STATUS_KEY)) {
2491                            String st = key.replace(BSSID_STATUS_KEY, "");
2492                            st = st.replace(SEPARATOR_KEY, "");
2493                            status = Integer.parseInt(st);
2494                        }
2495
2496                        if (key.startsWith(FREQ_KEY)) {
2497                            String channel = key.replace(FREQ_KEY, "");
2498                            channel = channel.replace(SEPARATOR_KEY, "");
2499                            freq = Integer.parseInt(channel);
2500                        }
2501
2502                        if (key.startsWith(DATE_KEY)) {
2503                        /*
2504                         * when reading the configuration from file we don't update the date
2505                         * so as to avoid reading back stale or non-sensical data that would
2506                         * depend on network time.
2507                         * The date of a WifiConfiguration should only come from actual scan result.
2508                         *
2509                        String s = key.replace(FREQ_KEY, "");
2510                        seen = Integer.getInteger(s);
2511                        */
2512                        }
2513
2514                        if (key.startsWith(BSSID_KEY_END)) {
2515                            if ((bssid != null) && (ssid != null)) {
2516
2517                                if (getScanDetailCache(config) != null) {
2518                                    WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
2519                                    ScanDetail scanDetail = new ScanDetail(wssid, bssid,
2520                                            caps, rssi, freq, (long) 0, seen);
2521                                    getScanDetailCache(config).put(scanDetail);
2522                                    scanDetail.getScanResult().autoJoinStatus = status;
2523                                }
2524                            }
2525                        }
2526
2527                        if (key.startsWith(DELETED_CRC32_KEY)) {
2528                            String crc = key.replace(DELETED_CRC32_KEY, "");
2529                            Long c = Long.parseLong(crc);
2530                            mDeletedSSIDs.add(c);
2531                        }
2532                        if (key.startsWith(DELETED_EPHEMERAL_KEY)) {
2533                            String s = key.replace(DELETED_EPHEMERAL_KEY, "");
2534                            if (!TextUtils.isEmpty(s)) {
2535                                s = s.replace(SEPARATOR_KEY, "");
2536                                mDeletedEphemeralSSIDs.add(s);
2537                            }
2538                        }
2539                    }
2540                }
2541            }
2542        } catch (EOFException ignore) {
2543            if (in != null) {
2544                try {
2545                    in.close();
2546                } catch (Exception e) {
2547                    loge("readNetworkHistory: Error reading file" + e);
2548                }
2549            }
2550        } catch (IOException e) {
2551            loge("readNetworkHistory: No config file, revert to default" + e);
2552        }
2553
2554        if(in!=null) {
2555            try {
2556                in.close();
2557            } catch (Exception e) {
2558                loge("readNetworkHistory: Error closing file" + e);
2559            }
2560        }
2561    }
2562
2563    private void readAutoJoinConfig() {
2564        try (BufferedReader reader = new BufferedReader(new FileReader(autoJoinConfigFile))) {
2565            for (String key = reader.readLine(); key != null; key = reader.readLine()) {
2566                Log.d(TAG, "readAutoJoinConfig line: " + key);
2567
2568                int split = key.indexOf(':');
2569                if (split < 0) {
2570                    continue;
2571                }
2572
2573                String name = key.substring(0, split);
2574                Object reference = sKeyMap.get(name);
2575                if (reference == null) {
2576                    continue;
2577                }
2578
2579                try {
2580                    int value = Integer.parseInt(key.substring(split+1).trim());
2581                    if (reference.getClass() == AtomicBoolean.class) {
2582                        ((AtomicBoolean)reference).set(value != 0);
2583                    }
2584                    else {
2585                        ((AtomicInteger)reference).set(value);
2586                    }
2587                    Log.d(TAG,"readAutoJoinConfig: " + name + " = " + value);
2588                }
2589                catch (NumberFormatException nfe) {
2590                    Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
2591                }
2592            }
2593        } catch (IOException e) {
2594            loge("readAutoJoinStatus: Error parsing configuration" + e);
2595        }
2596    }
2597
2598
2599    private void writeIpAndProxyConfigurations() {
2600        final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
2601        for(WifiConfiguration config : mConfiguredNetworks.values()) {
2602            if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) {
2603                networks.put(configKey(config), config.getIpConfiguration());
2604            }
2605        }
2606
2607        super.writeIpAndProxyConfigurations(ipConfigFile, networks);
2608    }
2609
2610    private void readIpAndProxyConfigurations() {
2611        SparseArray<IpConfiguration> networks = super.readIpAndProxyConfigurations(ipConfigFile);
2612
2613        if (networks == null || networks.size() == 0) {
2614            // IpConfigStore.readIpAndProxyConfigurations has already logged an error.
2615            return;
2616        }
2617
2618        for (int i = 0; i < networks.size(); i++) {
2619            int id = networks.keyAt(i);
2620            WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id));
2621
2622
2623            if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED ||
2624                    config.ephemeral) {
2625                loge("configuration found for missing network, nid=" + id
2626                        +", ignored, networks.size=" + Integer.toString(networks.size()));
2627            } else {
2628                config.setIpConfiguration(networks.valueAt(i));
2629            }
2630        }
2631    }
2632
2633    /*
2634     * Convert string to Hexadecimal before passing to wifi native layer
2635     * In native function "doCommand()" have trouble in converting Unicode character string to UTF8
2636     * conversion to hex is required because SSIDs can have space characters in them;
2637     * and that can confuses the supplicant because it uses space charaters as delimiters
2638     */
2639
2640    public static String encodeSSID(String str){
2641        String tmp = removeDoubleQuotes(str);
2642        return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8"))));
2643    }
2644
2645    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config, int uid) {
2646        /*
2647         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
2648         * network configuration. Otherwise, the networkId should
2649         * refer to an existing configuration.
2650         */
2651
2652        if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
2653
2654        int netId = config.networkId;
2655        boolean newNetwork = false;
2656        // networkId of INVALID_NETWORK_ID means we want to create a new network
2657        if (netId == INVALID_NETWORK_ID) {
2658            Integer savedNetId = mNetworkIds.get(configKey(config));
2659            // Check if either we have a network Id or a WifiConfiguration
2660            // matching the one we are trying to add.
2661            if (savedNetId == null) {
2662                for (WifiConfiguration test : mConfiguredNetworks.values()) {
2663                    if (test.configKey().equals(config.configKey())) {
2664                        savedNetId = test.networkId;
2665                        loge("addOrUpdateNetworkNative " + config.configKey()
2666                                + " was found, but no network Id");
2667                        break;
2668                    }
2669                }
2670            }
2671            if (savedNetId != null) {
2672                netId = savedNetId;
2673            } else {
2674                if (mConfiguredHomeSPs.containsValue(config.FQDN)) {
2675                    loge("addOrUpdateNetworkNative passpoint " + config.FQDN
2676                            + " was found, but no network Id");
2677                }
2678                newNetwork = true;
2679                netId = mWifiNative.addNetwork();
2680                if (netId < 0) {
2681                    loge("Failed to add a network!");
2682                    return new NetworkUpdateResult(INVALID_NETWORK_ID);
2683                } else {
2684                    loge("addOrUpdateNetworkNative created netId=" + netId);
2685                }
2686            }
2687        }
2688
2689        boolean updateFailed = true;
2690
2691        setVariables: {
2692
2693            if (config.SSID != null &&
2694                    !mWifiNative.setNetworkVariable(
2695                        netId,
2696                        WifiConfiguration.ssidVarName,
2697                        encodeSSID(config.SSID))) {
2698                loge("failed to set SSID: "+config.SSID);
2699                break setVariables;
2700            }
2701
2702            if (config.isPasspoint()) {
2703                if (!mWifiNative.setNetworkVariable(
2704                            netId,
2705                            idStringVarName,
2706                            Long.toString(getChecksum(config.FQDN)))) {
2707                    loge("failed to set id_str: " + config.SSID);
2708                    break setVariables;
2709                }
2710            } else {
2711                loge("not writing id_str: "+config.SSID + " because not a passpoint network");
2712                loge("Config is : " + config);
2713            }
2714
2715            if (config.BSSID != null) {
2716                loge("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
2717                if (!mWifiNative.setNetworkVariable(
2718                        netId,
2719                        WifiConfiguration.bssidVarName,
2720                        config.BSSID)) {
2721                    loge("failed to set BSSID: " + config.BSSID);
2722                    break setVariables;
2723                }
2724            }
2725
2726            String allowedKeyManagementString =
2727                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
2728            if (config.allowedKeyManagement.cardinality() != 0 &&
2729                    !mWifiNative.setNetworkVariable(
2730                        netId,
2731                        WifiConfiguration.KeyMgmt.varName,
2732                        allowedKeyManagementString)) {
2733                loge("failed to set key_mgmt: "+
2734                        allowedKeyManagementString);
2735                break setVariables;
2736            }
2737
2738            String allowedProtocolsString =
2739                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
2740            if (config.allowedProtocols.cardinality() != 0 &&
2741                    !mWifiNative.setNetworkVariable(
2742                        netId,
2743                        WifiConfiguration.Protocol.varName,
2744                        allowedProtocolsString)) {
2745                loge("failed to set proto: "+
2746                        allowedProtocolsString);
2747                break setVariables;
2748            }
2749
2750            String allowedAuthAlgorithmsString =
2751                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
2752            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
2753                    !mWifiNative.setNetworkVariable(
2754                        netId,
2755                        WifiConfiguration.AuthAlgorithm.varName,
2756                        allowedAuthAlgorithmsString)) {
2757                loge("failed to set auth_alg: "+
2758                        allowedAuthAlgorithmsString);
2759                break setVariables;
2760            }
2761
2762            String allowedPairwiseCiphersString =
2763                    makeString(config.allowedPairwiseCiphers,
2764                    WifiConfiguration.PairwiseCipher.strings);
2765            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
2766                    !mWifiNative.setNetworkVariable(
2767                        netId,
2768                        WifiConfiguration.PairwiseCipher.varName,
2769                        allowedPairwiseCiphersString)) {
2770                loge("failed to set pairwise: "+
2771                        allowedPairwiseCiphersString);
2772                break setVariables;
2773            }
2774
2775            String allowedGroupCiphersString =
2776                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
2777            if (config.allowedGroupCiphers.cardinality() != 0 &&
2778                    !mWifiNative.setNetworkVariable(
2779                        netId,
2780                        WifiConfiguration.GroupCipher.varName,
2781                        allowedGroupCiphersString)) {
2782                loge("failed to set group: "+
2783                        allowedGroupCiphersString);
2784                break setVariables;
2785            }
2786
2787            // Prevent client screw-up by passing in a WifiConfiguration we gave it
2788            // by preventing "*" as a key.
2789            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
2790                    !mWifiNative.setNetworkVariable(
2791                        netId,
2792                        WifiConfiguration.pskVarName,
2793                        config.preSharedKey)) {
2794                loge("failed to set psk");
2795                break setVariables;
2796            }
2797
2798            boolean hasSetKey = false;
2799            if (config.wepKeys != null) {
2800                for (int i = 0; i < config.wepKeys.length; i++) {
2801                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
2802                    // by preventing "*" as a key.
2803                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
2804                        if (!mWifiNative.setNetworkVariable(
2805                                    netId,
2806                                    WifiConfiguration.wepKeyVarNames[i],
2807                                    config.wepKeys[i])) {
2808                            loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
2809                            break setVariables;
2810                        }
2811                        hasSetKey = true;
2812                    }
2813                }
2814            }
2815
2816            if (hasSetKey) {
2817                if (!mWifiNative.setNetworkVariable(
2818                            netId,
2819                            WifiConfiguration.wepTxKeyIdxVarName,
2820                            Integer.toString(config.wepTxKeyIndex))) {
2821                    loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
2822                    break setVariables;
2823                }
2824            }
2825
2826            if (!mWifiNative.setNetworkVariable(
2827                        netId,
2828                        WifiConfiguration.priorityVarName,
2829                        Integer.toString(config.priority))) {
2830                loge(config.SSID + ": failed to set priority: "
2831                        +config.priority);
2832                break setVariables;
2833            }
2834
2835            if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
2836                        netId,
2837                        WifiConfiguration.hiddenSSIDVarName,
2838                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
2839                loge(config.SSID + ": failed to set hiddenSSID: "+
2840                        config.hiddenSSID);
2841                break setVariables;
2842            }
2843
2844            if (config.requirePMF && !mWifiNative.setNetworkVariable(
2845                        netId,
2846                        WifiConfiguration.pmfVarName,
2847                        "2")) {
2848                loge(config.SSID + ": failed to set requirePMF: "+
2849                        config.requirePMF);
2850                break setVariables;
2851            }
2852
2853            if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable(
2854                    netId,
2855                    WifiConfiguration.updateIdentiferVarName,
2856                    config.updateIdentifier)) {
2857                loge(config.SSID + ": failed to set updateIdentifier: "+
2858                        config.updateIdentifier);
2859                break setVariables;
2860            }
2861
2862            if (config.enterpriseConfig != null &&
2863                    config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
2864
2865                WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
2866
2867                if (needsKeyStore(enterpriseConfig)) {
2868                    /**
2869                     * Keyguard settings may eventually be controlled by device policy.
2870                     * We check here if keystore is unlocked before installing
2871                     * credentials.
2872                     * TODO: Do we need a dialog here ?
2873                     */
2874                    if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
2875                        loge(config.SSID + ": key store is locked");
2876                        break setVariables;
2877                    }
2878
2879                    try {
2880                        /* config passed may include only fields being updated.
2881                         * In order to generate the key id, fetch uninitialized
2882                         * fields from the currently tracked configuration
2883                         */
2884                        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
2885                        String keyId = config.getKeyIdForCredentials(currentConfig);
2886
2887                        if (!installKeys(enterpriseConfig, keyId)) {
2888                            loge(config.SSID + ": failed to install keys");
2889                            break setVariables;
2890                        }
2891                    } catch (IllegalStateException e) {
2892                        loge(config.SSID + " invalid config for key installation");
2893                        break setVariables;
2894                    }
2895                }
2896
2897                HashMap<String, String> enterpriseFields = enterpriseConfig.getFields();
2898                for (String key : enterpriseFields.keySet()) {
2899                        String value = enterpriseFields.get(key);
2900                        if (key.equals("password") && value != null && value.equals("*")) {
2901                            // No need to try to set an obfuscated password, which will fail
2902                            continue;
2903                        }
2904                        if (key.equals(WifiEnterpriseConfig.REALM_KEY)
2905                                || key.equals(WifiEnterpriseConfig.PLMN_KEY)) {
2906                            // No need to save realm or PLMN in supplicant
2907                            continue;
2908                        }
2909                        if (!mWifiNative.setNetworkVariable(
2910                                    netId,
2911                                    key,
2912                                    value)) {
2913                            removeKeys(enterpriseConfig);
2914                            loge(config.SSID + ": failed to set " + key +
2915                                    ": " + value);
2916                            break setVariables;
2917                        }
2918                }
2919            }
2920            updateFailed = false;
2921        } // End of setVariables
2922
2923        if (updateFailed) {
2924            if (newNetwork) {
2925                mWifiNative.removeNetwork(netId);
2926                loge("Failed to set a network variable, removed network: " + netId);
2927            }
2928            return new NetworkUpdateResult(INVALID_NETWORK_ID);
2929        }
2930
2931        /* An update of the network variables requires reading them
2932         * back from the supplicant to update mConfiguredNetworks.
2933         * This is because some of the variables (SSID, wep keys &
2934         * passphrases) reflect different values when read back than
2935         * when written. For example, wep key is stored as * irrespective
2936         * of the value sent to the supplicant
2937         */
2938        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
2939        if (currentConfig == null) {
2940            currentConfig = new WifiConfiguration();
2941            currentConfig.setIpAssignment(IpAssignment.DHCP);
2942            currentConfig.setProxySettings(ProxySettings.NONE);
2943            currentConfig.networkId = netId;
2944            if (config != null) {
2945                // Carry over the creation parameters
2946                currentConfig.selfAdded = config.selfAdded;
2947                currentConfig.didSelfAdd = config.didSelfAdd;
2948                currentConfig.ephemeral = config.ephemeral;
2949                currentConfig.autoJoinUseAggressiveJoinAttemptThreshold
2950                        = config.autoJoinUseAggressiveJoinAttemptThreshold;
2951                currentConfig.lastConnectUid = config.lastConnectUid;
2952                currentConfig.lastUpdateUid = config.lastUpdateUid;
2953                currentConfig.creatorUid = config.creatorUid;
2954                currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
2955                currentConfig.FQDN = config.FQDN;
2956                currentConfig.providerFriendlyName = config.providerFriendlyName;
2957                currentConfig.roamingConsortiumIds = config.roamingConsortiumIds;
2958            }
2959            if (DBG) {
2960                loge("created new config netId=" + Integer.toString(netId)
2961                        + " uid=" + Integer.toString(currentConfig.creatorUid));
2962            }
2963        }
2964
2965        /* save HomeSP object for passpoint networks */
2966        if (config.isPasspoint()) {
2967            if (newNetwork == false) {
2968                /* when updating a network, we'll just create a new HomeSP */
2969                mConfiguredHomeSPs.remove(netId);
2970            }
2971
2972            Credential credential = new Credential(config.enterpriseConfig);
2973            HomeSP homeSP = new HomeSP(Collections.<String, Long>emptyMap(), config.FQDN,
2974                    config.roamingConsortiumIds, Collections.<String>emptySet(),
2975                    Collections.<Long>emptySet(), Collections.<Long>emptyList(),
2976                    config.providerFriendlyName, null, credential);
2977            mConfiguredHomeSPs.put(netId, homeSP);
2978            log("created a homeSP object for " + config.networkId + ":" + config.SSID);
2979
2980            /* fix enterprise config properties for passpoint */
2981            currentConfig.enterpriseConfig.setRealm(config.enterpriseConfig.getRealm());
2982            currentConfig.enterpriseConfig.setPlmn(config.enterpriseConfig.getPlmn());
2983        }
2984
2985        if (uid >= 0) {
2986            if (newNetwork) {
2987                currentConfig.creatorUid = uid;
2988            } else {
2989                currentConfig.lastUpdateUid = uid;
2990            }
2991        }
2992
2993        if (newNetwork) {
2994            currentConfig.dirty = true;
2995        }
2996
2997        if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
2998            // Make sure the configuration is not deleted anymore since we just
2999            // added or modified it.
3000            currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
3001            currentConfig.selfAdded = false;
3002            currentConfig.didSelfAdd = false;
3003            if (DBG) {
3004                loge("remove deleted status netId=" + Integer.toString(netId)
3005                        + " " + currentConfig.configKey());
3006            }
3007        }
3008
3009        if (currentConfig.status == WifiConfiguration.Status.ENABLED) {
3010            // Make sure autojoin remain in sync with user modifying the configuration
3011            currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
3012        }
3013
3014        if (currentConfig.configKey().equals(getLastSelectedConfiguration()) &&
3015                currentConfig.ephemeral) {
3016            // Make the config non-ephemeral since the user just explicitly clicked it.
3017            currentConfig.ephemeral = false;
3018            if (DBG) loge("remove ephemeral status netId=" + Integer.toString(netId)
3019                    + " " + currentConfig.configKey());
3020        }
3021
3022        if (DBG) loge("will read network variables netId=" + Integer.toString(netId));
3023
3024        readNetworkVariables(currentConfig);
3025
3026        mConfiguredNetworks.put(netId, currentConfig);
3027        mNetworkIds.put(configKey(currentConfig), netId);
3028
3029        NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
3030        result.setIsNewNetwork(newNetwork);
3031        result.setNetworkId(netId);
3032
3033        writePasspointConfigs();
3034        writeKnownNetworkHistory(false);
3035
3036        return result;
3037    }
3038
3039    public Collection<HomeSP> getHomeSPs() {
3040        return mConfiguredHomeSPs.values();
3041    }
3042
3043    public WifiConfiguration getWifiConfigForHomeSP(HomeSP homeSP) {
3044        for (Map.Entry<Integer, HomeSP> e : mConfiguredHomeSPs.entrySet()) {
3045            if (homeSP.equals(e.getValue())) {
3046                Integer networkId = e.getKey();
3047                return mConfiguredNetworks.get(networkId);
3048            }
3049        }
3050
3051        Log.e(TAG, "Could not find network for homeSP " + homeSP.getFQDN());
3052        return null;
3053    }
3054
3055    public ScanDetailCache getScanDetailCache(WifiConfiguration config) {
3056        if (config == null) return null;
3057        ScanDetailCache cache = mScanDetailCaches.get(config.networkId);
3058        if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
3059            cache = new ScanDetailCache(config);
3060            mScanDetailCaches.put(config.networkId, cache);
3061        }
3062        return cache;
3063    }
3064
3065    /**
3066     * This function run thru the Saved WifiConfigurations and check if some should be linked.
3067     * @param config
3068     */
3069    public void linkConfiguration(WifiConfiguration config) {
3070
3071        if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 6) {
3072            // Ignore configurations with large number of BSSIDs
3073            return;
3074        }
3075        if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
3076            // Only link WPA_PSK config
3077            return;
3078        }
3079        for (WifiConfiguration link : mConfiguredNetworks.values()) {
3080            boolean doLink = false;
3081
3082            if (link.configKey().equals(config.configKey())) {
3083                continue;
3084            }
3085
3086            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.ephemeral) {
3087                continue;
3088            }
3089
3090            // Autojoin will be allowed to dynamically jump from a linked configuration
3091            // to another, hence only link configurations that have equivalent level of security
3092            if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) {
3093                continue;
3094            }
3095
3096            ScanDetailCache linkedScanDetailCache = getScanDetailCache(link);
3097            if (linkedScanDetailCache != null && linkedScanDetailCache.size() > 6) {
3098                // Ignore configurations with large number of BSSIDs
3099                continue;
3100            }
3101
3102            if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) {
3103                // If both default GW are known, link only if they are equal
3104                if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) {
3105                    if (VDBG) {
3106                        loge("linkConfiguration link due to same gw " + link.SSID +
3107                                " and " + config.SSID + " GW " + config.defaultGwMacAddress);
3108                    }
3109                    doLink = true;
3110                }
3111            } else {
3112                // We do not know BOTH default gateways hence we will try to link
3113                // hoping that WifiConfigurations are indeed behind the same gateway.
3114                // once both WifiConfiguration have been tried and thus once both efault gateways
3115                // are known we will revisit the choice of linking them
3116                if ((getScanDetailCache(config) != null)
3117                        && (getScanDetailCache(config).size() <= 6)) {
3118
3119                    for (String abssid : getScanDetailCache(config).keySet()) {
3120                        for (String bbssid : linkedScanDetailCache.keySet()) {
3121                            if (VVDBG) {
3122                                loge("linkConfiguration try to link due to DBDC BSSID match "
3123                                        + link.SSID +
3124                                        " and " + config.SSID + " bssida " + abssid
3125                                        + " bssidb " + bbssid);
3126                            }
3127                            if (abssid.regionMatches(true, 0, bbssid, 0, 16)) {
3128                                // If first 16 ascii characters of BSSID matches,
3129                                // we assume this is a DBDC
3130                                doLink = true;
3131                            }
3132                        }
3133                    }
3134                }
3135            }
3136
3137            if (doLink == true && onlyLinkSameCredentialConfigurations) {
3138                String apsk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
3139                String bpsk = readNetworkVariableFromSupplicantFile(config.SSID, "psk");
3140                if (apsk == null || bpsk == null
3141                        || TextUtils.isEmpty(apsk) || TextUtils.isEmpty(apsk)
3142                        || apsk.equals("*") || apsk.equals(DELETED_CONFIG_PSK)
3143                        || !apsk.equals(bpsk)) {
3144                    doLink = false;
3145                }
3146            }
3147
3148            if (doLink) {
3149                if (VDBG) {
3150                   loge("linkConfiguration: will link " + link.configKey()
3151                           + " and " + config.configKey());
3152                }
3153                if (link.linkedConfigurations == null) {
3154                    link.linkedConfigurations = new HashMap<String, Integer>();
3155                }
3156                if (config.linkedConfigurations == null) {
3157                    config.linkedConfigurations = new HashMap<String, Integer>();
3158                }
3159                if (link.linkedConfigurations.get(config.configKey()) == null) {
3160                    link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
3161                    link.dirty = true;
3162                }
3163                if (config.linkedConfigurations.get(link.configKey()) == null) {
3164                    config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
3165                    config.dirty = true;
3166                }
3167            } else {
3168                if (link.linkedConfigurations != null
3169                        && (link.linkedConfigurations.get(config.configKey()) != null)) {
3170                    if (VDBG) {
3171                        loge("linkConfiguration: un-link " + config.configKey()
3172                                + " from " + link.configKey());
3173                    }
3174                    link.dirty = true;
3175                    link.linkedConfigurations.remove(config.configKey());
3176                }
3177                if (config.linkedConfigurations != null
3178                        && (config.linkedConfigurations.get(link.configKey()) != null)) {
3179                    if (VDBG) {
3180                        loge("linkConfiguration: un-link " + link.configKey()
3181                                + " from " + config.configKey());
3182                    }
3183                    config.dirty = true;
3184                    config.linkedConfigurations.remove(link.configKey());
3185                }
3186            }
3187        }
3188    }
3189
3190    /*
3191     * We try to link a scan result with a WifiConfiguration for which SSID and
3192     * key management dont match,
3193     * for instance, we try identify the 5GHz SSID of a DBDC AP,
3194     * even though we know only of the 2.4GHz
3195     *
3196     * Obviously, this function is not optimal since it is used to compare every scan
3197     * result with every Saved WifiConfiguration, with a string.equals operation.
3198     * As a speed up, might be better to implement the mConfiguredNetworks store as a
3199     * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object
3200     * so as to speed this up. Also to prevent the tiny probability of hash collision.
3201     *
3202     */
3203    public WifiConfiguration associateWithConfiguration(ScanDetail scanDetail) {
3204
3205        ScanResult result = scanDetail.getScanResult();
3206        boolean doNotAdd = false;
3207        String configKey = WifiConfiguration.configKey(result);
3208        if (configKey == null) {
3209            if (DBG) loge("associateWithConfiguration(): no config key " );
3210            return null;
3211        }
3212
3213        // Need to compare with quoted string
3214        String SSID = "\"" + result.SSID + "\"";
3215
3216        if (VVDBG) {
3217            loge("associateWithConfiguration(): try " + configKey);
3218        }
3219
3220        Checksum csum = new CRC32();
3221        csum.update(SSID.getBytes(), 0, SSID.getBytes().length);
3222        if (mDeletedSSIDs.contains(csum.getValue())) {
3223            doNotAdd = true;
3224        }
3225
3226        WifiConfiguration config = null;
3227        for (WifiConfiguration link : mConfiguredNetworks.values()) {
3228            boolean doLink = false;
3229
3230            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.selfAdded ||
3231                    link.ephemeral) {
3232                if (VVDBG) loge("associateWithConfiguration(): skip selfadd " + link.configKey() );
3233                // Make sure we dont associate the scan result to a deleted config
3234                continue;
3235            }
3236
3237            if (!link.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
3238                if (VVDBG) loge("associateWithConfiguration(): skip non-PSK " + link.configKey() );
3239                // Make sure we dont associate the scan result to a non-PSK config
3240                continue;
3241            }
3242
3243            if (configKey.equals(link.configKey())) {
3244                if (VVDBG) loge("associateWithConfiguration(): found it!!! " + configKey );
3245                return link; // Found it exactly
3246            }
3247
3248            ScanDetailCache linkedScanDetailCache = getScanDetailCache(link);
3249            if (!doNotAdd
3250                    && (linkedScanDetailCache != null) && (linkedScanDetailCache.size() <= 6)) {
3251                for (String bssid : linkedScanDetailCache.keySet()) {
3252                    if (result.BSSID.regionMatches(true, 0, bssid, 0, 16)
3253                            && SSID.regionMatches(false, 0, link.SSID, 0, 4)) {
3254                        // If first 16 ascii characters of BSSID matches, and first 3
3255                        // characters of SSID match, we assume this is a home setup
3256                        // and thus we will try to transfer the password from the known
3257                        // BSSID/SSID to the recently found BSSID/SSID
3258
3259                        // If (VDBG)
3260                        //    loge("associateWithConfiguration OK " );
3261                        doLink = true;
3262                        break;
3263                    }
3264                }
3265            }
3266
3267            if (doLink) {
3268                // Try to make a non verified WifiConfiguration, but only if the original
3269                // configuration was not self already added
3270                if (VDBG) {
3271                    loge("associateWithConfiguration: try to create " +
3272                            result.SSID + " and associate it with: " + link.SSID
3273                            + " key " + link.configKey());
3274                }
3275                config = wifiConfigurationFromScanResult(scanDetail);
3276                if (config != null) {
3277                    config.selfAdded = true;
3278                    config.didSelfAdd = true;
3279                    config.dirty = true;
3280                    config.peerWifiConfiguration = link.configKey();
3281                    if (config.allowedKeyManagement.equals(link.allowedKeyManagement) &&
3282                            config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
3283                        if (VDBG && config != null) {
3284                            loge("associateWithConfiguration: got a config from beacon"
3285                                    + config.SSID + " key " + config.configKey());
3286                        }
3287                        // Transfer the credentials from the configuration we are linking from
3288                        String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
3289                        if (psk != null) {
3290                            config.preSharedKey = psk;
3291                            if (VDBG) {
3292                                if (config.preSharedKey != null)
3293                                    loge(" transfer PSK : " + config.preSharedKey);
3294                            }
3295
3296                            // Link configurations
3297                            if (link.linkedConfigurations == null) {
3298                                link.linkedConfigurations = new HashMap<String, Integer>();
3299                            }
3300                            if (config.linkedConfigurations == null) {
3301                                config.linkedConfigurations = new HashMap<String, Integer>();
3302                            }
3303                            link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
3304                            config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
3305
3306                            // Carry over the Ip configuration
3307                            if (link.getIpConfiguration() != null) {
3308                                config.setIpConfiguration(link.getIpConfiguration());
3309                            }
3310                        } else {
3311                            config = null;
3312                        }
3313                    } else {
3314                        config = null;
3315                    }
3316                    if (config != null) break;
3317                }
3318                if (VDBG && config != null) {
3319                    loge("associateWithConfiguration: success, created: " + config.SSID
3320                            + " key " + config.configKey());
3321                }
3322            }
3323        }
3324        return config;
3325    }
3326
3327    public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) {
3328        if (config == null)
3329            return null;
3330        long now_ms = System.currentTimeMillis();
3331
3332        HashSet<Integer> channels = new HashSet<Integer>();
3333
3334        //get channels for this configuration, if there are at least 2 BSSIDs
3335        if (getScanDetailCache(config) == null && config.linkedConfigurations == null) {
3336            return null;
3337        }
3338
3339        if (VDBG) {
3340            StringBuilder dbg = new StringBuilder();
3341            dbg.append("makeChannelList age=" + Integer.toString(age)
3342                    + " for " + config.configKey()
3343                    + " max=" + maxNumActiveChannelsForPartialScans);
3344            if (getScanDetailCache(config) != null) {
3345                dbg.append(" bssids=" + getScanDetailCache(config).size());
3346            }
3347            if (config.linkedConfigurations != null) {
3348                dbg.append(" linked=" + config.linkedConfigurations.size());
3349            }
3350            loge(dbg.toString());
3351        }
3352
3353        int numChannels = 0;
3354        if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 0) {
3355            for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
3356                ScanResult result = scanDetail.getScanResult();
3357                //TODO : cout active and passive channels separately
3358                if (numChannels > maxNumActiveChannelsForPartialScans.get()) {
3359                    break;
3360                }
3361                if (VDBG) {
3362                    boolean test = (now_ms - result.seen) < age;
3363                    loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
3364                            + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
3365                }
3366                if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
3367                    channels.add(result.frequency);
3368                    numChannels++;
3369                }
3370            }
3371        }
3372
3373        //get channels for linked configurations
3374        if (config.linkedConfigurations != null) {
3375            for (String key : config.linkedConfigurations.keySet()) {
3376                WifiConfiguration linked = getWifiConfiguration(key);
3377                if (linked == null)
3378                    continue;
3379                if (getScanDetailCache(linked) == null) {
3380                    continue;
3381                }
3382                for (ScanDetail scanDetail : getScanDetailCache(linked).values()) {
3383                    ScanResult result = scanDetail.getScanResult();
3384                    if (VDBG) {
3385                        loge("has link: " + result.BSSID
3386                                + " freq=" + Integer.toString(result.frequency)
3387                                + " age=" + Long.toString(now_ms - result.seen));
3388                    }
3389                    if (numChannels > maxNumActiveChannelsForPartialScans.get()) {
3390                        break;
3391                    }
3392                    if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
3393                        channels.add(result.frequency);
3394                        numChannels++;
3395                    }
3396                }
3397            }
3398        }
3399        return channels;
3400    }
3401
3402    // !!! JNo >>
3403
3404    // !!! Call from addToScanCache
3405    private Map<HomeSP, PasspointMatch> matchPasspointNetworks(ScanDetail scanDetail) {
3406        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
3407        if (!networkDetail.has80211uInfo()) {
3408            return null;
3409        }
3410        updateAnqpCache(scanDetail, networkDetail.getANQPElements());
3411
3412        Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail,
3413                networkDetail.getANQPElements() == null);
3414        return matches;
3415    }
3416
3417    private Map<HomeSP, PasspointMatch> matchNetwork(ScanDetail scanDetail, boolean query) {
3418        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
3419
3420        ANQPData anqpData = mAnqpCache.getEntry(networkDetail);
3421
3422        Map<Constants.ANQPElementType, ANQPElement> anqpElements =
3423                anqpData != null ? anqpData.getANQPElements() : null;
3424
3425        boolean queried = !query;
3426        Collection<HomeSP> homeSPs = getHomeSPs();
3427        Map<HomeSP, PasspointMatch> matches = new HashMap<>(homeSPs.size());
3428        for (HomeSP homeSP : homeSPs) {
3429            PasspointMatch match = homeSP.match(networkDetail, anqpElements);
3430
3431            if (match == PasspointMatch.Incomplete && networkDetail.isInterworking() && !queried) {
3432                if (mAnqpCache.initiate(networkDetail)) {
3433                    mSupplicantBridge.startANQP(scanDetail);
3434                }
3435                queried = true;
3436            }
3437            matches.put(homeSP, match);
3438        }
3439        return matches;
3440    }
3441
3442    public void notifyANQPDone(Long bssid, boolean success) {
3443        mSupplicantBridge.notifyANQPDone(bssid, success);
3444    }
3445
3446    public void notifyANQPResponse(ScanDetail scanDetail,
3447                                   Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
3448
3449        updateAnqpCache(scanDetail, anqpElements);
3450        if (anqpElements == null) {
3451            return;
3452        }
3453
3454        Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, false);
3455        Log.d("HS2J", scanDetail.getSSID() + " 2nd Matches: " + toMatchString(matches));
3456
3457        cacheScanResultForPasspointConfig(scanDetail, matches);
3458    }
3459
3460
3461    private void updateAnqpCache(ScanDetail scanDetail,
3462                                 Map<Constants.ANQPElementType,ANQPElement> anqpElements)
3463    {
3464        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
3465
3466        if (anqpElements == null) {
3467            ANQPData data = mAnqpCache.getEntry(networkDetail);
3468            if (data != null) {
3469                scanDetail.propagateANQPInfo(data.getANQPElements());
3470            }
3471            return;
3472        }
3473
3474        mAnqpCache.update(networkDetail, anqpElements);
3475
3476        Log.d("HS2J", "Cached " + networkDetail.getBSSIDString() +
3477                "/" + networkDetail.getAnqpDomainID());
3478        scanDetail.propagateANQPInfo(anqpElements);
3479    }
3480
3481    private static String toMatchString(Map<HomeSP, PasspointMatch> matches) {
3482        StringBuilder sb = new StringBuilder();
3483        for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
3484            sb.append(' ').append(entry.getKey().getFQDN()).append("->").append(entry.getValue());
3485        }
3486        return sb.toString();
3487    }
3488
3489    // !!! << JNo
3490
3491    private void cacheScanResultForPasspointConfig(ScanDetail scanDetail,
3492                                           Map<HomeSP,PasspointMatch> matches) {
3493
3494        for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
3495            WifiConfiguration config = getWifiConfigForHomeSP(entry.getKey());
3496            if (config != null) {
3497                cacheScanResultForConfig(config, scanDetail);
3498            } else {
3499                /* perhaps the configuration was deleted?? */
3500            }
3501        }
3502    }
3503
3504    private void cacheScanResultForConfig(WifiConfiguration config, ScanDetail scanDetail) {
3505
3506        ScanResult scanResult = scanDetail.getScanResult();
3507
3508        if (config.autoJoinStatus >= WifiConfiguration.AUTO_JOIN_DELETED) {
3509            if (VVDBG) {
3510                loge("updateSavedNetworkHistory(): found a deleted, skip it...  "
3511                        + config.configKey());
3512            }
3513            // The scan result belongs to a deleted config:
3514            //   - increment numConfigFound to remember that we found a config
3515            //            matching for this scan result
3516            //   - dont do anything since the config was deleted, just skip...
3517            return;
3518        }
3519
3520        ScanDetailCache scanDetailCache = getScanDetailCache(config);
3521        if (scanDetailCache == null) {
3522            Log.w(TAG, "Could not allocate scan cache for " + config.SSID);
3523            return;
3524        }
3525
3526        // Adding a new BSSID
3527        ScanResult result = scanDetailCache.get(scanResult.BSSID);
3528        if (result != null) {
3529            // transfer the black list status
3530            scanResult.autoJoinStatus = result.autoJoinStatus;
3531            scanResult.blackListTimestamp = result.blackListTimestamp;
3532            scanResult.numIpConfigFailures = result.numIpConfigFailures;
3533            scanResult.numConnection = result.numConnection;
3534            scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate;
3535        }
3536
3537        if (config.ephemeral) {
3538            // For an ephemeral Wi-Fi config, the ScanResult should be considered
3539            // untrusted.
3540            scanResult.untrusted = true;
3541        }
3542
3543        if (scanDetailCache.size() > (maxNumScanCacheEntries + 64)) {
3544            long now_dbg = 0;
3545            if (VVDBG) {
3546                loge(" Will trim config " + config.configKey()
3547                        + " size " + scanDetailCache.size());
3548
3549                for (ScanDetail sd : scanDetailCache.values()) {
3550                    loge("     " + sd.getBSSIDString() + " " + sd.getSeen());
3551                }
3552                now_dbg = SystemClock.elapsedRealtimeNanos();
3553            }
3554            // Trim the scan result cache to maxNumScanCacheEntries entries max
3555            // Since this operation is expensive, make sure it is not performed
3556            // until the cache has grown significantly above the trim treshold
3557            scanDetailCache.trim(maxNumScanCacheEntries);
3558            if (VVDBG) {
3559                long diff = SystemClock.elapsedRealtimeNanos() - now_dbg;
3560                loge(" Finished trimming config, time(ns) " + diff);
3561                for (ScanDetail sd : scanDetailCache.values()) {
3562                    loge("     " + sd.getBSSIDString() + " " + sd.getSeen());
3563                }
3564            }
3565        }
3566
3567        // Add the scan result to this WifiConfiguration
3568        scanDetailCache.put(scanDetail);
3569        // Since we added a scan result to this configuration, re-attempt linking
3570        linkConfiguration(config);
3571    }
3572
3573
3574    // Update the WifiConfiguration database with the new scan result
3575    // A scan result can be associated to multiple WifiConfigurations
3576    public boolean updateSavedNetworkHistory(ScanDetail scanDetail) {
3577
3578        ScanResult scanResult = scanDetail.getScanResult();
3579        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
3580
3581        int numConfigFound = 0;
3582        if (scanResult == null)
3583            return false;
3584
3585        String SSID = "\"" + scanResult.SSID + "\"";
3586
3587        if (networkDetail.has80211uInfo()) {
3588            Map<HomeSP, PasspointMatch> matches = matchPasspointNetworks(scanDetail);
3589            cacheScanResultForPasspointConfig(scanDetail, matches);
3590            return matches.size() != 0;
3591        }
3592
3593        for (WifiConfiguration config : mConfiguredNetworks.values()) {
3594            boolean found = false;
3595
3596            if (config.SSID == null || !config.SSID.equals(SSID)) {
3597                // SSID mismatch
3598                if (VVDBG) {
3599                    loge("updateSavedNetworkHistory(): SSID mismatch " + config.configKey()
3600                            + " SSID=" + config.SSID + " " + SSID);
3601                }
3602                continue;
3603            }
3604            if (VDBG) {
3605                loge("updateSavedNetworkHistory(): try " + config.configKey()
3606                        + " SSID=" + config.SSID + " " + scanResult.SSID
3607                        + " " + scanResult.capabilities
3608                        + " ajst=" + config.autoJoinStatus);
3609            }
3610            if (scanResult.capabilities.contains("WEP")
3611                    && config.configKey().contains("WEP")) {
3612                found = true;
3613            } else if (scanResult.capabilities.contains("PSK")
3614                    && config.configKey().contains("PSK")) {
3615                found = true;
3616            } else if (scanResult.capabilities.contains("EAP")
3617                    && config.configKey().contains("EAP")) {
3618                found = true;
3619            } else if (!scanResult.capabilities.contains("WEP")
3620                && !scanResult.capabilities.contains("PSK")
3621                && !scanResult.capabilities.contains("EAP")
3622                && !config.configKey().contains("WEP")
3623                    && !config.configKey().contains("PSK")
3624                    && !config.configKey().contains("EAP")) {
3625                found = true;
3626            }
3627
3628            if (found) {
3629                numConfigFound ++;
3630                cacheScanResultForConfig(config, scanDetail);
3631            }
3632
3633            if (VDBG && found) {
3634                String status = "";
3635                if (scanResult.autoJoinStatus > 0) {
3636                    status = " status=" + Integer.toString(scanResult.autoJoinStatus);
3637                }
3638                loge("        got known scan result " +
3639                        scanResult.BSSID + " key : "
3640                        + config.configKey() + " num: " +
3641                        Integer.toString(getScanDetailCache(config).size())
3642                        + " rssi=" + Integer.toString(scanResult.level)
3643                        + " freq=" + Integer.toString(scanResult.frequency)
3644                        + status);
3645            }
3646        }
3647        return numConfigFound != 0;
3648    }
3649
3650    /* Compare current and new configuration and write to file on change */
3651    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
3652            WifiConfiguration currentConfig,
3653            WifiConfiguration newConfig) {
3654        boolean ipChanged = false;
3655        boolean proxyChanged = false;
3656
3657        if (VDBG) {
3658            loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +
3659                    newConfig.SSID + " path: " + ipConfigFile);
3660        }
3661
3662
3663        switch (newConfig.getIpAssignment()) {
3664            case STATIC:
3665                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
3666                    ipChanged = true;
3667                } else {
3668                    ipChanged = !Objects.equals(
3669                            currentConfig.getStaticIpConfiguration(),
3670                            newConfig.getStaticIpConfiguration());
3671                }
3672                break;
3673            case DHCP:
3674                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
3675                    ipChanged = true;
3676                }
3677                break;
3678            case UNASSIGNED:
3679                /* Ignore */
3680                break;
3681            default:
3682                loge("Ignore invalid ip assignment during write");
3683                break;
3684        }
3685
3686        switch (newConfig.getProxySettings()) {
3687            case STATIC:
3688            case PAC:
3689                ProxyInfo newHttpProxy = newConfig.getHttpProxy();
3690                ProxyInfo currentHttpProxy = currentConfig.getHttpProxy();
3691
3692                if (newHttpProxy != null) {
3693                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
3694                } else {
3695                    proxyChanged = (currentHttpProxy != null);
3696                }
3697                break;
3698            case NONE:
3699                if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
3700                    proxyChanged = true;
3701                }
3702                break;
3703            case UNASSIGNED:
3704                /* Ignore */
3705                break;
3706            default:
3707                loge("Ignore invalid proxy configuration during write");
3708                break;
3709        }
3710
3711        if (ipChanged) {
3712            currentConfig.setIpAssignment(newConfig.getIpAssignment());
3713            currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration());
3714            log("IP config changed SSID = " + currentConfig.SSID);
3715            if (currentConfig.getStaticIpConfiguration() != null) {
3716                log(" static configuration: " +
3717                    currentConfig.getStaticIpConfiguration().toString());
3718            }
3719        }
3720
3721        if (proxyChanged) {
3722            currentConfig.setProxySettings(newConfig.getProxySettings());
3723            currentConfig.setHttpProxy(newConfig.getHttpProxy());
3724            log("proxy changed SSID = " + currentConfig.SSID);
3725            if (currentConfig.getHttpProxy() != null) {
3726                log(" proxyProperties: " + currentConfig.getHttpProxy().toString());
3727            }
3728        }
3729
3730        if (ipChanged || proxyChanged) {
3731            writeIpAndProxyConfigurations();
3732            sendConfiguredNetworksChangedBroadcast(currentConfig,
3733                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
3734        }
3735        return new NetworkUpdateResult(ipChanged, proxyChanged);
3736    }
3737
3738    /** Returns true if a particular config key needs to be quoted when passed to the supplicant. */
3739    private boolean enterpriseConfigKeyShouldBeQuoted(String key) {
3740        switch (key) {
3741            case WifiEnterpriseConfig.EAP_KEY:
3742            case WifiEnterpriseConfig.ENGINE_KEY:
3743                return false;
3744            default:
3745                return true;
3746        }
3747    }
3748
3749    /**
3750     * Read the variables from the supplicant daemon that are needed to
3751     * fill in the WifiConfiguration object.
3752     *
3753     * @param config the {@link WifiConfiguration} object to be filled in.
3754     */
3755    private void readNetworkVariables(WifiConfiguration config) {
3756
3757        int netId = config.networkId;
3758        if (netId < 0)
3759            return;
3760
3761        /*
3762         * TODO: maybe should have a native method that takes an array of
3763         * variable names and returns an array of values. But we'd still
3764         * be doing a round trip to the supplicant daemon for each variable.
3765         */
3766        String value;
3767
3768        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
3769        if (!TextUtils.isEmpty(value)) {
3770            if (value.charAt(0) != '"') {
3771                config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
3772                //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
3773                //supplicant string
3774            } else {
3775                config.SSID = value;
3776            }
3777        } else {
3778            config.SSID = null;
3779        }
3780
3781        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
3782        if (!TextUtils.isEmpty(value)) {
3783            config.BSSID = value;
3784        } else {
3785            config.BSSID = null;
3786        }
3787
3788        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
3789        config.priority = -1;
3790        if (!TextUtils.isEmpty(value)) {
3791            try {
3792                config.priority = Integer.parseInt(value);
3793            } catch (NumberFormatException ignore) {
3794            }
3795        }
3796
3797        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
3798        config.hiddenSSID = false;
3799        if (!TextUtils.isEmpty(value)) {
3800            try {
3801                config.hiddenSSID = Integer.parseInt(value) != 0;
3802            } catch (NumberFormatException ignore) {
3803            }
3804        }
3805
3806        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
3807        config.wepTxKeyIndex = -1;
3808        if (!TextUtils.isEmpty(value)) {
3809            try {
3810                config.wepTxKeyIndex = Integer.parseInt(value);
3811            } catch (NumberFormatException ignore) {
3812            }
3813        }
3814
3815        for (int i = 0; i < 4; i++) {
3816            value = mWifiNative.getNetworkVariable(netId,
3817                    WifiConfiguration.wepKeyVarNames[i]);
3818            if (!TextUtils.isEmpty(value)) {
3819                config.wepKeys[i] = value;
3820            } else {
3821                config.wepKeys[i] = null;
3822            }
3823        }
3824
3825        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
3826        if (!TextUtils.isEmpty(value)) {
3827            config.preSharedKey = value;
3828        } else {
3829            config.preSharedKey = null;
3830        }
3831
3832        value = mWifiNative.getNetworkVariable(config.networkId,
3833                WifiConfiguration.Protocol.varName);
3834        if (!TextUtils.isEmpty(value)) {
3835            String vals[] = value.split(" ");
3836            for (String val : vals) {
3837                int index =
3838                    lookupString(val, WifiConfiguration.Protocol.strings);
3839                if (0 <= index) {
3840                    config.allowedProtocols.set(index);
3841                }
3842            }
3843        }
3844
3845        value = mWifiNative.getNetworkVariable(config.networkId,
3846                WifiConfiguration.KeyMgmt.varName);
3847        if (!TextUtils.isEmpty(value)) {
3848            String vals[] = value.split(" ");
3849            for (String val : vals) {
3850                int index =
3851                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
3852                if (0 <= index) {
3853                    config.allowedKeyManagement.set(index);
3854                }
3855            }
3856        }
3857
3858        value = mWifiNative.getNetworkVariable(config.networkId,
3859                WifiConfiguration.AuthAlgorithm.varName);
3860        if (!TextUtils.isEmpty(value)) {
3861            String vals[] = value.split(" ");
3862            for (String val : vals) {
3863                int index =
3864                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
3865                if (0 <= index) {
3866                    config.allowedAuthAlgorithms.set(index);
3867                }
3868            }
3869        }
3870
3871        value = mWifiNative.getNetworkVariable(config.networkId,
3872                WifiConfiguration.PairwiseCipher.varName);
3873        if (!TextUtils.isEmpty(value)) {
3874            String vals[] = value.split(" ");
3875            for (String val : vals) {
3876                int index =
3877                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
3878                if (0 <= index) {
3879                    config.allowedPairwiseCiphers.set(index);
3880                }
3881            }
3882        }
3883
3884        value = mWifiNative.getNetworkVariable(config.networkId,
3885                WifiConfiguration.GroupCipher.varName);
3886        if (!TextUtils.isEmpty(value)) {
3887            String vals[] = value.split(" ");
3888            for (String val : vals) {
3889                int index =
3890                    lookupString(val, WifiConfiguration.GroupCipher.strings);
3891                if (0 <= index) {
3892                    config.allowedGroupCiphers.set(index);
3893                }
3894            }
3895        }
3896
3897        if (config.enterpriseConfig == null) {
3898            config.enterpriseConfig = new WifiEnterpriseConfig();
3899        }
3900        HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
3901        for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) {
3902            value = mWifiNative.getNetworkVariable(netId, key);
3903            if (!TextUtils.isEmpty(value)) {
3904                if (!enterpriseConfigKeyShouldBeQuoted(key)) {
3905                    value = removeDoubleQuotes(value);
3906                }
3907                enterpriseFields.put(key, value);
3908            } else {
3909                enterpriseFields.put(key, EMPTY_VALUE);
3910            }
3911        }
3912
3913        if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) {
3914            saveConfig();
3915        }
3916
3917        migrateCerts(config.enterpriseConfig);
3918        // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore);
3919    }
3920
3921    private static String removeDoubleQuotes(String string) {
3922        int length = string.length();
3923        if ((length > 1) && (string.charAt(0) == '"')
3924                && (string.charAt(length - 1) == '"')) {
3925            return string.substring(1, length - 1);
3926        }
3927        return string;
3928    }
3929
3930    private static String makeString(BitSet set, String[] strings) {
3931        StringBuffer buf = new StringBuffer();
3932        int nextSetBit = -1;
3933
3934        /* Make sure all set bits are in [0, strings.length) to avoid
3935         * going out of bounds on strings.  (Shouldn't happen, but...) */
3936        set = set.get(0, strings.length);
3937
3938        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
3939            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
3940        }
3941
3942        // remove trailing space
3943        if (set.cardinality() > 0) {
3944            buf.setLength(buf.length() - 1);
3945        }
3946
3947        return buf.toString();
3948    }
3949
3950    private int lookupString(String string, String[] strings) {
3951        int size = strings.length;
3952
3953        string = string.replace('-', '_');
3954
3955        for (int i = 0; i < size; i++)
3956            if (string.equals(strings[i]))
3957                return i;
3958
3959        // if we ever get here, we should probably add the
3960        // value to WifiConfiguration to reflect that it's
3961        // supported by the WPA supplicant
3962        loge("Failed to look-up a string: " + string);
3963
3964        return -1;
3965    }
3966
3967    /* return the allowed key management based on a scan result */
3968
3969    public WifiConfiguration wifiConfigurationFromScanResult(ScanDetail scanDetail) {
3970
3971        ScanResult result = scanDetail.getScanResult();
3972        WifiConfiguration config = new WifiConfiguration();
3973
3974        config.SSID = "\"" + result.SSID + "\"";
3975
3976        if (VDBG) {
3977            loge("WifiConfiguration from scan results " +
3978                    config.SSID + " cap " + result.capabilities);
3979        }
3980        if (result.capabilities.contains("WEP")) {
3981            config.allowedKeyManagement.set(KeyMgmt.NONE);
3982            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //?
3983            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
3984        }
3985
3986        if (result.capabilities.contains("PSK")) {
3987            config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
3988        }
3989
3990        if (result.capabilities.contains("EAP")) {
3991            //this is probably wrong, as we don't have a way to enter the enterprise config
3992            config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
3993            config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
3994        }
3995
3996        getScanDetailCache(config).put(scanDetail);
3997
3998        return config;
3999    }
4000
4001
4002    /* Returns a unique for a given configuration */
4003    private static int configKey(WifiConfiguration config) {
4004        String key = config.configKey();
4005        return key.hashCode();
4006    }
4007
4008    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4009        pw.println("Dump of WifiConfigStore");
4010        pw.println("mLastPriority " + mLastPriority);
4011        pw.println("Configured networks");
4012        for (WifiConfiguration conf : getConfiguredNetworks()) {
4013            pw.println(conf);
4014        }
4015        pw.println();
4016
4017        if (mLocalLog != null) {
4018            pw.println("WifiConfigStore - Log Begin ----");
4019            mLocalLog.dump(fd, pw, args);
4020            pw.println("WifiConfigStore - Log End ----");
4021        }
4022    }
4023
4024    public String getConfigFile() {
4025        return ipConfigFile;
4026    }
4027
4028    protected void loge(String s) {
4029        loge(s, false);
4030    }
4031
4032    protected void loge(String s, boolean stack) {
4033        if (stack) {
4034            Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
4035                    + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
4036                    + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
4037                    + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
4038        } else {
4039            Log.e(TAG, s);
4040        }
4041    }
4042
4043    protected void log(String s) {
4044        Log.d(TAG, s);
4045    }
4046
4047    private void localLog(String s) {
4048        if (mLocalLog != null) {
4049            mLocalLog.log(s);
4050        }
4051    }
4052
4053    private void localLog(String s, boolean force) {
4054        localLog(s);
4055        if (force) loge(s);
4056    }
4057
4058    private void localLog(String s, int netId) {
4059        if (mLocalLog == null) {
4060            return;
4061        }
4062
4063        WifiConfiguration config;
4064        synchronized(mConfiguredNetworks) {
4065            config = mConfiguredNetworks.get(netId);
4066        }
4067
4068        if (config != null) {
4069            mLocalLog.log(s + " " + config.getPrintableSsid() + " " + netId
4070                    + " status=" + config.status
4071                    + " key=" + config.configKey());
4072        } else {
4073            mLocalLog.log(s + " " + netId);
4074        }
4075    }
4076
4077    // Certificate and private key management for EnterpriseConfig
4078    static boolean needsKeyStore(WifiEnterpriseConfig config) {
4079        // Has no keys to be installed
4080        if (config.getClientCertificate() == null && config.getCaCertificate() == null)
4081            return false;
4082        return true;
4083    }
4084
4085    static boolean isHardwareBackedKey(PrivateKey key) {
4086        return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
4087    }
4088
4089    static boolean hasHardwareBackedKey(Certificate certificate) {
4090        return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
4091    }
4092
4093    static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
4094        String client = config.getClientCertificateAlias();
4095        if (!TextUtils.isEmpty(client)) {
4096            // a valid client certificate is configured
4097
4098            // BUGBUG: keyStore.get() never returns certBytes; because it is not
4099            // taking WIFI_UID as a parameter. It always looks for certificate
4100            // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
4101            // all certificates need software keystore until we get the get() API
4102            // fixed.
4103
4104            return true;
4105        }
4106
4107        /*
4108        try {
4109
4110            if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
4111                    .USER_CERTIFICATE + client);
4112
4113            CertificateFactory factory = CertificateFactory.getInstance("X.509");
4114            if (factory == null) {
4115                Slog.e(TAG, "Error getting certificate factory");
4116                return;
4117            }
4118
4119            byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
4120            if (certBytes != null) {
4121                Certificate cert = (X509Certificate) factory.generateCertificate(
4122                        new ByteArrayInputStream(certBytes));
4123
4124                if (cert != null) {
4125                    mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
4126
4127                    if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
4128                            .USER_CERTIFICATE + client);
4129                    if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
4130                            "does not need" ) + " software key store");
4131                } else {
4132                    Slog.d(TAG, "could not generate certificate");
4133                }
4134            } else {
4135                Slog.e(TAG, "Could not load client certificate " + Credentials
4136                        .USER_CERTIFICATE + client);
4137                mNeedsSoftwareKeystore = true;
4138            }
4139
4140        } catch(CertificateException e) {
4141            Slog.e(TAG, "Could not read certificates");
4142            mCaCert = null;
4143            mClientCertificate = null;
4144        }
4145        */
4146
4147        return false;
4148    }
4149
4150    /** called when CS ask WiFistateMachine to disconnect the current network
4151     * because the score is bad.
4152     */
4153    void handleBadNetworkDisconnectReport(int netId, WifiInfo info) {
4154        /* TODO verify the bad network is current */
4155        WifiConfiguration config = mConfiguredNetworks.get(netId);
4156        if (config != null) {
4157            if ((info.getRssi() < WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_24
4158                    && info.is24GHz()) || (info.getRssi() <
4159                            WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_5 && info.is5GHz())) {
4160                // We got disconnected and RSSI was bad, so disable light
4161                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
4162                        + WifiConfiguration.UNWANTED_BLACKLIST_SOFT_BUMP);
4163                loge("handleBadNetworkDisconnectReport (+4) "
4164                        + Integer.toString(netId) + " " + info);
4165            } else {
4166                // We got disabled but RSSI is good, so disable hard
4167                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
4168                        + WifiConfiguration.UNWANTED_BLACKLIST_HARD_BUMP);
4169                loge("handleBadNetworkDisconnectReport (+8) "
4170                        + Integer.toString(netId) + " " + info);
4171            }
4172        }
4173        // Record last time Connectivity Service switched us away from WiFi and onto Cell
4174        lastUnwantedNetworkDisconnectTimestamp = System.currentTimeMillis();
4175    }
4176
4177    boolean handleBSSIDBlackList(int netId, String BSSID, boolean enable) {
4178        boolean found = false;
4179        if (BSSID == null)
4180            return found;
4181
4182        // Look for the BSSID in our config store
4183        for (WifiConfiguration config : mConfiguredNetworks.values()) {
4184            if (getScanDetailCache(config) != null) {
4185                for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
4186                    if (scanDetail.getBSSIDString().equals(BSSID)) {
4187                        if (enable) {
4188                            scanDetail.getScanResult().setAutoJoinStatus(ScanResult.ENABLED);
4189                        } else {
4190                            // Black list the BSSID we were trying to join
4191                            // so as the Roam state machine
4192                            // doesn't pick it up over and over
4193                            scanDetail.getScanResult().setAutoJoinStatus(
4194                                    ScanResult.AUTO_ROAM_DISABLED);
4195                            found = true;
4196                        }
4197                    }
4198                }
4199            }
4200        }
4201        return found;
4202    }
4203
4204    int getMaxDhcpRetries() {
4205        return Settings.Global.getInt(mContext.getContentResolver(),
4206                Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
4207                DEFAULT_MAX_DHCP_RETRIES);
4208    }
4209
4210    void handleSSIDStateChange(int netId, boolean enabled, String message, String BSSID) {
4211        WifiConfiguration config = mConfiguredNetworks.get(netId);
4212        if (config != null) {
4213            if (enabled) {
4214                loge("SSID re-enabled for  " + config.configKey() +
4215                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
4216                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
4217                //TODO: http://b/16381983 Fix Wifi Network Blacklisting
4218                //TODO: really I don't know if re-enabling is right but we
4219                //TODO: should err on the side of trying to connect
4220                //TODO: even if the attempt will fail
4221                if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
4222                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
4223                }
4224            } else {
4225                loge("SSID temp disabled for  " + config.configKey() +
4226                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
4227                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
4228                if (message != null) {
4229                    loge(" message=" + message);
4230                }
4231                if (config.selfAdded && config.lastConnected == 0) {
4232                    // This is a network we self added, and we never succeeded,
4233                    // the user did not create this network and never entered its credentials,
4234                    // so we want to be very aggressive in disabling it completely.
4235                    removeConfigAndSendBroadcastIfNeeded(config.networkId);
4236                } else {
4237                    if (message != null) {
4238                        if (message.contains("no identity")) {
4239                            config.setAutoJoinStatus(
4240                                    WifiConfiguration.AUTO_JOIN_DISABLED_NO_CREDENTIALS);
4241                            if (DBG) {
4242                                loge("no identity blacklisted " + config.configKey() + " to "
4243                                        + Integer.toString(config.autoJoinStatus));
4244                            }
4245                        } else if (message.contains("WRONG_KEY")
4246                                || message.contains("AUTH_FAILED")) {
4247                            // This configuration has received an auth failure, so disable it
4248                            // temporarily because we don't want auto-join to try it out.
4249                            // this network may be re-enabled by the "usual"
4250                            // enableAllNetwork function
4251                            config.numAuthFailures++;
4252                            if (config.numAuthFailures > maxAuthErrorsToBlacklist) {
4253                                config.setAutoJoinStatus
4254                                        (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
4255                                disableNetwork(netId,
4256                                        WifiConfiguration.DISABLED_AUTH_FAILURE);
4257                                loge("Authentication failure, blacklist " + config.configKey() + " "
4258                                            + Integer.toString(config.networkId)
4259                                            + " num failures " + config.numAuthFailures);
4260                            }
4261                        } else if (message.contains("DHCP FAILURE")) {
4262                            config.numIpConfigFailures++;
4263                            config.lastConnectionFailure = System.currentTimeMillis();
4264                            int maxRetries = getMaxDhcpRetries();
4265                            // maxRetries == 0 means keep trying forever
4266                            if (maxRetries > 0 && config.numIpConfigFailures > maxRetries) {
4267                                /**
4268                                 * If we've exceeded the maximum number of retries for DHCP
4269                                 * to a given network, disable the network
4270                                 */
4271                                config.setAutoJoinStatus
4272                                        (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
4273                                disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE);
4274                                loge("DHCP failure, blacklist " + config.configKey() + " "
4275                                        + Integer.toString(config.networkId)
4276                                        + " num failures " + config.numIpConfigFailures);
4277                            }
4278
4279                            // Also blacklist the BSSId if we find it
4280                            ScanResult result = null;
4281                            String bssidDbg = "";
4282                            if (getScanDetailCache(config) != null && BSSID != null) {
4283                                result = getScanDetailCache(config).get(BSSID);
4284                            }
4285                            if (result != null) {
4286                                result.numIpConfigFailures ++;
4287                                bssidDbg = BSSID + " ipfail=" + result.numIpConfigFailures;
4288                                if (result.numIpConfigFailures > 3) {
4289                                    // Tell supplicant to stop trying this BSSID
4290                                    mWifiNative.addToBlacklist(BSSID);
4291                                    result.setAutoJoinStatus(ScanResult.AUTO_JOIN_DISABLED);
4292                                }
4293                            }
4294
4295                            if (DBG) {
4296                                loge("blacklisted " + config.configKey() + " to "
4297                                        + config.autoJoinStatus
4298                                        + " due to IP config failures, count="
4299                                        + config.numIpConfigFailures
4300                                        + " disableReason=" + config.disableReason
4301                                        + " " + bssidDbg);
4302                            }
4303                        } else if (message.contains("CONN_FAILED")) {
4304                            config.numConnectionFailures++;
4305                            if (config.numConnectionFailures > maxConnectionErrorsToBlacklist) {
4306                                config.setAutoJoinStatus
4307                                        (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
4308                                disableNetwork(netId,
4309                                        WifiConfiguration.DISABLED_ASSOCIATION_REJECT);
4310                                loge("Connection failure, blacklist " + config.configKey() + " "
4311                                        + config.networkId
4312                                        + " num failures " + config.numConnectionFailures);
4313                            }
4314                        }
4315                        message.replace("\n", "");
4316                        message.replace("\r", "");
4317                        config.lastFailure = message;
4318                    }
4319                }
4320            }
4321        }
4322    }
4323
4324    boolean installKeys(WifiEnterpriseConfig config, String name) {
4325        boolean ret = true;
4326        String privKeyName = Credentials.USER_PRIVATE_KEY + name;
4327        String userCertName = Credentials.USER_CERTIFICATE + name;
4328        String caCertName = Credentials.CA_CERTIFICATE + name;
4329        if (config.getClientCertificate() != null) {
4330            byte[] privKeyData = config.getClientPrivateKey().getEncoded();
4331            if (isHardwareBackedKey(config.getClientPrivateKey())) {
4332                // Hardware backed key store is secure enough to store keys un-encrypted, this
4333                // removes the need for user to punch a PIN to get access to these keys
4334                if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store");
4335                ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID,
4336                        KeyStore.FLAG_NONE);
4337            } else {
4338                // Software backed key store is NOT secure enough to store keys un-encrypted.
4339                // Save keys encrypted so they are protected with user's PIN. User will
4340                // have to unlock phone before being able to use these keys and connect to
4341                // networks.
4342                if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store");
4343                ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
4344                        KeyStore.FLAG_ENCRYPTED);
4345            }
4346            if (ret == false) {
4347                return ret;
4348            }
4349
4350            ret = putCertInKeyStore(userCertName, config.getClientCertificate());
4351            if (ret == false) {
4352                // Remove private key installed
4353                mKeyStore.delKey(privKeyName, Process.WIFI_UID);
4354                return ret;
4355            }
4356        }
4357
4358        if (config.getCaCertificate() != null) {
4359            ret = putCertInKeyStore(caCertName, config.getCaCertificate());
4360            if (ret == false) {
4361                if (config.getClientCertificate() != null) {
4362                    // Remove client key+cert
4363                    mKeyStore.delKey(privKeyName, Process.WIFI_UID);
4364                    mKeyStore.delete(userCertName, Process.WIFI_UID);
4365                }
4366                return ret;
4367            }
4368        }
4369
4370        // Set alias names
4371        if (config.getClientCertificate() != null) {
4372            config.setClientCertificateAlias(name);
4373            config.resetClientKeyEntry();
4374        }
4375
4376        if (config.getCaCertificate() != null) {
4377            config.setCaCertificateAlias(name);
4378            config.resetCaCertificate();
4379        }
4380
4381        return ret;
4382    }
4383
4384    private boolean putCertInKeyStore(String name, Certificate cert) {
4385        try {
4386            byte[] certData = Credentials.convertToPem(cert);
4387            if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore");
4388            return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
4389
4390        } catch (IOException e1) {
4391            return false;
4392        } catch (CertificateException e2) {
4393            return false;
4394        }
4395    }
4396
4397    void removeKeys(WifiEnterpriseConfig config) {
4398        String client = config.getClientCertificateAlias();
4399        // a valid client certificate is configured
4400        if (!TextUtils.isEmpty(client)) {
4401            if (DBG) Log.d(TAG, "removing client private key and user cert");
4402            mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
4403            mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
4404        }
4405
4406        String ca = config.getCaCertificateAlias();
4407        // a valid ca certificate is configured
4408        if (!TextUtils.isEmpty(ca)) {
4409            if (DBG) Log.d(TAG, "removing CA cert");
4410            mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
4411        }
4412    }
4413
4414
4415    /** Migrates the old style TLS config to the new config style. This should only be used
4416     * when restoring an old wpa_supplicant.conf or upgrading from a previous
4417     * platform version.
4418     * @return true if the config was updated
4419     * @hide
4420     */
4421    boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) {
4422        String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME);
4423        /*
4424         * If the old configuration value is not present, then there is nothing
4425         * to do.
4426         */
4427        if (TextUtils.isEmpty(oldPrivateKey)) {
4428            return false;
4429        } else {
4430            // Also ignore it if it's empty quotes.
4431            oldPrivateKey = removeDoubleQuotes(oldPrivateKey);
4432            if (TextUtils.isEmpty(oldPrivateKey)) {
4433                return false;
4434            }
4435        }
4436
4437        config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE);
4438        config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY,
4439                WifiEnterpriseConfig.ENGINE_ID_KEYSTORE);
4440
4441        /*
4442        * The old key started with the keystore:// URI prefix, but we don't
4443        * need that anymore. Trim it off if it exists.
4444        */
4445        final String keyName;
4446        if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) {
4447            keyName = new String(
4448                    oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length()));
4449        } else {
4450            keyName = oldPrivateKey;
4451        }
4452        config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName);
4453
4454        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY,
4455                config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""));
4456
4457        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY,
4458                config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""));
4459
4460        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY,
4461                config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""));
4462
4463        // Remove old private_key string so we don't run this again.
4464        mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE);
4465
4466        return true;
4467    }
4468
4469    /** Migrate certs from global pool to wifi UID if not already done */
4470    void migrateCerts(WifiEnterpriseConfig config) {
4471        String client = config.getClientCertificateAlias();
4472        // a valid client certificate is configured
4473        if (!TextUtils.isEmpty(client)) {
4474            if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) {
4475                mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1,
4476                        Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
4477                mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1,
4478                        Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
4479            }
4480        }
4481
4482        String ca = config.getCaCertificateAlias();
4483        // a valid ca certificate is configured
4484        if (!TextUtils.isEmpty(ca)) {
4485            if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) {
4486                mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1,
4487                        Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
4488            }
4489        }
4490    }
4491
4492}
4493