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