WifiConfigManager.java revision 7002981cd8cf4ed73e9bbb2c1a877c3594e0ab39
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 static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
20
21import android.app.admin.DeviceAdminInfo;
22import android.app.admin.DevicePolicyManagerInternal;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.pm.ApplicationInfo;
27import android.content.pm.PackageManager;
28import android.content.pm.UserInfo;
29import android.net.IpConfiguration;
30import android.net.IpConfiguration.IpAssignment;
31import android.net.IpConfiguration.ProxySettings;
32import android.net.NetworkInfo.DetailedState;
33import android.net.ProxyInfo;
34import android.net.StaticIpConfiguration;
35import android.net.wifi.PasspointManagementObjectDefinition;
36import android.net.wifi.ScanResult;
37import android.net.wifi.WifiConfiguration;
38import android.net.wifi.WifiConfiguration.KeyMgmt;
39import android.net.wifi.WifiConfiguration.Status;
40import android.net.wifi.WifiEnterpriseConfig;
41import android.net.wifi.WifiInfo;
42import android.net.wifi.WifiManager;
43import android.net.wifi.WifiScanner;
44import android.net.wifi.WpsInfo;
45import android.net.wifi.WpsResult;
46import android.os.Environment;
47import android.os.RemoteException;
48import android.os.SystemClock;
49import android.os.UserHandle;
50import android.os.UserManager;
51import android.provider.Settings;
52import android.security.KeyStore;
53import android.text.TextUtils;
54import android.util.LocalLog;
55import android.util.Log;
56import android.util.SparseArray;
57
58import com.android.internal.R;
59import com.android.server.LocalServices;
60import com.android.server.net.DelayedDiskWrite;
61import com.android.server.net.IpConfigStore;
62import com.android.server.wifi.anqp.ANQPElement;
63import com.android.server.wifi.anqp.ANQPFactory;
64import com.android.server.wifi.anqp.Constants;
65import com.android.server.wifi.hotspot2.ANQPData;
66import com.android.server.wifi.hotspot2.AnqpCache;
67import com.android.server.wifi.hotspot2.IconEvent;
68import com.android.server.wifi.hotspot2.NetworkDetail;
69import com.android.server.wifi.hotspot2.PasspointMatch;
70import com.android.server.wifi.hotspot2.SupplicantBridge;
71import com.android.server.wifi.hotspot2.Utils;
72import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager;
73import com.android.server.wifi.hotspot2.pps.Credential;
74import com.android.server.wifi.hotspot2.pps.HomeSP;
75
76import org.xml.sax.SAXException;
77
78import java.io.BufferedReader;
79import java.io.DataOutputStream;
80import java.io.File;
81import java.io.FileDescriptor;
82import java.io.FileNotFoundException;
83import java.io.FileReader;
84import java.io.IOException;
85import java.io.PrintWriter;
86import java.security.cert.X509Certificate;
87import java.text.DateFormat;
88import java.util.ArrayList;
89import java.util.BitSet;
90import java.util.Calendar;
91import java.util.Collection;
92import java.util.Collections;
93import java.util.Comparator;
94import java.util.Date;
95import java.util.HashMap;
96import java.util.HashSet;
97import java.util.List;
98import java.util.Map;
99import java.util.Objects;
100import java.util.Set;
101import java.util.concurrent.ConcurrentHashMap;
102import java.util.concurrent.atomic.AtomicBoolean;
103import java.util.concurrent.atomic.AtomicInteger;
104import java.util.zip.CRC32;
105import java.util.zip.Checksum;
106
107
108/**
109 * This class provides the API to manage configured
110 * wifi networks. The API is not thread safe is being
111 * used only from WifiStateMachine.
112 *
113 * It deals with the following
114 * - Add/update/remove a WifiConfiguration
115 *   The configuration contains two types of information.
116 *     = IP and proxy configuration that is handled by WifiConfigManager and
117 *       is saved to disk on any change.
118 *
119 *       The format of configuration file is as follows:
120 *       <version>
121 *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
122 *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
123 *       ..
124 *
125 *       (key, value) pairs for a given network are grouped together and can
126 *       be in any order. A EOS at the end of a set of (key, value) pairs
127 *       indicates that the next set of (key, value) pairs are for a new
128 *       network. A network is identified by a unique ID_KEY. If there is no
129 *       ID_KEY in the (key, value) pairs, the data is discarded.
130 *
131 *       An invalid version on read would result in discarding the contents of
132 *       the file. On the next write, the latest version is written to file.
133 *
134 *       Any failures during read or write to the configuration file are ignored
135 *       without reporting to the user since the likelihood of these errors are
136 *       low and the impact on connectivity is low.
137 *
138 *     = SSID & security details that is pushed to the supplicant.
139 *       supplicant saves these details to the disk on calling
140 *       saveConfigCommand().
141 *
142 *       We have two kinds of APIs exposed:
143 *        > public API calls that provide fine grained control
144 *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
145 *          removeNetwork(). For these calls, the config is not persisted
146 *          to the disk. (TODO: deprecate these calls in WifiManager)
147 *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
148 *          These calls persist the supplicant config to disk.
149 *
150 * - Maintain a list of configured networks for quick access
151 *
152 */
153public class WifiConfigManager {
154    public static final String TAG = "WifiConfigManager";
155    public static final int MAX_TX_PACKET_FOR_FULL_SCANS = 8;
156    public static final int MAX_RX_PACKET_FOR_FULL_SCANS = 16;
157    public static final int MAX_TX_PACKET_FOR_PARTIAL_SCANS = 40;
158    public static final int MAX_RX_PACKET_FOR_PARTIAL_SCANS = 80;
159    public static final boolean ROAM_ON_ANY = false;
160    public static final int MAX_NUM_SCAN_CACHE_ENTRIES = 128;
161    private static final boolean DBG = true;
162    private static final String PPS_FILE = "/data/misc/wifi/PerProviderSubscription.conf";
163    private static final String IP_CONFIG_FILE =
164            Environment.getDataDirectory() + "/misc/wifi/ipconfig.txt";
165
166    // The Wifi verbose log is provided as a way to persist the verbose logging settings
167    // for testing purpose.
168    // It is not intended for normal use.
169    private static final String WIFI_VERBOSE_LOGS_KEY = "WIFI_VERBOSE_LOGS";
170
171    // As we keep deleted PSK WifiConfiguration for a while, the PSK of
172    // those deleted WifiConfiguration is set to this random unused PSK
173    private static final String DELETED_CONFIG_PSK = "Mjkd86jEMGn79KhKll298Uu7-deleted";
174
175    /**
176     * The maximum number of times we will retry a connection to an access point
177     * for which we have failed in acquiring an IP address from DHCP. A value of
178     * N means that we will make N+1 connection attempts in all.
179     * <p>
180     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
181     * value if a Settings value is not present.
182     */
183    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
184
185    /**
186     * The threshold for each kind of error. If a network continuously encounter the same error more
187     * than the threshold times, this network will be disabled. -1 means unavailable.
188     */
189    private static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = {
190            -1, //  threshold for NETWORK_SELECTION_ENABLE
191            1,  //  threshold for DISABLED_BAD_LINK
192            5,  //  threshold for DISABLED_ASSOCIATION_REJECTION
193            5,  //  threshold for DISABLED_AUTHENTICATION_FAILURE
194            5,  //  threshold for DISABLED_DHCP_FAILURE
195            5,  //  threshold for DISABLED_DNS_FAILURE
196            1,  //  threshold for DISABLED_WPS_START
197            6,  //  threshold for DISABLED_TLS_VERSION_MISMATCH
198            1,  //  threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
199            1,  //  threshold for DISABLED_NO_INTERNET
200            1,  //  threshold for DISABLED_BY_WIFI_MANAGER
201            1   //  threshold for DISABLED_BY_USER_SWITCH
202    };
203
204    /**
205     * Timeout for each kind of error. After the timeout minutes, unblock the network again.
206     */
207    private static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT = {
208            Integer.MAX_VALUE,  // threshold for NETWORK_SELECTION_ENABLE
209            15,                 // threshold for DISABLED_BAD_LINK
210            5,                  // threshold for DISABLED_ASSOCIATION_REJECTION
211            5,                  // threshold for DISABLED_AUTHENTICATION_FAILURE
212            5,                  // threshold for DISABLED_DHCP_FAILURE
213            5,                  // threshold for DISABLED_DNS_FAILURE
214            0,                  // threshold for DISABLED_WPS_START
215            Integer.MAX_VALUE,  // threshold for DISABLED_TLS_VERSION
216            Integer.MAX_VALUE,  // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
217            Integer.MAX_VALUE,  // threshold for DISABLED_NO_INTERNET
218            Integer.MAX_VALUE,  // threshold for DISABLED_BY_WIFI_MANAGER
219            Integer.MAX_VALUE   // threshold for DISABLED_BY_USER_SWITCH
220    };
221
222    public final AtomicBoolean mEnableAutoJoinWhenAssociated = new AtomicBoolean();
223    public final AtomicBoolean mEnableChipWakeUpWhenAssociated = new AtomicBoolean(true);
224    public final AtomicBoolean mEnableRssiPollWhenAssociated = new AtomicBoolean(true);
225    public final AtomicInteger mThresholdSaturatedRssi5 = new AtomicInteger();
226    public final AtomicInteger mThresholdQualifiedRssi24 = new AtomicInteger();
227    public final AtomicInteger mAlwaysEnableScansWhileAssociated = new AtomicInteger(0);
228    public final AtomicInteger mMaxNumActiveChannelsForPartialScans = new AtomicInteger();
229
230    public boolean mEnableLinkDebouncing;
231    public boolean mEnableWifiCellularHandoverUserTriggeredAdjustment;
232    public int mNetworkSwitchingBlackListPeriodMs;
233    public int mBadLinkSpeed24;
234    public int mBadLinkSpeed5;
235    public int mGoodLinkSpeed24;
236    public int mGoodLinkSpeed5;
237
238    // These fields are non-final for testing.
239    public AtomicInteger mThresholdQualifiedRssi5 = new AtomicInteger();
240    public AtomicInteger mThresholdMinimumRssi5 = new AtomicInteger();
241    public AtomicInteger mThresholdSaturatedRssi24 = new AtomicInteger();
242    public AtomicInteger mThresholdMinimumRssi24 = new AtomicInteger();
243    public AtomicInteger mCurrentNetworkBoost = new AtomicInteger();
244    public AtomicInteger mBandAward5Ghz = new AtomicInteger();
245
246    /**
247     * If Connectivity Service has triggered an unwanted network disconnect
248     */
249    public long mLastUnwantedNetworkDisconnectTimestamp = 0;
250
251    /**
252     * Framework keeps a list of ephemeral SSIDs that where deleted by user,
253     * so as, framework knows not to autojoin again those SSIDs based on scorer input.
254     * The list is never cleared up.
255     *
256     * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
257     */
258    public Set<String> mDeletedEphemeralSSIDs = new HashSet<String>();
259
260    /* configured networks with network id as the key */
261    private final ConfigurationMap mConfiguredNetworks;
262
263    private final LocalLog mLocalLog;
264    private final KeyStore mKeyStore;
265    private final WifiNetworkHistory mWifiNetworkHistory;
266    private final WifiConfigStore mWifiConfigStore;
267    private final AnqpCache mAnqpCache;
268    private final SupplicantBridge mSupplicantBridge;
269    private final SupplicantBridgeCallbacks mSupplicantBridgeCallbacks;
270    private final PasspointManagementObjectManager mMOManager;
271    private final boolean mEnableOsuQueries;
272    private final SIMAccessor mSIMAccessor;
273    private final UserManager mUserManager;
274    private final Object mActiveScanDetailLock = new Object();
275
276    private boolean mVerboseLoggingEnabled = false;
277    private Context mContext;
278    private FrameworkFacade mFacade;
279    private Clock mClock;
280    private IpConfigStore mIpconfigStore;
281    private DelayedDiskWrite mWriter;
282    private boolean mOnlyLinkSameCredentialConfigurations;
283    private int mCurrentUserId = UserHandle.USER_SYSTEM;
284
285    /* Stores a map of NetworkId to ScanCache */
286    private ConcurrentHashMap<Integer, ScanDetailCache> mScanDetailCaches;
287
288    /* Tracks the highest priority of configured networks */
289    private int mLastPriority = -1;
290
291    /**
292     * The mLastSelectedConfiguration is used to remember which network
293     * was selected last by the user.
294     * The connection to this network may not be successful, as well
295     * the selection (i.e. network priority) might not be persisted.
296     * WiFi state machine is the only object that sets this variable.
297     */
298    private String mLastSelectedConfiguration = null;
299    private long mLastSelectedTimeStamp =
300            WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
301
302    /*
303     * Lost config list, whenever we read a config from networkHistory.txt that was not in
304     * wpa_supplicant.conf
305     */
306    private HashSet<String> mLostConfigsDbg = new HashSet<String>();
307
308    private ScanDetail mActiveScanDetail;   // ScanDetail associated with active network
309
310    private class SupplicantBridgeCallbacks implements SupplicantBridge.SupplicantBridgeCallbacks {
311        @Override
312        public void notifyANQPResponse(ScanDetail scanDetail,
313                                       Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
314            updateAnqpCache(scanDetail, anqpElements);
315            if (anqpElements == null || anqpElements.isEmpty()) {
316                return;
317            }
318            scanDetail.propagateANQPInfo(anqpElements);
319
320            Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, false);
321            Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() + " pass 2 matches: "
322                    + toMatchString(matches));
323
324            cacheScanResultForPasspointConfigs(scanDetail, matches, null);
325        }
326        @Override
327        public void notifyIconFailed(long bssid) {
328            Intent intent = new Intent(WifiManager.PASSPOINT_ICON_RECEIVED_ACTION);
329            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
330            intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_BSSID, bssid);
331            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
332        }
333
334    }
335
336    WifiConfigManager(Context context, WifiNative wifiNative, FrameworkFacade facade, Clock clock,
337            UserManager userManager, KeyStore keyStore) {
338        mContext = context;
339        mFacade = facade;
340        mClock = clock;
341        mKeyStore = keyStore;
342        mUserManager = userManager;
343        mLocalLog = wifiNative.getLocalLog();
344
345        mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
346                R.bool.config_wifi_only_link_same_credential_configurations);
347        mMaxNumActiveChannelsForPartialScans.set(mContext.getResources().getInteger(
348                R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels));
349        mEnableLinkDebouncing = mContext.getResources().getBoolean(
350                R.bool.config_wifi_enable_disconnection_debounce);
351        mBandAward5Ghz.set(mContext.getResources().getInteger(
352                R.integer.config_wifi_framework_5GHz_preference_boost_factor));
353        mThresholdMinimumRssi5.set(mContext.getResources().getInteger(
354                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz));
355        mThresholdQualifiedRssi5.set(mContext.getResources().getInteger(
356                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz));
357        mThresholdSaturatedRssi5.set(mContext.getResources().getInteger(
358                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz));
359        mThresholdMinimumRssi24.set(mContext.getResources().getInteger(
360                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz));
361        mThresholdQualifiedRssi24.set(mContext.getResources().getInteger(
362                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz));
363        mThresholdSaturatedRssi24.set(mContext.getResources().getInteger(
364                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz));
365        mEnableWifiCellularHandoverUserTriggeredAdjustment = mContext.getResources().getBoolean(
366                R.bool.config_wifi_framework_cellular_handover_enable_user_triggered_adjustment);
367        mBadLinkSpeed24 = mContext.getResources().getInteger(
368                R.integer.config_wifi_framework_wifi_score_bad_link_speed_24);
369        mBadLinkSpeed5 = mContext.getResources().getInteger(
370                R.integer.config_wifi_framework_wifi_score_bad_link_speed_5);
371        mGoodLinkSpeed24 = mContext.getResources().getInteger(
372                R.integer.config_wifi_framework_wifi_score_good_link_speed_24);
373        mGoodLinkSpeed5 = mContext.getResources().getInteger(
374                R.integer.config_wifi_framework_wifi_score_good_link_speed_5);
375        mEnableAutoJoinWhenAssociated.set(mContext.getResources().getBoolean(
376                R.bool.config_wifi_framework_enable_associated_network_selection));
377        mCurrentNetworkBoost.set(mContext.getResources().getInteger(
378                R.integer.config_wifi_framework_current_network_boost));
379        mNetworkSwitchingBlackListPeriodMs = mContext.getResources().getInteger(
380                R.integer.config_wifi_network_switching_blacklist_time);
381
382        boolean hs2on = mContext.getResources().getBoolean(R.bool.config_wifi_hotspot2_enabled);
383        Log.d(Utils.hs2LogTag(getClass()), "Passpoint is " + (hs2on ? "enabled" : "disabled"));
384
385        mConfiguredNetworks = new ConfigurationMap(userManager);
386        mMOManager = new PasspointManagementObjectManager(new File(PPS_FILE), hs2on);
387        mEnableOsuQueries = true;
388        mAnqpCache = new AnqpCache(mClock);
389        mSupplicantBridgeCallbacks = new SupplicantBridgeCallbacks();
390        mSupplicantBridge = new SupplicantBridge(wifiNative, mSupplicantBridgeCallbacks);
391        mScanDetailCaches = new ConcurrentHashMap<>(16, 0.75f, 2);
392        mSIMAccessor = new SIMAccessor(mContext);
393        mWriter = new DelayedDiskWrite();
394        mIpconfigStore = new IpConfigStore(mWriter);
395        mWifiNetworkHistory = new WifiNetworkHistory(context, mLocalLog, mWriter);
396        mWifiConfigStore = new WifiConfigStore(wifiNative, mKeyStore, mLocalLog);
397    }
398
399    public void trimANQPCache(boolean all) {
400        mAnqpCache.clear(all, DBG);
401    }
402
403    void enableVerboseLogging(int verbose) {
404        if (verbose > 0) {
405            mVerboseLoggingEnabled = true;
406        } else {
407            mVerboseLoggingEnabled = false;
408        }
409        mWifiConfigStore.enableVerboseLogging(mVerboseLoggingEnabled);
410    }
411
412    /**
413     * Fetch the list of configured networks
414     * and enable all stored networks in supplicant.
415     */
416    void loadAndEnableAllNetworks() {
417        if (DBG) log("Loading config and enabling all networks ");
418        loadConfiguredNetworks();
419        enableAllNetworks();
420    }
421
422    int getConfiguredNetworksSize() {
423        return mConfiguredNetworks.sizeForCurrentUser();
424    }
425
426    boolean getVerboseLoggingEnabled() {
427        return mVerboseLoggingEnabled;
428    }
429
430    /**
431     * Fetch the list of currently saved networks (i.e. all configured networks, excluding
432     * ephemeral networks).
433     * @param pskMap Map of preSharedKeys, keyed by the configKey of the configuration the
434     * preSharedKeys belong to
435     * @return List of networks
436     */
437    private List<WifiConfiguration> getSavedNetworks(Map<String, String> pskMap) {
438        List<WifiConfiguration> networks = new ArrayList<>();
439        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
440            WifiConfiguration newConfig = new WifiConfiguration(config);
441            // When updating this condition, update WifiStateMachine's CONNECT_NETWORK handler to
442            // correctly handle updating existing configs that are filtered out here.
443            if (config.ephemeral) {
444                // Do not enumerate and return this configuration to anyone (e.g. WiFi Picker);
445                // treat it as unknown instead. This configuration can still be retrieved
446                // directly by its key or networkId.
447                continue;
448            }
449
450            if (pskMap != null && config.allowedKeyManagement != null
451                    && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
452                    && pskMap.containsKey(config.configKey(true))) {
453                newConfig.preSharedKey = pskMap.get(config.configKey(true));
454            }
455            networks.add(newConfig);
456        }
457        return networks;
458    }
459
460    /**
461     * This function returns all configuration, and is used for debug and creating bug reports.
462     */
463    private List<WifiConfiguration> getAllConfiguredNetworks() {
464        List<WifiConfiguration> networks = new ArrayList<>();
465        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
466            WifiConfiguration newConfig = new WifiConfiguration(config);
467            networks.add(newConfig);
468        }
469        return networks;
470    }
471
472    /**
473     * Fetch the list of currently saved networks (i.e. all configured networks, excluding
474     * ephemeral networks).
475     * @return List of networks
476     */
477    public List<WifiConfiguration> getSavedNetworks() {
478        return getSavedNetworks(null);
479    }
480
481    /**
482     * Fetch the list of currently saved networks (i.e. all configured networks, excluding
483     * ephemeral networks), filled with real preSharedKeys.
484     * @return List of networks
485     */
486    List<WifiConfiguration> getPrivilegedSavedNetworks() {
487        Map<String, String> pskMap = getCredentialsByConfigKeyMap();
488        List<WifiConfiguration> configurations = getSavedNetworks(pskMap);
489        for (WifiConfiguration configuration : configurations) {
490            try {
491                configuration
492                        .setPasspointManagementObjectTree(mMOManager.getMOTree(configuration.FQDN));
493            } catch (IOException ioe) {
494                Log.w(TAG, "Failed to parse MO from " + configuration.FQDN + ": " + ioe);
495            }
496        }
497        return configurations;
498    }
499
500    /**
501     * Fetch the list of networkId's which are hidden in current user's configuration.
502     * @return List of networkIds
503     */
504    public Set<Integer> getHiddenConfiguredNetworkIds() {
505        return mConfiguredNetworks.getHiddenNetworkIdsForCurrentUser();
506    }
507
508    /**
509     * Find matching network for this scanResult
510     */
511    WifiConfiguration getMatchingConfig(ScanResult scanResult) {
512
513        for (Map.Entry entry : mScanDetailCaches.entrySet()) {
514            Integer netId = (Integer) entry.getKey();
515            ScanDetailCache cache = (ScanDetailCache) entry.getValue();
516            WifiConfiguration config = getWifiConfiguration(netId);
517            if (config == null) {
518                continue;
519            }
520            if (cache.get(scanResult.BSSID) != null) {
521                return config;
522            }
523        }
524
525        return null;
526    }
527
528    /**
529     * Fetch the preSharedKeys for all networks.
530     * @return a map from configKey to preSharedKey.
531     */
532    private Map<String, String> getCredentialsByConfigKeyMap() {
533        return readNetworkVariablesFromSupplicantFile("psk");
534    }
535
536    /**
537     *  Update the configuration and BSSID with latest RSSI value.
538     */
539    void updateConfiguration(WifiInfo info) {
540        WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
541        if (config != null && getScanDetailCache(config) != null) {
542            ScanDetail scanDetail = getScanDetailCache(config).getScanDetail(info.getBSSID());
543            if (scanDetail != null) {
544                ScanResult result = scanDetail.getScanResult();
545                long previousSeen = result.seen;
546                int previousRssi = result.level;
547
548                // Update the scan result
549                scanDetail.setSeen();
550                result.level = info.getRssi();
551
552                // Average the RSSI value
553                result.averageRssi(previousRssi, previousSeen,
554                        WifiQualifiedNetworkSelector.SCAN_RESULT_MAXIMUNM_AGE);
555                if (mVerboseLoggingEnabled) {
556                    logd("updateConfiguration freq=" + result.frequency
557                            + " BSSID=" + result.BSSID
558                            + " RSSI=" + result.level
559                            + " " + config.configKey());
560                }
561            }
562        }
563    }
564
565    /**
566     * get the Wificonfiguration for this netId
567     *
568     * @return Wificonfiguration
569     */
570    public WifiConfiguration getWifiConfiguration(int netId) {
571        return mConfiguredNetworks.getForCurrentUser(netId);
572    }
573
574    /**
575     * Get the Wificonfiguration for this key
576     * @return Wificonfiguration
577     */
578    public WifiConfiguration getWifiConfiguration(String key) {
579        return mConfiguredNetworks.getByConfigKeyForCurrentUser(key);
580    }
581
582    /**
583     * Enable all networks (if disabled time expire) and save config. This will be a no-op if the
584     * list of configured networks indicates all networks as being enabled
585     */
586    void enableAllNetworks() {
587        boolean networkEnabledStateChanged = false;
588
589        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
590            if (config != null && !config.ephemeral
591                    && !config.getNetworkSelectionStatus().isNetworkEnabled()) {
592                if (tryEnableQualifiedNetwork(config)) {
593                    networkEnabledStateChanged = true;
594                }
595            }
596        }
597
598        if (networkEnabledStateChanged) {
599            saveConfig();
600            sendConfiguredNetworksChangedBroadcast();
601        }
602    }
603
604    private boolean setNetworkPriorityNative(WifiConfiguration config, int priority) {
605        return mWifiConfigStore.setNetworkPriority(config, priority);
606    }
607
608    private boolean setSSIDNative(WifiConfiguration config, String ssid) {
609        return mWifiConfigStore.setNetworkSSID(config, ssid);
610    }
611
612    public boolean updateLastConnectUid(WifiConfiguration config, int uid) {
613        if (config != null) {
614            if (config.lastConnectUid != uid) {
615                config.lastConnectUid = uid;
616                return true;
617            }
618        }
619        return false;
620    }
621
622    /**
623     * Selects the specified network for connection. This involves
624     * updating the priority of all the networks and enabling the given
625     * network while disabling others.
626     *
627     * Selecting a network will leave the other networks disabled and
628     * a call to enableAllNetworks() needs to be issued upon a connection
629     * or a failure event from supplicant
630     *
631     * @param config network to select for connection
632     * @param updatePriorities makes config highest priority network
633     * @return false if the network id is invalid
634     */
635    boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) {
636        if (mVerboseLoggingEnabled) localLogNetwork("selectNetwork", config.networkId);
637        if (config.networkId == INVALID_NETWORK_ID) return false;
638        if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
639                mUserManager.getProfiles(mCurrentUserId))) {
640            loge("selectNetwork " + Integer.toString(config.networkId) + ": Network config is not "
641                    + "visible to current user.");
642            return false;
643        }
644
645        // Reset the priority of each network at start or if it goes too high.
646        if (mLastPriority == -1 || mLastPriority > 1000000) {
647            if (updatePriorities) {
648                for (WifiConfiguration config2 : mConfiguredNetworks.valuesForCurrentUser()) {
649                    if (config2.networkId != INVALID_NETWORK_ID) {
650                        setNetworkPriorityNative(config2, 0);
651                    }
652                }
653            }
654            mLastPriority = 0;
655        }
656
657        // Set to the highest priority and save the configuration.
658        if (updatePriorities) {
659            setNetworkPriorityNative(config, ++mLastPriority);
660        }
661
662        if (config.isPasspoint()) {
663            /* need to slap on the SSID of selected bssid to work */
664            if (getScanDetailCache(config).size() != 0) {
665                ScanDetail result = getScanDetailCache(config).getFirst();
666                if (result == null) {
667                    loge("Could not find scan result for " + config.BSSID);
668                } else {
669                    logd("Setting SSID for " + config.networkId + " to" + result.getSSID());
670                    setSSIDNative(config, result.getSSID());
671                }
672
673            } else {
674                loge("Could not find bssid for " + config);
675            }
676        }
677
678        mWifiConfigStore.enableHS20(config.isPasspoint());
679
680        if (updatePriorities) {
681            saveConfig();
682        }
683
684        updateLastConnectUid(config, uid);
685
686        writeKnownNetworkHistory();
687
688        /* Enable the given network while disabling all other networks */
689        selectNetworkWithoutBroadcast(config.networkId);
690
691       /* Avoid saving the config & sending a broadcast to prevent settings
692        * from displaying a disabled list of networks */
693        return true;
694    }
695
696    /**
697     * Add/update the specified configuration and save config
698     *
699     * @param config WifiConfiguration to be saved
700     * @return network update result
701     */
702    NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) {
703        WifiConfiguration conf;
704
705        // A new network cannot have null SSID
706        if (config == null || (config.networkId == INVALID_NETWORK_ID && config.SSID == null)) {
707            return new NetworkUpdateResult(INVALID_NETWORK_ID);
708        }
709
710        if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
711                mUserManager.getProfiles(mCurrentUserId))) {
712            return new NetworkUpdateResult(INVALID_NETWORK_ID);
713        }
714
715        if (mVerboseLoggingEnabled) {
716            localLogNetwork("WifiConfigManager: saveNetwork netId", config.networkId);
717        }
718        if (mVerboseLoggingEnabled) {
719            logd("WifiConfigManager saveNetwork,"
720                    + " size=" + Integer.toString(mConfiguredNetworks.sizeForAllUsers())
721                    + " (for all users)"
722                    + " SSID=" + config.SSID
723                    + " Uid=" + Integer.toString(config.creatorUid)
724                    + "/" + Integer.toString(config.lastUpdateUid));
725        }
726
727        if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
728            if (mVerboseLoggingEnabled) {
729                logd("WifiConfigManager: removed from ephemeral blacklist: " + config.SSID);
730            }
731            // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call
732            // below, since we're creating/modifying a config.
733        }
734
735        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
736        NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
737        int netId = result.getNetworkId();
738
739        if (mVerboseLoggingEnabled) {
740            localLogNetwork("WifiConfigManager: saveNetwork got it back netId=", netId);
741        }
742
743        conf = mConfiguredNetworks.getForCurrentUser(netId);
744        if (conf != null) {
745            if (!conf.getNetworkSelectionStatus().isNetworkEnabled()) {
746                if (mVerboseLoggingEnabled) {
747                    localLog("WifiConfigManager: re-enabling: " + conf.SSID);
748                }
749
750                // reenable autojoin, since new information has been provided
751                updateNetworkSelectionStatus(netId,
752                        WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
753            }
754            if (mVerboseLoggingEnabled) {
755                logd("WifiConfigManager: saveNetwork got config back netId="
756                        + Integer.toString(netId)
757                        + " uid=" + Integer.toString(config.creatorUid));
758            }
759        }
760
761        saveConfig();
762        sendConfiguredNetworksChangedBroadcast(
763                conf,
764                result.isNewNetwork()
765                        ? WifiManager.CHANGE_REASON_ADDED
766                        : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
767        return result;
768    }
769
770    void noteRoamingFailure(WifiConfiguration config, int reason) {
771        if (config == null) return;
772        config.lastRoamingFailure = mClock.currentTimeMillis();
773        config.roamingFailureBlackListTimeMilli =
774                2 * (config.roamingFailureBlackListTimeMilli + 1000);
775        if (config.roamingFailureBlackListTimeMilli > mNetworkSwitchingBlackListPeriodMs) {
776            config.roamingFailureBlackListTimeMilli = mNetworkSwitchingBlackListPeriodMs;
777        }
778        config.lastRoamingFailureReason = reason;
779    }
780
781    void saveWifiConfigBSSID(WifiConfiguration config, String bssid) {
782        mWifiConfigStore.setNetworkBSSID(config, bssid);
783    }
784
785
786    void updateStatus(int netId, DetailedState state) {
787        if (netId != INVALID_NETWORK_ID) {
788            WifiConfiguration config = mConfiguredNetworks.getForAllUsers(netId);
789            if (config == null) return;
790            switch (state) {
791                case CONNECTED:
792                    config.status = Status.CURRENT;
793                    //we successfully connected, hence remove the blacklist
794                    updateNetworkSelectionStatus(netId,
795                            WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
796                    break;
797                case DISCONNECTED:
798                    //If network is already disabled, keep the status
799                    if (config.status == Status.CURRENT) {
800                        config.status = Status.ENABLED;
801                    }
802                    break;
803                default:
804                    //do nothing, retain the existing state
805                    break;
806            }
807        }
808    }
809
810
811    /**
812     * Disable an ephemeral SSID for the purpose of auto-joining thru scored.
813     * This SSID will never be scored anymore.
814     * The only way to "un-disable it" is if the user create a network for that SSID and then
815     * forget it.
816     *
817     * @param ssid caller must ensure that the SSID passed thru this API match
818     *            the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
819     * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
820     *         disconnect if this is the current network.
821     */
822    WifiConfiguration disableEphemeralNetwork(String ssid) {
823        if (ssid == null) {
824            return null;
825        }
826
827        WifiConfiguration foundConfig = mConfiguredNetworks.getEphemeralForCurrentUser(ssid);
828
829        mDeletedEphemeralSSIDs.add(ssid);
830        logd("Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size());
831
832        if (foundConfig != null) {
833            logd("Found ephemeral config in disableEphemeralNetwork: " + foundConfig.networkId);
834        }
835
836        writeKnownNetworkHistory();
837        return foundConfig;
838    }
839
840    /**
841     * Forget the specified network and save config
842     *
843     * @param netId network to forget
844     * @return {@code true} if it succeeds, {@code false} otherwise
845     */
846    boolean forgetNetwork(int netId) {
847        if (mVerboseLoggingEnabled) localLogNetwork("forgetNetwork", netId);
848        if (!removeNetwork(netId)) {
849            loge("Failed to forget network " + netId);
850            return false;
851        }
852        saveConfig();
853        writeKnownNetworkHistory();
854        return true;
855    }
856
857    /**
858     * Add/update a network. Note that there is no saveConfig operation.
859     * This function is retained for compatibility with the public
860     * API. The more powerful saveNetwork() is used by the
861     * state machine
862     *
863     * @param config wifi configuration to add/update
864     * @return network Id
865     */
866    int addOrUpdateNetwork(WifiConfiguration config, int uid) {
867        if (config == null || !WifiConfigurationUtil.isVisibleToAnyProfile(config,
868                mUserManager.getProfiles(mCurrentUserId))) {
869            return WifiConfiguration.INVALID_NETWORK_ID;
870        }
871
872        if (mVerboseLoggingEnabled) localLogNetwork("addOrUpdateNetwork id=", config.networkId);
873        if (config.isPasspoint()) {
874            /* create a temporary SSID with providerFriendlyName */
875            Long csum = getChecksum(config.FQDN);
876            config.SSID = csum.toString();
877            config.enterpriseConfig.setDomainSuffixMatch(config.FQDN);
878        }
879
880        NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
881        if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
882            WifiConfiguration conf = mConfiguredNetworks.getForCurrentUser(result.getNetworkId());
883            if (conf != null) {
884                sendConfiguredNetworksChangedBroadcast(
885                        conf,
886                        result.isNewNetwork
887                                ? WifiManager.CHANGE_REASON_ADDED
888                                : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
889            }
890        }
891
892        return result.getNetworkId();
893    }
894
895    public int addPasspointManagementObject(String managementObject) {
896        try {
897            mMOManager.addSP(managementObject);
898            return 0;
899        } catch (IOException | SAXException e) {
900            return -1;
901        }
902    }
903
904    public int modifyPasspointMo(String fqdn, List<PasspointManagementObjectDefinition> mos) {
905        try {
906            return mMOManager.modifySP(fqdn, mos);
907        } catch (IOException | SAXException e) {
908            return -1;
909        }
910    }
911
912    public boolean queryPasspointIcon(long bssid, String fileName) {
913        return mSupplicantBridge.doIconQuery(bssid, fileName);
914    }
915
916    public int matchProviderWithCurrentNetwork(String fqdn) {
917        ScanDetail scanDetail = null;
918        synchronized (mActiveScanDetailLock) {
919            scanDetail = mActiveScanDetail;
920        }
921        if (scanDetail == null) {
922            return PasspointMatch.None.ordinal();
923        }
924        HomeSP homeSP = mMOManager.getHomeSP(fqdn);
925        if (homeSP == null) {
926            return PasspointMatch.None.ordinal();
927        }
928
929        ANQPData anqpData = mAnqpCache.getEntry(scanDetail.getNetworkDetail());
930
931        Map<Constants.ANQPElementType, ANQPElement> anqpElements =
932                anqpData != null ? anqpData.getANQPElements() : null;
933
934        return homeSP.match(scanDetail.getNetworkDetail(), anqpElements, mSIMAccessor).ordinal();
935    }
936
937    /**
938     * General PnoNetwork list sorting algorithm:
939     * 1, Place the fully enabled networks first. Among the fully enabled networks,
940     * sort them in the oder determined by the return of |compareConfigurations| method
941     * implementation.
942     * 2. Next place all the temporarily disabled networks. Among the temporarily disabled
943     * networks, sort them in the order determined by the return of |compareConfigurations| method
944     * implementation.
945     * 3. Place the permanently disabled networks last. The order among permanently disabled
946     * networks doesn't matter.
947     */
948    private static class PnoListComparator implements Comparator<WifiConfiguration> {
949
950        public final int ENABLED_NETWORK_SCORE = 3;
951        public final int TEMPORARY_DISABLED_NETWORK_SCORE = 2;
952        public final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1;
953
954        @Override
955        public int compare(WifiConfiguration a, WifiConfiguration b) {
956            int configAScore = getPnoNetworkSortScore(a);
957            int configBScore = getPnoNetworkSortScore(b);
958            if (configAScore == configBScore) {
959                return compareConfigurations(a, b);
960            } else {
961                return Integer.compare(configBScore, configAScore);
962            }
963        }
964
965        // This needs to be implemented by the connected/disconnected PNO list comparator.
966        public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) {
967            return 0;
968        }
969
970        /**
971         * Returns an integer representing a score for each configuration. The scores are assigned
972         * based on the status of the configuration. The scores are assigned according to the order:
973         * Fully enabled network > Temporarily disabled network > Permanently disabled network.
974         */
975        private int getPnoNetworkSortScore(WifiConfiguration config) {
976            if (config.getNetworkSelectionStatus().isNetworkEnabled()) {
977                return ENABLED_NETWORK_SCORE;
978            } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
979                return TEMPORARY_DISABLED_NETWORK_SCORE;
980            } else {
981                return PERMANENTLY_DISABLED_NETWORK_SCORE;
982            }
983        }
984    }
985
986    /**
987     * Disconnected PnoNetwork list sorting algorithm:
988     * Place the configurations in descending order of their |numAssociation| values. If networks
989     * have the same |numAssociation|, then sort them in descending order of their |priority|
990     * values.
991     */
992    private static final PnoListComparator sDisconnectedPnoListComparator =
993            new PnoListComparator() {
994                @Override
995                public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) {
996                    if (a.numAssociation != b.numAssociation) {
997                        return Long.compare(b.numAssociation, a.numAssociation);
998                    } else {
999                        return Integer.compare(b.priority, a.priority);
1000                    }
1001                }
1002            };
1003
1004    /**
1005     * Retrieves an updated list of priorities for all the saved networks before
1006     * enabling disconnected PNO (wpa_supplicant based PNO).
1007     *
1008     * wpa_supplicant uses the priority of networks to build the list of SSID's to monitor
1009     * during PNO. If there are a lot of saved networks, this list will be truncated and we
1010     * might end up not connecting to the networks we use most frequently. So, We want the networks
1011     * to be re-sorted based on the relative |numAssociation| values.
1012     *
1013     * @return list of networks with updated priorities.
1014     */
1015    public ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrieveDisconnectedPnoNetworkList() {
1016        return retrievePnoNetworkList(sDisconnectedPnoListComparator);
1017    }
1018
1019    /**
1020     * Connected PnoNetwork list sorting algorithm:
1021     * Place the configurations with |lastSeenInQualifiedNetworkSelection| set first. If networks
1022     * have the same value, then sort them in descending order of their |numAssociation|
1023     * values.
1024     */
1025    private static final PnoListComparator sConnectedPnoListComparator =
1026            new PnoListComparator() {
1027                @Override
1028                public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) {
1029                    boolean isConfigALastSeen =
1030                            a.getNetworkSelectionStatus().getSeenInLastQualifiedNetworkSelection();
1031                    boolean isConfigBLastSeen =
1032                            b.getNetworkSelectionStatus().getSeenInLastQualifiedNetworkSelection();
1033                    if (isConfigALastSeen != isConfigBLastSeen) {
1034                        return Boolean.compare(isConfigBLastSeen, isConfigALastSeen);
1035                    } else {
1036                        return Long.compare(b.numAssociation, a.numAssociation);
1037                    }
1038                }
1039            };
1040
1041    /**
1042     * Retrieves an updated list of priorities for all the saved networks before
1043     * enabling connected PNO (HAL based ePno).
1044     *
1045     * @return list of networks with updated priorities.
1046     */
1047    public ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrieveConnectedPnoNetworkList() {
1048        return retrievePnoNetworkList(sConnectedPnoListComparator);
1049    }
1050
1051    /**
1052     * Create a PnoNetwork object from the provided WifiConfiguration.
1053     * @param config Configuration corresponding to the network.
1054     * @param newPriority New priority to be assigned to the network.
1055     */
1056    private static WifiScanner.PnoSettings.PnoNetwork createPnoNetworkFromWifiConfiguration(
1057            WifiConfiguration config, int newPriority) {
1058        WifiScanner.PnoSettings.PnoNetwork pnoNetwork =
1059                new WifiScanner.PnoSettings.PnoNetwork(config.SSID);
1060        pnoNetwork.networkId = config.networkId;
1061        pnoNetwork.priority = newPriority;
1062        if (config.hiddenSSID) {
1063            pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN;
1064        }
1065        pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND;
1066        pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND;
1067        if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
1068            pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK;
1069        } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
1070                || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
1071            pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL;
1072        } else {
1073            pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN;
1074        }
1075        return pnoNetwork;
1076    }
1077
1078    /**
1079     * Retrieves an updated list of priorities for all the saved networks before
1080     * enabling/disabling PNO.
1081     *
1082     * @param pnoListComparator The comparator to use for sorting networks
1083     * @return list of networks with updated priorities.
1084     */
1085    private ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList(
1086            PnoListComparator pnoListComparator) {
1087        ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
1088        ArrayList<WifiConfiguration> wifiConfigurations =
1089                new ArrayList<>(mConfiguredNetworks.valuesForCurrentUser());
1090        Collections.sort(wifiConfigurations, pnoListComparator);
1091        // Let's use the network list size as the highest priority and then go down from there.
1092        // So, the most frequently connected network has the highest priority now.
1093        int priority = wifiConfigurations.size();
1094        for (WifiConfiguration config : wifiConfigurations) {
1095            pnoList.add(createPnoNetworkFromWifiConfiguration(config, priority));
1096            priority--;
1097        }
1098        return pnoList;
1099    }
1100
1101    /**
1102     * Remove a network. Note that there is no saveConfig operation.
1103     * This function is retained for compatibility with the public
1104     * API. The more powerful forgetNetwork() is used by the
1105     * state machine for network removal
1106     *
1107     * @param netId network to be removed
1108     * @return {@code true} if it succeeds, {@code false} otherwise
1109     */
1110    boolean removeNetwork(int netId) {
1111        if (mVerboseLoggingEnabled) localLogNetwork("removeNetwork", netId);
1112        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1113        if (!removeConfigAndSendBroadcastIfNeeded(config)) {
1114            return false;
1115        }
1116        if (config.isPasspoint()) {
1117            writePasspointConfigs(config.FQDN, null);
1118        }
1119        return true;
1120    }
1121
1122    private static Long getChecksum(String source) {
1123        Checksum csum = new CRC32();
1124        csum.update(source.getBytes(), 0, source.getBytes().length);
1125        return csum.getValue();
1126    }
1127
1128    private boolean removeConfigWithoutBroadcast(WifiConfiguration config) {
1129        if (config == null) {
1130            return false;
1131        }
1132        if (!mWifiConfigStore.removeNetwork(config)) {
1133            loge("Failed to remove network " + config.networkId);
1134            return false;
1135        }
1136        if (config.configKey().equals(mLastSelectedConfiguration)) {
1137            mLastSelectedConfiguration = null;
1138        }
1139        mConfiguredNetworks.remove(config.networkId);
1140        mScanDetailCaches.remove(config.networkId);
1141        return true;
1142    }
1143
1144    private boolean removeConfigAndSendBroadcastIfNeeded(WifiConfiguration config) {
1145        if (!removeConfigWithoutBroadcast(config)) {
1146            return false;
1147        }
1148        String key = config.configKey();
1149        if (mVerboseLoggingEnabled) {
1150            logd("removeNetwork " + " key=" + key + " config.id=" + config.networkId);
1151        }
1152        writeIpAndProxyConfigurations();
1153        sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
1154        if (!config.ephemeral) {
1155            removeUserSelectionPreference(key);
1156        }
1157        writeKnownNetworkHistory();
1158        return true;
1159    }
1160
1161    private void removeUserSelectionPreference(String configKey) {
1162        if (DBG) {
1163            Log.d(TAG, "removeUserSelectionPreference: key is " + configKey);
1164        }
1165        if (configKey == null) {
1166            return;
1167        }
1168        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
1169            WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1170            String connectChoice = status.getConnectChoice();
1171            if (connectChoice != null && connectChoice.equals(configKey)) {
1172                Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
1173                        + " : " + config.networkId);
1174                status.setConnectChoice(null);
1175                status.setConnectChoiceTimestamp(WifiConfiguration.NetworkSelectionStatus
1176                            .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1177            }
1178        }
1179    }
1180
1181    /*
1182     * Remove all networks associated with an application
1183     *
1184     * @param packageName name of the package of networks to remove
1185     * @return {@code true} if all networks removed successfully, {@code false} otherwise
1186     */
1187    boolean removeNetworksForApp(ApplicationInfo app) {
1188        if (app == null || app.packageName == null) {
1189            return false;
1190        }
1191
1192        boolean success = true;
1193
1194        WifiConfiguration [] copiedConfigs =
1195                mConfiguredNetworks.valuesForCurrentUser().toArray(new WifiConfiguration[0]);
1196        for (WifiConfiguration config : copiedConfigs) {
1197            if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) {
1198                continue;
1199            }
1200            if (mVerboseLoggingEnabled) {
1201                localLog("Removing network " + config.SSID
1202                         + ", application \"" + app.packageName + "\" uninstalled"
1203                         + " from user " + UserHandle.getUserId(app.uid));
1204            }
1205            success &= removeNetwork(config.networkId);
1206        }
1207
1208        saveConfig();
1209
1210        return success;
1211    }
1212
1213    boolean removeNetworksForUser(int userId) {
1214        boolean success = true;
1215
1216        WifiConfiguration[] copiedConfigs =
1217                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1218        for (WifiConfiguration config : copiedConfigs) {
1219            if (userId != UserHandle.getUserId(config.creatorUid)) {
1220                continue;
1221            }
1222            success &= removeNetwork(config.networkId);
1223            if (mVerboseLoggingEnabled) {
1224                localLog("Removing network " + config.SSID
1225                        + ", user " + userId + " removed");
1226            }
1227        }
1228
1229        return success;
1230    }
1231
1232    /**
1233     * Enable a network. Note that there is no saveConfig operation.
1234     * This function is retained for compatibility with the public
1235     * API. The more powerful selectNetwork()/saveNetwork() is used by the
1236     * state machine for connecting to a network
1237     *
1238     * @param config network to be enabled
1239     * @return {@code true} if it succeeds, {@code false} otherwise
1240     */
1241    boolean enableNetwork(WifiConfiguration config, boolean disableOthers, int uid) {
1242        if (config == null) {
1243            return false;
1244        }
1245        updateNetworkSelectionStatus(
1246                config, WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1247        setLatestUserSelectedConfiguration(config);
1248        boolean ret = true;
1249        if (disableOthers) {
1250            ret = selectNetworkWithoutBroadcast(config.networkId);
1251            if (ret) {
1252                if (mVerboseLoggingEnabled) {
1253                    localLogNetwork("enableNetwork(disableOthers=true, uid=" + uid + ") ",
1254                            config.networkId);
1255                }
1256                markAllNetworksDisabledExcept(config.networkId,
1257                        WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
1258                updateLastConnectUid(config, uid);
1259                writeKnownNetworkHistory();
1260                sendConfiguredNetworksChangedBroadcast();
1261            }
1262        } else {
1263            if (mVerboseLoggingEnabled) {
1264                localLogNetwork("enableNetwork(disableOthers=false) ", config.networkId);
1265            }
1266            sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1267        }
1268        return ret;
1269    }
1270
1271    boolean selectNetworkWithoutBroadcast(int netId) {
1272        return mWifiConfigStore.selectNetwork(mConfiguredNetworks.getForCurrentUser(netId));
1273    }
1274
1275    /**
1276     * Disable a network in wpa_supplicant.
1277     */
1278    boolean disableNetworkNative(WifiConfiguration config) {
1279        return mWifiConfigStore.disableNetwork(config);
1280    }
1281
1282    /**
1283     * Disable all networks in wpa_supplicant.
1284     */
1285    void disableAllNetworksNative() {
1286        mWifiConfigStore.disableAllNetworks(mConfiguredNetworks.valuesForCurrentUser());
1287    }
1288
1289    /* Mark all networks except specified netId as disabled */
1290    private void markAllNetworksDisabledExcept(int netId, int disableReason) {
1291        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
1292            if (config != null && config.networkId != netId) {
1293                updateNetworkSelectionStatus(config, disableReason);
1294            }
1295        }
1296    }
1297
1298    private void markAllNetworksDisabled(int disableReason) {
1299        markAllNetworksDisabledExcept(WifiConfiguration.INVALID_NETWORK_ID, disableReason);
1300    }
1301
1302    /**
1303     * Update a network according to the update reason and its current state
1304     * @param netId The network ID of the network need update
1305     * @param reason The reason to update the network
1306     * @return false if no change made to the input configure file, can due to error or need not
1307     *         true the input config file has been changed
1308     */
1309    boolean updateNetworkSelectionStatus(int netId, int reason) {
1310        WifiConfiguration config = getWifiConfiguration(netId);
1311        return updateNetworkSelectionStatus(config, reason);
1312    }
1313
1314    /**
1315     * Update a network according to the update reason and its current state
1316     * @param config the network need update
1317     * @param reason The reason to update the network
1318     * @return false if no change made to the input configure file, can due to error or need not
1319     *         true the input config file has been changed
1320     */
1321    boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
1322        if (config == null) {
1323            return false;
1324        }
1325
1326        WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1327        if (reason == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1328            updateNetworkStatus(config, WifiConfiguration.NetworkSelectionStatus
1329                    .NETWORK_SELECTION_ENABLE);
1330            localLog("Enable network:" + config.configKey());
1331            return true;
1332        }
1333
1334        networkStatus.incrementDisableReasonCounter(reason);
1335        if (DBG) {
1336            localLog("Network:" + config.SSID + "disable counter of "
1337                    + WifiConfiguration.NetworkSelectionStatus.getNetworkDisableReasonString(reason)
1338                    + " is: " + networkStatus.getDisableReasonCounter(reason) + "and threshold is: "
1339                    + NETWORK_SELECTION_DISABLE_THRESHOLD[reason]);
1340        }
1341
1342        if (networkStatus.getDisableReasonCounter(reason)
1343                >= NETWORK_SELECTION_DISABLE_THRESHOLD[reason]) {
1344            return updateNetworkStatus(config, reason);
1345        }
1346        return true;
1347    }
1348
1349    /**
1350     * Attempt to re-enable a network for qualified network selection, if this network was either:
1351     *      a) Previously temporarily disabled, but its disable timeout has expired, or
1352     *      b) Previously disabled because of a user switch, but is now visible to the current
1353     *         user.
1354     * @param networkId the id of the network to be checked for possible unblock (due to timeout)
1355     * @return true if the network identified by {@param networkId} was re-enabled for qualified
1356     *         network selection, false otherwise.
1357     */
1358    public boolean tryEnableQualifiedNetwork(int networkId) {
1359        WifiConfiguration config = getWifiConfiguration(networkId);
1360        if (config == null) {
1361            localLog("updateQualifiedNetworkstatus invalid network.");
1362            return false;
1363        }
1364        return tryEnableQualifiedNetwork(config);
1365    }
1366
1367    /**
1368     * Attempt to re-enable a network for qualified network selection, if this network was either:
1369     *      a) Previously temporarily disabled, but its disable timeout has expired, or
1370     *      b) Previously disabled because of a user switch, but is now visible to the current
1371     *         user.
1372     * @param config configuration for the network to be re-enabled for network selection. The
1373     *               network corresponding to the config must be visible to the current user.
1374     * @return true if the network identified by {@param config} was re-enabled for qualified
1375     *         network selection, false otherwise.
1376     */
1377    private boolean tryEnableQualifiedNetwork(WifiConfiguration config) {
1378        WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1379        if (networkStatus.isNetworkTemporaryDisabled()) {
1380            //time difference in minutes
1381            long timeDifference =
1382                    (mClock.elapsedRealtime() - networkStatus.getDisableTime()) / 1000 / 60;
1383            if (timeDifference < 0 || timeDifference
1384                    >= NETWORK_SELECTION_DISABLE_TIMEOUT[
1385                    networkStatus.getNetworkSelectionDisableReason()]) {
1386                updateNetworkSelectionStatus(config.networkId,
1387                        WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1388                return true;
1389            }
1390        } else if (networkStatus.isDisabledByReason(
1391                WifiConfiguration.NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH)) {
1392            updateNetworkSelectionStatus(config.networkId,
1393                    WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1394            return true;
1395        }
1396        return false;
1397    }
1398
1399    /**
1400     * Update a network's status. Note that there is no saveConfig operation.
1401     * @param config network to be updated
1402     * @param reason reason code for updated
1403     * @return false if no change made to the input configure file, can due to error or need not
1404     *         true the input config file has been changed
1405     */
1406    boolean updateNetworkStatus(WifiConfiguration config, int reason) {
1407        localLog("updateNetworkStatus:" + (config == null ? null : config.SSID));
1408        if (config == null) {
1409            return false;
1410        }
1411
1412        WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1413        if (reason < 0 || reason >= WifiConfiguration.NetworkSelectionStatus
1414                .NETWORK_SELECTION_DISABLED_MAX) {
1415            localLog("Invalid Network disable reason: " + reason);
1416            return false;
1417        }
1418
1419        if (reason == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1420            if (networkStatus.isNetworkEnabled()) {
1421                if (DBG) {
1422                    localLog("Do nothing. Network is already enabled!");
1423                }
1424                return false;
1425            }
1426            networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
1427                    .NETWORK_SELECTION_ENABLED);
1428            networkStatus.setNetworkSelectionDisableReason(reason);
1429            networkStatus.setDisableTime(
1430                    WifiConfiguration.NetworkSelectionStatus
1431                    .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1432            networkStatus.clearDisableReasonCounter();
1433            config.status = Status.ENABLED;
1434            sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1435        } else {
1436            //disable the network
1437            if (networkStatus.isNetworkPermanentlyDisabled()) {
1438                if (DBG) {
1439                    localLog("Do nothing. Network is already permanently disabled! Disable reason: "
1440                            + WifiConfiguration.NetworkSelectionStatus
1441                            .getNetworkDisableReasonString(reason));
1442                }
1443                return false;
1444            } else if (networkStatus.isNetworkTemporaryDisabled()
1445                    && reason < WifiConfiguration.NetworkSelectionStatus
1446                    .DISABLED_TLS_VERSION_MISMATCH) {
1447                if (DBG) {
1448                    localLog("Do nothing. Network is already temporarily disabled! Disable reason: "
1449                            + WifiConfiguration.NetworkSelectionStatus
1450                            .getNetworkDisableReasonString(reason));
1451                }
1452                return false;
1453            }
1454            disableNetworkNative(config);
1455            if (reason < WifiConfiguration.NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
1456                networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
1457                        .NETWORK_SELECTION_TEMPORARY_DISABLED);
1458                networkStatus.setDisableTime(mClock.elapsedRealtime());
1459            } else {
1460                networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
1461                        .NETWORK_SELECTION_PERMANENTLY_DISABLED);
1462                config.status = Status.DISABLED;
1463                sendConfiguredNetworksChangedBroadcast(
1464                        config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1465            }
1466            networkStatus.setNetworkSelectionDisableReason(reason);
1467        }
1468        if (DBG) {
1469            String time = DateFormat.getDateTimeInstance().format(new Date());
1470            localLog("Network: " + config.SSID + " Configure new status: "
1471                    + networkStatus.getNetworkStatusString() + " with reason: "
1472                    + networkStatus.getNetworkDisableReasonString() + " at: " + time);
1473        }
1474        return true;
1475    }
1476
1477    /**
1478     * Save the configured networks in supplicant to disk
1479     * @return {@code true} if it succeeds, {@code false} otherwise
1480     */
1481    boolean saveConfig() {
1482        return mWifiConfigStore.saveConfig();
1483    }
1484
1485    /**
1486     * Start WPS pin method configuration with pin obtained
1487     * from the access point
1488     * @param config WPS configuration
1489     * @return Wps result containing status and pin
1490     */
1491    WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
1492        WpsResult result = mWifiConfigStore.startWpsWithPinFromAccessPoint(config);
1493        /* WPS leaves all networks disabled */
1494        if (result.status == WpsResult.Status.SUCCESS) {
1495            markAllNetworksDisabled(WifiConfiguration.NetworkSelectionStatus.DISABLED_WPS_START);
1496        }
1497        return result;
1498    }
1499
1500    /**
1501     * Start WPS pin method configuration with obtained
1502     * from the device
1503     * @return WpsResult indicating status and pin
1504     */
1505    WpsResult startWpsWithPinFromDevice(WpsInfo config) {
1506        WpsResult result = mWifiConfigStore.startWpsWithPinFromDevice(config);
1507        /* WPS leaves all networks disabled */
1508        if (result.status == WpsResult.Status.SUCCESS) {
1509            markAllNetworksDisabled(WifiConfiguration.NetworkSelectionStatus.DISABLED_WPS_START);
1510        }
1511        return result;
1512    }
1513
1514    /**
1515     * Start WPS push button configuration
1516     * @param config WPS configuration
1517     * @return WpsResult indicating status and pin
1518     */
1519    WpsResult startWpsPbc(WpsInfo config) {
1520        WpsResult result = mWifiConfigStore.startWpsPbc(config);
1521        /* WPS leaves all networks disabled */
1522        if (result.status == WpsResult.Status.SUCCESS) {
1523            markAllNetworksDisabled(WifiConfiguration.NetworkSelectionStatus.DISABLED_WPS_START);
1524        }
1525        return result;
1526    }
1527
1528    /**
1529     * Fetch the static IP configuration for a given network id
1530     */
1531    StaticIpConfiguration getStaticIpConfiguration(int netId) {
1532        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1533        if (config != null) {
1534            return config.getStaticIpConfiguration();
1535        }
1536        return null;
1537    }
1538
1539    /**
1540     * Set the static IP configuration for a given network id
1541     */
1542    void setStaticIpConfiguration(int netId, StaticIpConfiguration staticIpConfiguration) {
1543        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1544        if (config != null) {
1545            config.setStaticIpConfiguration(staticIpConfiguration);
1546        }
1547    }
1548
1549    /**
1550     * set default GW MAC address
1551     */
1552    void setDefaultGwMacAddress(int netId, String macAddress) {
1553        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1554        if (config != null) {
1555            //update defaultGwMacAddress
1556            config.defaultGwMacAddress = macAddress;
1557        }
1558    }
1559
1560
1561    /**
1562     * Fetch the proxy properties for a given network id
1563     * @param netId id
1564     * @return ProxyInfo for the network id
1565     */
1566    ProxyInfo getProxyProperties(int netId) {
1567        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1568        if (config != null) {
1569            return config.getHttpProxy();
1570        }
1571        return null;
1572    }
1573
1574    /**
1575     * Return if the specified network is using static IP
1576     * @param netId id
1577     * @return {@code true} if using static ip for netId
1578     */
1579    boolean isUsingStaticIp(int netId) {
1580        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1581        if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {
1582            return true;
1583        }
1584        return false;
1585    }
1586
1587    boolean isEphemeral(int netId) {
1588        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1589        return config != null && config.ephemeral;
1590    }
1591
1592    boolean getMeteredHint(int netId) {
1593        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1594        return config != null && config.meteredHint;
1595    }
1596
1597    /**
1598     * Should be called when a single network configuration is made.
1599     * @param network The network configuration that changed.
1600     * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
1601     * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
1602     */
1603    private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
1604            int reason) {
1605        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
1606        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1607        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
1608        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
1609        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
1610        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1611    }
1612
1613    /**
1614     * Should be called when multiple network configuration changes are made.
1615     */
1616    private void sendConfiguredNetworksChangedBroadcast() {
1617        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
1618        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1619        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
1620        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1621    }
1622
1623    void loadConfiguredNetworks() {
1624
1625        final Map<String, WifiConfiguration> configs = new HashMap<>();
1626        final SparseArray<Map<String, String>> networkExtras = new SparseArray<>();
1627        mLastPriority = mWifiConfigStore.loadNetworks(configs, networkExtras);
1628
1629        readNetworkHistory(configs);
1630        readPasspointConfig(configs, networkExtras);
1631
1632        // We are only now updating mConfiguredNetworks for two reasons:
1633        // 1) The information required to compute configKeys is spread across wpa_supplicant.conf
1634        //    and networkHistory.txt. Thus, we had to load both files first.
1635        // 2) mConfiguredNetworks caches a Passpoint network's FQDN the moment the network is added.
1636        //    Thus, we had to load the FQDNs first.
1637        mConfiguredNetworks.clear();
1638        for (Map.Entry<String, WifiConfiguration> entry : configs.entrySet()) {
1639            final String configKey = entry.getKey();
1640            final WifiConfiguration config = entry.getValue();
1641            if (!configKey.equals(config.configKey())) {
1642                if (mVerboseLoggingEnabled) {
1643                    log("Ignoring network " + config.networkId + " because the configKey loaded "
1644                            + "from wpa_supplicant.conf is not valid.");
1645                }
1646                mWifiConfigStore.removeNetwork(config);
1647                continue;
1648            }
1649            mConfiguredNetworks.put(config);
1650        }
1651
1652        readIpAndProxyConfigurations();
1653
1654        sendConfiguredNetworksChangedBroadcast();
1655
1656        if (mVerboseLoggingEnabled) {
1657            localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.sizeForAllUsers()
1658                    + " networks (for all users)");
1659        }
1660
1661        if (mConfiguredNetworks.sizeForAllUsers() == 0) {
1662            // no networks? Lets log if the file contents
1663            logKernelTime();
1664            logContents(WifiConfigStore.SUPPLICANT_CONFIG_FILE);
1665            logContents(WifiConfigStore.SUPPLICANT_CONFIG_FILE_BACKUP);
1666            logContents(WifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE);
1667        }
1668    }
1669
1670    private void logContents(String file) {
1671        localLogAndLogcat("--- Begin " + file + " ---");
1672        BufferedReader reader = null;
1673        try {
1674            reader = new BufferedReader(new FileReader(file));
1675            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1676                localLogAndLogcat(line);
1677            }
1678        } catch (FileNotFoundException e) {
1679            localLog("Could not open " + file + ", " + e);
1680            Log.w(TAG, "Could not open " + file + ", " + e);
1681        } catch (IOException e) {
1682            localLog("Could not read " + file + ", " + e);
1683            Log.w(TAG, "Could not read " + file + ", " + e);
1684        } finally {
1685            try {
1686                if (reader != null) {
1687                    reader.close();
1688                }
1689            } catch (IOException e) {
1690                // Just ignore the fact that we couldn't close
1691            }
1692        }
1693        localLogAndLogcat("--- End " + file + " Contents ---");
1694    }
1695
1696    private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
1697        return mWifiConfigStore.readNetworkVariablesFromSupplicantFile(key);
1698    }
1699
1700    private String readNetworkVariableFromSupplicantFile(String configKey, String key) {
1701        long start = SystemClock.elapsedRealtimeNanos();
1702        Map<String, String> data = mWifiConfigStore.readNetworkVariablesFromSupplicantFile(key);
1703        long end = SystemClock.elapsedRealtimeNanos();
1704
1705        if (mVerboseLoggingEnabled) {
1706            localLog("readNetworkVariableFromSupplicantFile configKey=[" + configKey + "] key="
1707                    + key + " duration=" + (long) (end - start));
1708        }
1709        return data.get(configKey);
1710    }
1711
1712    boolean needsUnlockedKeyStore() {
1713
1714        // Any network using certificates to authenticate access requires
1715        // unlocked key store; unless the certificates can be stored with
1716        // hardware encryption
1717
1718        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
1719
1720            if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
1721                    && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1722
1723                if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
1724                    return true;
1725                }
1726            }
1727        }
1728
1729        return false;
1730    }
1731
1732    void readPasspointConfig(Map<String, WifiConfiguration> configs,
1733            SparseArray<Map<String, String>> networkExtras) {
1734        List<HomeSP> homeSPs;
1735        try {
1736            homeSPs = mMOManager.loadAllSPs();
1737        } catch (IOException e) {
1738            loge("Could not read " + PPS_FILE + " : " + e);
1739            return;
1740        }
1741
1742        int matchedConfigs = 0;
1743        for (HomeSP homeSp : homeSPs) {
1744            String fqdn = homeSp.getFQDN();
1745            Log.d(TAG, "Looking for " + fqdn);
1746            for (WifiConfiguration config : configs.values()) {
1747                Log.d(TAG, "Testing " + config.SSID);
1748
1749                if (config.enterpriseConfig == null) {
1750                    continue;
1751                }
1752                final String configFqdn =
1753                        networkExtras.get(config.networkId).get(WifiConfigStore.ID_STRING_KEY_FQDN);
1754                if (configFqdn != null && configFqdn.equals(fqdn)) {
1755                    Log.d(TAG, "Matched " + configFqdn + " with " + config.networkId);
1756                    ++matchedConfigs;
1757                    config.FQDN = fqdn;
1758                    config.providerFriendlyName = homeSp.getFriendlyName();
1759
1760                    HashSet<Long> roamingConsortiumIds = homeSp.getRoamingConsortiums();
1761                    config.roamingConsortiumIds = new long[roamingConsortiumIds.size()];
1762                    int i = 0;
1763                    for (long id : roamingConsortiumIds) {
1764                        config.roamingConsortiumIds[i] = id;
1765                        i++;
1766                    }
1767                    IMSIParameter imsiParameter = homeSp.getCredential().getImsi();
1768                    config.enterpriseConfig.setPlmn(
1769                            imsiParameter != null ? imsiParameter.toString() : null);
1770                    config.enterpriseConfig.setRealm(homeSp.getCredential().getRealm());
1771                }
1772            }
1773        }
1774
1775        Log.d(TAG, "loaded " + matchedConfigs + " passpoint configs");
1776    }
1777
1778    public void writePasspointConfigs(final String fqdn, final HomeSP homeSP) {
1779        mWriter.write(PPS_FILE, new DelayedDiskWrite.Writer() {
1780            @Override
1781            public void onWriteCalled(DataOutputStream out) throws IOException {
1782                try {
1783                    if (homeSP != null) {
1784                        mMOManager.addSP(homeSP);
1785                    } else {
1786                        mMOManager.removeSP(fqdn);
1787                    }
1788                } catch (IOException e) {
1789                    loge("Could not write " + PPS_FILE + " : " + e);
1790                }
1791            }
1792        }, false);
1793    }
1794
1795    /**
1796     *  Write network history, WifiConfigurations and mScanDetailCaches to file.
1797     */
1798    private void readNetworkHistory(Map<String, WifiConfiguration> configs) {
1799        mWifiNetworkHistory.readNetworkHistory(configs,
1800                mScanDetailCaches,
1801                mDeletedEphemeralSSIDs);
1802    }
1803
1804    /**
1805     *  Read Network history from file, merge it into mConfiguredNetowrks and mScanDetailCaches
1806     */
1807    public void writeKnownNetworkHistory() {
1808        final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
1809        for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
1810            networks.add(new WifiConfiguration(config));
1811        }
1812        mWifiNetworkHistory.writeKnownNetworkHistory(networks,
1813                mScanDetailCaches,
1814                mDeletedEphemeralSSIDs);
1815    }
1816
1817    public void setAndEnableLastSelectedConfiguration(int netId) {
1818        if (mVerboseLoggingEnabled) {
1819            logd("setLastSelectedConfiguration " + Integer.toString(netId));
1820        }
1821        if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
1822            mLastSelectedConfiguration = null;
1823            mLastSelectedTimeStamp = -1;
1824        } else {
1825            WifiConfiguration selected = getWifiConfiguration(netId);
1826            if (selected == null) {
1827                mLastSelectedConfiguration = null;
1828                mLastSelectedTimeStamp = -1;
1829            } else {
1830                mLastSelectedConfiguration = selected.configKey();
1831                mLastSelectedTimeStamp = mClock.elapsedRealtime();
1832                updateNetworkSelectionStatus(netId,
1833                        WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1834                if (mVerboseLoggingEnabled) {
1835                    logd("setLastSelectedConfiguration now: " + mLastSelectedConfiguration);
1836                }
1837            }
1838        }
1839    }
1840
1841    public void setLatestUserSelectedConfiguration(WifiConfiguration network) {
1842        if (network != null) {
1843            mLastSelectedConfiguration = network.configKey();
1844            mLastSelectedTimeStamp = mClock.elapsedRealtime();
1845        }
1846    }
1847
1848    public String getLastSelectedConfiguration() {
1849        return mLastSelectedConfiguration;
1850    }
1851
1852    public long getLastSelectedTimeStamp() {
1853        return mLastSelectedTimeStamp;
1854    }
1855
1856    public boolean isLastSelectedConfiguration(WifiConfiguration config) {
1857        return (mLastSelectedConfiguration != null
1858                && config != null
1859                && mLastSelectedConfiguration.equals(config.configKey()));
1860    }
1861
1862    private void writeIpAndProxyConfigurations() {
1863        final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
1864        for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
1865            if (!config.ephemeral) {
1866                networks.put(configKey(config), config.getIpConfiguration());
1867            }
1868        }
1869
1870        mIpconfigStore.writeIpAndProxyConfigurations(IP_CONFIG_FILE, networks);
1871    }
1872
1873    private void readIpAndProxyConfigurations() {
1874        SparseArray<IpConfiguration> networks =
1875                mIpconfigStore.readIpAndProxyConfigurations(IP_CONFIG_FILE);
1876
1877        if (networks == null || networks.size() == 0) {
1878            // IpConfigStore.readIpAndProxyConfigurations has already logged an error.
1879            return;
1880        }
1881
1882        for (int i = 0; i < networks.size(); i++) {
1883            int id = networks.keyAt(i);
1884            WifiConfiguration config = mConfiguredNetworks.getByConfigKeyIDForAllUsers(id);
1885            // This is the only place the map is looked up through a (dangerous) hash-value!
1886
1887            if (config == null || config.ephemeral) {
1888                logd("configuration found for missing network, nid=" + id
1889                        + ", ignored, networks.size=" + Integer.toString(networks.size()));
1890            } else {
1891                config.setIpConfiguration(networks.valueAt(i));
1892            }
1893        }
1894    }
1895
1896    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config, int uid) {
1897        /*
1898         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1899         * network configuration. Otherwise, the networkId should
1900         * refer to an existing configuration.
1901         */
1902
1903        if (mVerboseLoggingEnabled) {
1904            localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
1905        }
1906        if (config.isPasspoint() && !mMOManager.isEnabled()) {
1907            Log.e(TAG, "Passpoint is not enabled");
1908            return new NetworkUpdateResult(INVALID_NETWORK_ID);
1909        }
1910
1911        boolean newNetwork = false;
1912        boolean existingMO = false;
1913        WifiConfiguration currentConfig;
1914        // networkId of INVALID_NETWORK_ID means we want to create a new network
1915        if (config.networkId == INVALID_NETWORK_ID) {
1916            // Try to fetch the existing config using configKey
1917            currentConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
1918            if (currentConfig != null) {
1919                config.networkId = currentConfig.networkId;
1920            } else {
1921                if (mMOManager.getHomeSP(config.FQDN) != null) {
1922                    logd("addOrUpdateNetworkNative passpoint " + config.FQDN
1923                            + " was found, but no network Id");
1924                    existingMO = true;
1925                }
1926                newNetwork = true;
1927            }
1928        } else {
1929            // Fetch the existing config using networkID
1930            currentConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
1931        }
1932
1933        // originalConfig is used to check for credential and config changes that would cause
1934        // HasEverConnected to be set to false.
1935        WifiConfiguration originalConfig = new WifiConfiguration(currentConfig);
1936
1937        if (!mWifiConfigStore.addOrUpdateNetwork(config, currentConfig)) {
1938            return new NetworkUpdateResult(INVALID_NETWORK_ID);
1939        }
1940        int netId = config.networkId;
1941        String savedConfigKey = config.configKey();
1942
1943        /* An update of the network variables requires reading them
1944         * back from the supplicant to update mConfiguredNetworks.
1945         * This is because some of the variables (SSID, wep keys &
1946         * passphrases) reflect different values when read back than
1947         * when written. For example, wep key is stored as * irrespective
1948         * of the value sent to the supplicant.
1949         */
1950        if (currentConfig == null) {
1951            currentConfig = new WifiConfiguration();
1952            currentConfig.setIpAssignment(IpAssignment.DHCP);
1953            currentConfig.setProxySettings(ProxySettings.NONE);
1954            currentConfig.networkId = netId;
1955            if (config != null) {
1956                // Carry over the creation parameters
1957                currentConfig.selfAdded = config.selfAdded;
1958                currentConfig.didSelfAdd = config.didSelfAdd;
1959                currentConfig.ephemeral = config.ephemeral;
1960                currentConfig.meteredHint = config.meteredHint;
1961                currentConfig.useExternalScores = config.useExternalScores;
1962                currentConfig.lastConnectUid = config.lastConnectUid;
1963                currentConfig.lastUpdateUid = config.lastUpdateUid;
1964                currentConfig.creatorUid = config.creatorUid;
1965                currentConfig.creatorName = config.creatorName;
1966                currentConfig.lastUpdateName = config.lastUpdateName;
1967                currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
1968                currentConfig.FQDN = config.FQDN;
1969                currentConfig.providerFriendlyName = config.providerFriendlyName;
1970                currentConfig.roamingConsortiumIds = config.roamingConsortiumIds;
1971                currentConfig.validatedInternetAccess = config.validatedInternetAccess;
1972                currentConfig.numNoInternetAccessReports = config.numNoInternetAccessReports;
1973                currentConfig.updateTime = config.updateTime;
1974                currentConfig.creationTime = config.creationTime;
1975                currentConfig.shared = config.shared;
1976            }
1977            if (DBG) {
1978                log("created new config netId=" + Integer.toString(netId)
1979                        + " uid=" + Integer.toString(currentConfig.creatorUid)
1980                        + " name=" + currentConfig.creatorName);
1981            }
1982        }
1983
1984        /* save HomeSP object for passpoint networks */
1985        HomeSP homeSP = null;
1986
1987        if (!existingMO && config.isPasspoint()) {
1988            try {
1989                if (config.updateIdentifier == null) {   // Only create an MO for r1 networks
1990                    Credential credential =
1991                            new Credential(config.enterpriseConfig, mKeyStore, !newNetwork);
1992                    HashSet<Long> roamingConsortiumIds = new HashSet<Long>();
1993                    for (Long roamingConsortiumId : config.roamingConsortiumIds) {
1994                        roamingConsortiumIds.add(roamingConsortiumId);
1995                    }
1996
1997                    homeSP = new HomeSP(Collections.<String, Long>emptyMap(), config.FQDN,
1998                            roamingConsortiumIds, Collections.<String>emptySet(),
1999                            Collections.<Long>emptySet(), Collections.<Long>emptyList(),
2000                            config.providerFriendlyName, null, credential);
2001
2002                    log("created a homeSP object for " + config.networkId + ":" + config.SSID);
2003                }
2004
2005                /* fix enterprise config properties for passpoint */
2006                currentConfig.enterpriseConfig.setRealm(config.enterpriseConfig.getRealm());
2007                currentConfig.enterpriseConfig.setPlmn(config.enterpriseConfig.getPlmn());
2008            } catch (IOException ioe) {
2009                Log.e(TAG, "Failed to create Passpoint config: " + ioe);
2010                return new NetworkUpdateResult(INVALID_NETWORK_ID);
2011            }
2012        }
2013
2014        if (uid != WifiConfiguration.UNKNOWN_UID) {
2015            if (newNetwork) {
2016                currentConfig.creatorUid = uid;
2017            } else {
2018                currentConfig.lastUpdateUid = uid;
2019            }
2020        }
2021
2022        // For debug, record the time the configuration was modified
2023        StringBuilder sb = new StringBuilder();
2024        sb.append("time=");
2025        Calendar c = Calendar.getInstance();
2026        c.setTimeInMillis(mClock.currentTimeMillis());
2027        sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
2028
2029        if (newNetwork) {
2030            // New networks start out disabled according to public API.
2031            updateNetworkSelectionStatus(currentConfig,
2032                    WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
2033            currentConfig.creationTime = sb.toString();
2034        } else {
2035            currentConfig.updateTime = sb.toString();
2036        }
2037
2038        if (currentConfig.configKey().equals(getLastSelectedConfiguration())
2039                && currentConfig.ephemeral) {
2040            // Make the config non-ephemeral since the user just explicitly clicked it.
2041            currentConfig.ephemeral = false;
2042            if (DBG) {
2043                log("remove ephemeral status netId=" + Integer.toString(netId)
2044                        + " " + currentConfig.configKey());
2045            }
2046        }
2047
2048        if (mVerboseLoggingEnabled) {
2049            log("will read network variables netId=" + Integer.toString(netId));
2050        }
2051
2052        readNetworkVariables(currentConfig);
2053        // When we read back the config from wpa_supplicant, some of the default values are set
2054        // which could change the configKey.
2055        if (!savedConfigKey.equals(currentConfig.configKey())) {
2056            if (!mWifiConfigStore.saveNetworkMetadata(currentConfig)) {
2057                loge("Failed to set network metadata. Removing config " + config.networkId);
2058                mWifiConfigStore.removeNetwork(config);
2059                return new NetworkUpdateResult(INVALID_NETWORK_ID);
2060            }
2061        }
2062
2063        boolean passwordChanged = false;
2064        // check passed in config to see if it has more than a password set.
2065        if (!newNetwork && config.preSharedKey != null && !config.preSharedKey.equals("*")) {
2066            passwordChanged = true;
2067        }
2068
2069        if (newNetwork || passwordChanged || wasCredentialChange(originalConfig, currentConfig)) {
2070            currentConfig.getNetworkSelectionStatus().setHasEverConnected(false);
2071        }
2072
2073        // Persist configuration paramaters that are not saved by supplicant.
2074        if (config.lastUpdateName != null) {
2075            currentConfig.lastUpdateName = config.lastUpdateName;
2076        }
2077        if (config.lastUpdateUid != WifiConfiguration.UNKNOWN_UID) {
2078            currentConfig.lastUpdateUid = config.lastUpdateUid;
2079        }
2080
2081        mConfiguredNetworks.put(currentConfig);
2082
2083        NetworkUpdateResult result =
2084                writeIpAndProxyConfigurationsOnChange(currentConfig, config, newNetwork);
2085        result.setIsNewNetwork(newNetwork);
2086        result.setNetworkId(netId);
2087
2088        if (homeSP != null) {
2089            writePasspointConfigs(null, homeSP);
2090        }
2091
2092        saveConfig();
2093        writeKnownNetworkHistory();
2094
2095        return result;
2096    }
2097
2098    private boolean wasBitSetUpdated(BitSet originalBitSet, BitSet currentBitSet) {
2099        if (originalBitSet != null && currentBitSet != null) {
2100            // both configs have values set, check if they are different
2101            if (!originalBitSet.equals(currentBitSet)) {
2102                // the BitSets are different
2103                return true;
2104            }
2105        } else if (originalBitSet != null || currentBitSet != null) {
2106            return true;
2107        }
2108        return false;
2109    }
2110
2111    private boolean wasCredentialChange(WifiConfiguration originalConfig,
2112            WifiConfiguration currentConfig) {
2113        // Check if any core WifiConfiguration parameters changed that would impact new connections
2114        if (originalConfig == null) {
2115            return true;
2116        }
2117
2118        if (wasBitSetUpdated(originalConfig.allowedKeyManagement,
2119                currentConfig.allowedKeyManagement)) {
2120            return true;
2121        }
2122
2123        if (wasBitSetUpdated(originalConfig.allowedProtocols, currentConfig.allowedProtocols)) {
2124            return true;
2125        }
2126
2127        if (wasBitSetUpdated(originalConfig.allowedAuthAlgorithms,
2128                currentConfig.allowedAuthAlgorithms)) {
2129            return true;
2130        }
2131
2132        if (wasBitSetUpdated(originalConfig.allowedPairwiseCiphers,
2133                currentConfig.allowedPairwiseCiphers)) {
2134            return true;
2135        }
2136
2137        if (wasBitSetUpdated(originalConfig.allowedGroupCiphers,
2138                currentConfig.allowedGroupCiphers)) {
2139            return true;
2140        }
2141
2142        if (originalConfig.wepKeys != null && currentConfig.wepKeys != null) {
2143            if (originalConfig.wepKeys.length == currentConfig.wepKeys.length) {
2144                for (int i = 0; i < originalConfig.wepKeys.length; i++) {
2145                    if (!Objects.equals(originalConfig.wepKeys[i], currentConfig.wepKeys[i])) {
2146                        return true;
2147                    }
2148                }
2149            } else {
2150                return true;
2151            }
2152        }
2153
2154        if (originalConfig.hiddenSSID != currentConfig.hiddenSSID) {
2155            return true;
2156        }
2157
2158        if (originalConfig.requirePMF != currentConfig.requirePMF) {
2159            return true;
2160        }
2161
2162        if (wasEnterpriseConfigChange(originalConfig.enterpriseConfig,
2163                currentConfig.enterpriseConfig)) {
2164            return true;
2165        }
2166        return false;
2167    }
2168
2169
2170    protected boolean wasEnterpriseConfigChange(WifiEnterpriseConfig originalEnterpriseConfig,
2171            WifiEnterpriseConfig currentEnterpriseConfig) {
2172        if (originalEnterpriseConfig != null && currentEnterpriseConfig != null) {
2173            if (originalEnterpriseConfig.getEapMethod() != currentEnterpriseConfig.getEapMethod()) {
2174                return true;
2175            }
2176
2177            if (originalEnterpriseConfig.getPhase2Method()
2178                    != currentEnterpriseConfig.getPhase2Method()) {
2179                return true;
2180            }
2181
2182            X509Certificate[] originalCaCerts = originalEnterpriseConfig.getCaCertificates();
2183            X509Certificate[] currentCaCerts = currentEnterpriseConfig.getCaCertificates();
2184
2185            if (originalCaCerts != null && currentCaCerts != null) {
2186                if (originalCaCerts.length == currentCaCerts.length) {
2187                    for (int i = 0; i < originalCaCerts.length; i++) {
2188                        if (!originalCaCerts[i].equals(currentCaCerts[i])) {
2189                            return true;
2190                        }
2191                    }
2192                } else {
2193                    // number of aliases is different, so the configs are different
2194                    return true;
2195                }
2196            } else {
2197                // one of the enterprise configs may have aliases
2198                if (originalCaCerts != null || currentCaCerts != null) {
2199                    return true;
2200                }
2201            }
2202        } else {
2203            // One of the configs may have an enterpriseConfig
2204            if (originalEnterpriseConfig != null || currentEnterpriseConfig != null) {
2205                return true;
2206            }
2207        }
2208        return false;
2209    }
2210
2211    public WifiConfiguration getWifiConfigForHomeSP(HomeSP homeSP) {
2212        WifiConfiguration config = mConfiguredNetworks.getByFQDNForCurrentUser(homeSP.getFQDN());
2213        if (config == null) {
2214            Log.e(TAG, "Could not find network for homeSP " + homeSP.getFQDN());
2215        }
2216        return config;
2217    }
2218
2219    public HomeSP getHomeSPForConfig(WifiConfiguration config) {
2220        WifiConfiguration storedConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
2221        return storedConfig != null && storedConfig.isPasspoint()
2222                ? mMOManager.getHomeSP(storedConfig.FQDN)
2223                : null;
2224    }
2225
2226    public ScanDetailCache getScanDetailCache(WifiConfiguration config) {
2227        if (config == null) return null;
2228        ScanDetailCache cache = mScanDetailCaches.get(config.networkId);
2229        if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
2230            cache = new ScanDetailCache(config);
2231            mScanDetailCaches.put(config.networkId, cache);
2232        }
2233        return cache;
2234    }
2235
2236    /**
2237     * This function run thru the Saved WifiConfigurations and check if some should be linked.
2238     * @param config
2239     */
2240    public void linkConfiguration(WifiConfiguration config) {
2241        if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
2242                mUserManager.getProfiles(mCurrentUserId))) {
2243            logd("linkConfiguration: Attempting to link config " + config.configKey()
2244                    + " that is not visible to the current user.");
2245            return;
2246        }
2247
2248        if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 6) {
2249            // Ignore configurations with large number of BSSIDs
2250            return;
2251        }
2252        if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
2253            // Only link WPA_PSK config
2254            return;
2255        }
2256        for (WifiConfiguration link : mConfiguredNetworks.valuesForCurrentUser()) {
2257            boolean doLink = false;
2258
2259            if (link.configKey().equals(config.configKey())) {
2260                continue;
2261            }
2262
2263            if (link.ephemeral) {
2264                continue;
2265            }
2266
2267            // Autojoin will be allowed to dynamically jump from a linked configuration
2268            // to another, hence only link configurations that have equivalent level of security
2269            if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) {
2270                continue;
2271            }
2272
2273            ScanDetailCache linkedScanDetailCache = getScanDetailCache(link);
2274            if (linkedScanDetailCache != null && linkedScanDetailCache.size() > 6) {
2275                // Ignore configurations with large number of BSSIDs
2276                continue;
2277            }
2278
2279            if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) {
2280                // If both default GW are known, link only if they are equal
2281                if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) {
2282                    if (mVerboseLoggingEnabled) {
2283                        logd("linkConfiguration link due to same gw " + link.SSID
2284                                + " and " + config.SSID + " GW " + config.defaultGwMacAddress);
2285                    }
2286                    doLink = true;
2287                }
2288            } else {
2289                // We do not know BOTH default gateways hence we will try to link
2290                // hoping that WifiConfigurations are indeed behind the same gateway.
2291                // once both WifiConfiguration have been tried and thus once both efault gateways
2292                // are known we will revisit the choice of linking them
2293                if ((getScanDetailCache(config) != null)
2294                        && (getScanDetailCache(config).size() <= 6)) {
2295
2296                    for (String abssid : getScanDetailCache(config).keySet()) {
2297                        for (String bbssid : linkedScanDetailCache.keySet()) {
2298                            if (mVerboseLoggingEnabled) {
2299                                logd("linkConfiguration try to link due to DBDC BSSID match "
2300                                        + link.SSID + " and " + config.SSID + " bssida " + abssid
2301                                        + " bssidb " + bbssid);
2302                            }
2303                            if (abssid.regionMatches(true, 0, bbssid, 0, 16)) {
2304                                // If first 16 ascii characters of BSSID matches,
2305                                // we assume this is a DBDC
2306                                doLink = true;
2307                            }
2308                        }
2309                    }
2310                }
2311            }
2312
2313            if (doLink && mOnlyLinkSameCredentialConfigurations) {
2314                String apsk =
2315                        readNetworkVariableFromSupplicantFile(link.configKey(), "psk");
2316                String bpsk =
2317                        readNetworkVariableFromSupplicantFile(config.configKey(), "psk");
2318                if (apsk == null || bpsk == null
2319                        || TextUtils.isEmpty(apsk) || TextUtils.isEmpty(apsk)
2320                        || apsk.equals("*") || apsk.equals(DELETED_CONFIG_PSK)
2321                        || !apsk.equals(bpsk)) {
2322                    doLink = false;
2323                }
2324            }
2325
2326            if (doLink) {
2327                if (mVerboseLoggingEnabled) {
2328                    logd("linkConfiguration: will link " + link.configKey()
2329                            + " and " + config.configKey());
2330                }
2331                if (link.linkedConfigurations == null) {
2332                    link.linkedConfigurations = new HashMap<String, Integer>();
2333                }
2334                if (config.linkedConfigurations == null) {
2335                    config.linkedConfigurations = new HashMap<String, Integer>();
2336                }
2337                if (link.linkedConfigurations.get(config.configKey()) == null) {
2338                    link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
2339                }
2340                if (config.linkedConfigurations.get(link.configKey()) == null) {
2341                    config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
2342                }
2343            } else {
2344                if (link.linkedConfigurations != null
2345                        && (link.linkedConfigurations.get(config.configKey()) != null)) {
2346                    if (mVerboseLoggingEnabled) {
2347                        logd("linkConfiguration: un-link " + config.configKey()
2348                                + " from " + link.configKey());
2349                    }
2350                    link.linkedConfigurations.remove(config.configKey());
2351                }
2352                if (config.linkedConfigurations != null
2353                        && (config.linkedConfigurations.get(link.configKey()) != null)) {
2354                    if (mVerboseLoggingEnabled) {
2355                        logd("linkConfiguration: un-link " + link.configKey()
2356                                + " from " + config.configKey());
2357                    }
2358                    config.linkedConfigurations.remove(link.configKey());
2359                }
2360            }
2361        }
2362    }
2363
2364    public HashSet<Integer> makeChannelList(WifiConfiguration config, int age) {
2365        if (config == null) {
2366            return null;
2367        }
2368        long now_ms = mClock.currentTimeMillis();
2369
2370        HashSet<Integer> channels = new HashSet<Integer>();
2371
2372        //get channels for this configuration, if there are at least 2 BSSIDs
2373        if (getScanDetailCache(config) == null && config.linkedConfigurations == null) {
2374            return null;
2375        }
2376
2377        if (mVerboseLoggingEnabled) {
2378            StringBuilder dbg = new StringBuilder();
2379            dbg.append("makeChannelList age=" + Integer.toString(age)
2380                    + " for " + config.configKey()
2381                    + " max=" + mMaxNumActiveChannelsForPartialScans);
2382            if (getScanDetailCache(config) != null) {
2383                dbg.append(" bssids=" + getScanDetailCache(config).size());
2384            }
2385            if (config.linkedConfigurations != null) {
2386                dbg.append(" linked=" + config.linkedConfigurations.size());
2387            }
2388            logd(dbg.toString());
2389        }
2390
2391        int numChannels = 0;
2392        if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 0) {
2393            for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
2394                ScanResult result = scanDetail.getScanResult();
2395                //TODO : cout active and passive channels separately
2396                if (numChannels > mMaxNumActiveChannelsForPartialScans.get()) {
2397                    break;
2398                }
2399                if (mVerboseLoggingEnabled) {
2400                    boolean test = (now_ms - result.seen) < age;
2401                    logd("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
2402                            + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
2403                }
2404                if (((now_ms - result.seen) < age)) {
2405                    channels.add(result.frequency);
2406                    numChannels++;
2407                }
2408            }
2409        }
2410
2411        //get channels for linked configurations
2412        if (config.linkedConfigurations != null) {
2413            for (String key : config.linkedConfigurations.keySet()) {
2414                WifiConfiguration linked = getWifiConfiguration(key);
2415                if (linked == null) {
2416                    continue;
2417                }
2418                if (getScanDetailCache(linked) == null) {
2419                    continue;
2420                }
2421                for (ScanDetail scanDetail : getScanDetailCache(linked).values()) {
2422                    ScanResult result = scanDetail.getScanResult();
2423                    if (mVerboseLoggingEnabled) {
2424                        logd("has link: " + result.BSSID
2425                                + " freq=" + Integer.toString(result.frequency)
2426                                + " age=" + Long.toString(now_ms - result.seen));
2427                    }
2428                    if (numChannels > mMaxNumActiveChannelsForPartialScans.get()) {
2429                        break;
2430                    }
2431                    if (((now_ms - result.seen) < age)) {
2432                        channels.add(result.frequency);
2433                        numChannels++;
2434                    }
2435                }
2436            }
2437        }
2438        return channels;
2439    }
2440
2441    private Map<HomeSP, PasspointMatch> matchPasspointNetworks(ScanDetail scanDetail) {
2442        if (!mMOManager.isConfigured()) {
2443            if (mEnableOsuQueries) {
2444                NetworkDetail networkDetail = scanDetail.getNetworkDetail();
2445                List<Constants.ANQPElementType> querySet =
2446                        ANQPFactory.buildQueryList(networkDetail, false, true);
2447
2448                if (networkDetail.queriable(querySet)) {
2449                    querySet = mAnqpCache.initiate(networkDetail, querySet);
2450                    if (querySet != null) {
2451                        mSupplicantBridge.startANQP(scanDetail, querySet);
2452                    }
2453                    updateAnqpCache(scanDetail, networkDetail.getANQPElements());
2454                }
2455            }
2456            return null;
2457        }
2458        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
2459        if (!networkDetail.hasInterworking()) {
2460            return null;
2461        }
2462        updateAnqpCache(scanDetail, networkDetail.getANQPElements());
2463
2464        Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, true);
2465        Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID()
2466                + " pass 1 matches: " + toMatchString(matches));
2467        return matches;
2468    }
2469
2470    private Map<HomeSP, PasspointMatch> matchNetwork(ScanDetail scanDetail, boolean query) {
2471        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
2472
2473        ANQPData anqpData = mAnqpCache.getEntry(networkDetail);
2474
2475        Map<Constants.ANQPElementType, ANQPElement> anqpElements =
2476                anqpData != null ? anqpData.getANQPElements() : null;
2477
2478        boolean queried = !query;
2479        Collection<HomeSP> homeSPs = mMOManager.getLoadedSPs().values();
2480        Map<HomeSP, PasspointMatch> matches = new HashMap<>(homeSPs.size());
2481        Log.d(Utils.hs2LogTag(getClass()), "match nwk " + scanDetail.toKeyString()
2482                + ", anqp " + (anqpData != null ? "present" : "missing")
2483                + ", query " + query + ", home sps: " + homeSPs.size());
2484
2485        for (HomeSP homeSP : homeSPs) {
2486            PasspointMatch match = homeSP.match(networkDetail, anqpElements, mSIMAccessor);
2487
2488            Log.d(Utils.hs2LogTag(getClass()), " -- "
2489                    + homeSP.getFQDN() + ": match " + match + ", queried " + queried);
2490
2491            if ((match == PasspointMatch.Incomplete || mEnableOsuQueries) && !queried) {
2492                boolean matchSet = match == PasspointMatch.Incomplete;
2493                boolean osu = mEnableOsuQueries;
2494                List<Constants.ANQPElementType> querySet =
2495                        ANQPFactory.buildQueryList(networkDetail, matchSet, osu);
2496                if (networkDetail.queriable(querySet)) {
2497                    querySet = mAnqpCache.initiate(networkDetail, querySet);
2498                    if (querySet != null) {
2499                        mSupplicantBridge.startANQP(scanDetail, querySet);
2500                    }
2501                }
2502                queried = true;
2503            }
2504            matches.put(homeSP, match);
2505        }
2506        return matches;
2507    }
2508
2509    public Map<Constants.ANQPElementType, ANQPElement> getANQPData(NetworkDetail network) {
2510        ANQPData data = mAnqpCache.getEntry(network);
2511        return data != null ? data.getANQPElements() : null;
2512    }
2513
2514    public SIMAccessor getSIMAccessor() {
2515        return mSIMAccessor;
2516    }
2517
2518    public void notifyANQPDone(Long bssid, boolean success) {
2519        mSupplicantBridge.notifyANQPDone(bssid, success);
2520    }
2521
2522    public void notifyIconReceived(IconEvent iconEvent) {
2523        Intent intent = new Intent(WifiManager.PASSPOINT_ICON_RECEIVED_ACTION);
2524        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2525        intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_BSSID, iconEvent.getBSSID());
2526        intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_FILE, iconEvent.getFileName());
2527        try {
2528            intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_DATA,
2529                    mSupplicantBridge.retrieveIcon(iconEvent));
2530        } catch (IOException ioe) {
2531            /* Simply omit the icon data as a failure indication */
2532        }
2533        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2534
2535    }
2536
2537    private void updateAnqpCache(ScanDetail scanDetail,
2538                                 Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
2539        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
2540
2541        if (anqpElements == null) {
2542            // Try to pull cached data if query failed.
2543            ANQPData data = mAnqpCache.getEntry(networkDetail);
2544            if (data != null) {
2545                scanDetail.propagateANQPInfo(data.getANQPElements());
2546            }
2547            return;
2548        }
2549
2550        mAnqpCache.update(networkDetail, anqpElements);
2551    }
2552
2553    private static String toMatchString(Map<HomeSP, PasspointMatch> matches) {
2554        StringBuilder sb = new StringBuilder();
2555        for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
2556            sb.append(' ').append(entry.getKey().getFQDN()).append("->").append(entry.getValue());
2557        }
2558        return sb.toString();
2559    }
2560
2561    private void cacheScanResultForPasspointConfigs(ScanDetail scanDetail,
2562            Map<HomeSP, PasspointMatch> matches,
2563            List<WifiConfiguration> associatedWifiConfigurations) {
2564
2565        for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
2566            PasspointMatch match = entry.getValue();
2567            if (match == PasspointMatch.HomeProvider || match == PasspointMatch.RoamingProvider) {
2568                WifiConfiguration config = getWifiConfigForHomeSP(entry.getKey());
2569                if (config != null) {
2570                    cacheScanResultForConfig(config, scanDetail, entry.getValue());
2571                    if (associatedWifiConfigurations != null) {
2572                        associatedWifiConfigurations.add(config);
2573                    }
2574                } else {
2575                    Log.w(Utils.hs2LogTag(getClass()), "Failed to find config for '"
2576                            + entry.getKey().getFQDN() + "'");
2577                    /* perhaps the configuration was deleted?? */
2578                }
2579            }
2580        }
2581    }
2582
2583    private void cacheScanResultForConfig(
2584            WifiConfiguration config, ScanDetail scanDetail, PasspointMatch passpointMatch) {
2585
2586        ScanResult scanResult = scanDetail.getScanResult();
2587
2588        ScanDetailCache scanDetailCache = getScanDetailCache(config);
2589        if (scanDetailCache == null) {
2590            Log.w(TAG, "Could not allocate scan cache for " + config.SSID);
2591            return;
2592        }
2593
2594        // Adding a new BSSID
2595        ScanResult result = scanDetailCache.get(scanResult.BSSID);
2596        if (result != null) {
2597            // transfer the black list status
2598            scanResult.blackListTimestamp = result.blackListTimestamp;
2599            scanResult.numIpConfigFailures = result.numIpConfigFailures;
2600            scanResult.numConnection = result.numConnection;
2601            scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate;
2602        }
2603
2604        if (config.ephemeral) {
2605            // For an ephemeral Wi-Fi config, the ScanResult should be considered
2606            // untrusted.
2607            scanResult.untrusted = true;
2608        }
2609
2610        if (scanDetailCache.size() > (MAX_NUM_SCAN_CACHE_ENTRIES + 64)) {
2611            long now_dbg = 0;
2612            if (mVerboseLoggingEnabled) {
2613                logd(" Will trim config " + config.configKey()
2614                        + " size " + scanDetailCache.size());
2615
2616                for (ScanDetail sd : scanDetailCache.values()) {
2617                    logd("     " + sd.getBSSIDString() + " " + sd.getSeen());
2618                }
2619                now_dbg = SystemClock.elapsedRealtimeNanos();
2620            }
2621            // Trim the scan result cache to MAX_NUM_SCAN_CACHE_ENTRIES entries max
2622            // Since this operation is expensive, make sure it is not performed
2623            // until the cache has grown significantly above the trim treshold
2624            scanDetailCache.trim(MAX_NUM_SCAN_CACHE_ENTRIES);
2625            if (mVerboseLoggingEnabled) {
2626                long diff = SystemClock.elapsedRealtimeNanos() - now_dbg;
2627                logd(" Finished trimming config, time(ns) " + diff);
2628                for (ScanDetail sd : scanDetailCache.values()) {
2629                    logd("     " + sd.getBSSIDString() + " " + sd.getSeen());
2630                }
2631            }
2632        }
2633
2634        // Add the scan result to this WifiConfiguration
2635        if (passpointMatch != null) {
2636            scanDetailCache.put(scanDetail, passpointMatch, getHomeSPForConfig(config));
2637        } else {
2638            scanDetailCache.put(scanDetail);
2639        }
2640
2641        // Since we added a scan result to this configuration, re-attempt linking
2642        linkConfiguration(config);
2643    }
2644
2645    private boolean isEncryptionWep(String encryption) {
2646        return encryption.contains("WEP");
2647    }
2648
2649    private boolean isEncryptionPsk(String encryption) {
2650        return encryption.contains("PSK");
2651    }
2652
2653    private boolean isEncryptionEap(String encryption) {
2654        return encryption.contains("EAP");
2655    }
2656
2657    public boolean isOpenNetwork(String encryption) {
2658        if (!isEncryptionWep(encryption) && !isEncryptionPsk(encryption)
2659                && !isEncryptionEap(encryption)) {
2660            return true;
2661        }
2662        return false;
2663    }
2664
2665    public boolean isOpenNetwork(ScanResult scan) {
2666        String scanResultEncrypt = scan.capabilities;
2667        return isOpenNetwork(scanResultEncrypt);
2668    }
2669
2670    public boolean isOpenNetwork(WifiConfiguration config) {
2671        String configEncrypt = config.configKey();
2672        return isOpenNetwork(configEncrypt);
2673    }
2674
2675    /**
2676     * Get saved WifiConfiguration associated with a scan detail.
2677     * @param scanDetail input a scanDetail from the scan result
2678     * @return WifiConfiguration WifiConfiguration associated with this scanDetail, null if none
2679     */
2680    public List<WifiConfiguration> getSavedNetworkFromScanDetail(ScanDetail scanDetail) {
2681        ScanResult scanResult = scanDetail.getScanResult();
2682        if (scanResult == null) {
2683            return null;
2684        }
2685        List<WifiConfiguration> savedWifiConfigurations = new ArrayList<>();
2686        String ssid = "\"" + scanResult.SSID + "\"";
2687        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
2688            if (config.SSID == null || !config.SSID.equals(ssid)) {
2689                continue;
2690            }
2691            if (DBG) {
2692                localLog("getSavedNetworkFromScanDetail(): try " + config.configKey()
2693                        + " SSID=" + config.SSID + " " + scanResult.SSID + " "
2694                        + scanResult.capabilities);
2695            }
2696            String scanResultEncrypt = scanResult.capabilities;
2697            String configEncrypt = config.configKey();
2698            if (isEncryptionWep(scanResultEncrypt) && isEncryptionWep(configEncrypt)
2699                    || (isEncryptionPsk(scanResultEncrypt) && isEncryptionPsk(configEncrypt))
2700                    || (isEncryptionEap(scanResultEncrypt) && isEncryptionEap(configEncrypt))
2701                    || (isOpenNetwork(scanResultEncrypt) && isOpenNetwork(configEncrypt))) {
2702                savedWifiConfigurations.add(config);
2703            }
2704        }
2705        return savedWifiConfigurations;
2706    }
2707
2708    /**
2709     * Create a mapping between the scandetail and the Wificonfiguration it associated with
2710     * because Passpoint, one BSSID can associated with multiple SSIDs
2711     * @param scanDetail input a scanDetail from the scan result
2712     * @param isConnectingOrConnected input a boolean to indicate if WiFi is connecting or conncted
2713     * This is used for avoiding ANQP request
2714     * @return List<WifiConfiguration> a list of WifiConfigurations associated to this scanDetail
2715     */
2716    public List<WifiConfiguration> updateSavedNetworkWithNewScanDetail(ScanDetail scanDetail,
2717            boolean isConnectingOrConnected) {
2718        ScanResult scanResult = scanDetail.getScanResult();
2719        if (scanResult == null) {
2720            return null;
2721        }
2722        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
2723        List<WifiConfiguration> associatedWifiConfigurations = new ArrayList<>();
2724        if (networkDetail.hasInterworking() && !isConnectingOrConnected) {
2725            Map<HomeSP, PasspointMatch> matches = matchPasspointNetworks(scanDetail);
2726            if (matches != null) {
2727                cacheScanResultForPasspointConfigs(scanDetail, matches,
2728                        associatedWifiConfigurations);
2729                //Do not return here. A BSSID can belong to both passpoint network and non-passpoint
2730                //Network
2731            }
2732        }
2733        List<WifiConfiguration> savedConfigurations = getSavedNetworkFromScanDetail(scanDetail);
2734        if (savedConfigurations != null) {
2735            for (WifiConfiguration config : savedConfigurations) {
2736                cacheScanResultForConfig(config, scanDetail, null);
2737                associatedWifiConfigurations.add(config);
2738            }
2739        }
2740        if (associatedWifiConfigurations.size() == 0) {
2741            return null;
2742        } else {
2743            return associatedWifiConfigurations;
2744        }
2745    }
2746
2747    /**
2748     * Handles the switch to a different foreground user:
2749     * - Removes all ephemeral networks
2750     * - Disables private network configurations belonging to the previous foreground user
2751     * - Enables private network configurations belonging to the new foreground user
2752     *
2753     * @param userId The identifier of the new foreground user, after the switch.
2754     *
2755     * TODO(b/26785736): Terminate background users if the new foreground user has one or more
2756     * private network configurations.
2757     */
2758    public void handleUserSwitch(int userId) {
2759        mCurrentUserId = userId;
2760        Set<WifiConfiguration> ephemeralConfigs = new HashSet<>();
2761        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
2762            if (config.ephemeral) {
2763                ephemeralConfigs.add(config);
2764            }
2765        }
2766        if (!ephemeralConfigs.isEmpty()) {
2767            for (WifiConfiguration config : ephemeralConfigs) {
2768                removeConfigWithoutBroadcast(config);
2769            }
2770            saveConfig();
2771            writeKnownNetworkHistory();
2772        }
2773
2774        final List<WifiConfiguration> hiddenConfigurations =
2775                mConfiguredNetworks.handleUserSwitch(mCurrentUserId);
2776        for (WifiConfiguration network : hiddenConfigurations) {
2777            updateNetworkSelectionStatus(
2778                    network, WifiConfiguration.NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH);
2779        }
2780        enableAllNetworks();
2781
2782        // TODO(b/26785746): This broadcast is unnecessary if either of the following is true:
2783        // * The user switch did not change the list of visible networks
2784        // * The user switch revealed additional networks that were temporarily disabled and got
2785        //   re-enabled now (because enableAllNetworks() sent the same broadcast already).
2786        sendConfiguredNetworksChangedBroadcast();
2787    }
2788
2789    public int getCurrentUserId() {
2790        return mCurrentUserId;
2791    }
2792
2793    public boolean isCurrentUserProfile(int userId) {
2794        if (userId == mCurrentUserId) {
2795            return true;
2796        }
2797        final UserInfo parent = mUserManager.getProfileParent(userId);
2798        return parent != null && parent.id == mCurrentUserId;
2799    }
2800
2801    /* Compare current and new configuration and write to file on change */
2802    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
2803            WifiConfiguration currentConfig,
2804            WifiConfiguration newConfig,
2805            boolean isNewNetwork) {
2806        boolean ipChanged = false;
2807        boolean proxyChanged = false;
2808
2809        switch (newConfig.getIpAssignment()) {
2810            case STATIC:
2811                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
2812                    ipChanged = true;
2813                } else {
2814                    ipChanged = !Objects.equals(
2815                            currentConfig.getStaticIpConfiguration(),
2816                            newConfig.getStaticIpConfiguration());
2817                }
2818                break;
2819            case DHCP:
2820                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
2821                    ipChanged = true;
2822                }
2823                break;
2824            case UNASSIGNED:
2825                /* Ignore */
2826                break;
2827            default:
2828                loge("Ignore invalid ip assignment during write");
2829                break;
2830        }
2831
2832        switch (newConfig.getProxySettings()) {
2833            case STATIC:
2834            case PAC:
2835                ProxyInfo newHttpProxy = newConfig.getHttpProxy();
2836                ProxyInfo currentHttpProxy = currentConfig.getHttpProxy();
2837
2838                if (newHttpProxy != null) {
2839                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
2840                } else {
2841                    proxyChanged = (currentHttpProxy != null);
2842                }
2843                break;
2844            case NONE:
2845                if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
2846                    proxyChanged = true;
2847                }
2848                break;
2849            case UNASSIGNED:
2850                /* Ignore */
2851                break;
2852            default:
2853                loge("Ignore invalid proxy configuration during write");
2854                break;
2855        }
2856
2857        if (ipChanged) {
2858            currentConfig.setIpAssignment(newConfig.getIpAssignment());
2859            currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration());
2860            log("IP config changed SSID = " + currentConfig.SSID);
2861            if (currentConfig.getStaticIpConfiguration() != null) {
2862                log(" static configuration: "
2863                        + currentConfig.getStaticIpConfiguration().toString());
2864            }
2865        }
2866
2867        if (proxyChanged) {
2868            currentConfig.setProxySettings(newConfig.getProxySettings());
2869            currentConfig.setHttpProxy(newConfig.getHttpProxy());
2870            log("proxy changed SSID = " + currentConfig.SSID);
2871            if (currentConfig.getHttpProxy() != null) {
2872                log(" proxyProperties: " + currentConfig.getHttpProxy().toString());
2873            }
2874        }
2875
2876        if (ipChanged || proxyChanged || isNewNetwork) {
2877            if (mVerboseLoggingEnabled) {
2878                logd("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> "
2879                        + newConfig.SSID + " path: " + IP_CONFIG_FILE);
2880            }
2881            writeIpAndProxyConfigurations();
2882        }
2883        return new NetworkUpdateResult(ipChanged, proxyChanged);
2884    }
2885
2886    /**
2887     * Read the variables from the supplicant daemon that are needed to
2888     * fill in the WifiConfiguration object.
2889     *
2890     * @param config the {@link WifiConfiguration} object to be filled in.
2891     */
2892    private void readNetworkVariables(WifiConfiguration config) {
2893        mWifiConfigStore.readNetworkVariables(config);
2894    }
2895
2896    /* return the allowed key management based on a scan result */
2897
2898    public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
2899
2900        WifiConfiguration config = new WifiConfiguration();
2901
2902        config.SSID = "\"" + result.SSID + "\"";
2903
2904        if (mVerboseLoggingEnabled) {
2905            logd("WifiConfiguration from scan results "
2906                    + config.SSID + " cap " + result.capabilities);
2907        }
2908
2909        if (result.capabilities.contains("PSK") || result.capabilities.contains("EAP")
2910                || result.capabilities.contains("WEP")) {
2911            if (result.capabilities.contains("PSK")) {
2912                config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
2913            }
2914
2915            if (result.capabilities.contains("EAP")) {
2916                config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
2917                config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
2918            }
2919
2920            if (result.capabilities.contains("WEP")) {
2921                config.allowedKeyManagement.set(KeyMgmt.NONE);
2922                config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
2923                config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
2924            }
2925        } else {
2926            config.allowedKeyManagement.set(KeyMgmt.NONE);
2927        }
2928
2929        return config;
2930    }
2931
2932    public WifiConfiguration wifiConfigurationFromScanResult(ScanDetail scanDetail) {
2933        ScanResult result = scanDetail.getScanResult();
2934        return wifiConfigurationFromScanResult(result);
2935    }
2936
2937    /* Returns a unique for a given configuration */
2938    private static int configKey(WifiConfiguration config) {
2939        String key = config.configKey();
2940        return key.hashCode();
2941    }
2942
2943    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2944        pw.println("Dump of WifiConfigManager");
2945        pw.println("mLastPriority " + mLastPriority);
2946        pw.println("Configured networks");
2947        for (WifiConfiguration conf : getAllConfiguredNetworks()) {
2948            pw.println(conf);
2949        }
2950        pw.println();
2951        if (mLostConfigsDbg != null && mLostConfigsDbg.size() > 0) {
2952            pw.println("LostConfigs: ");
2953            for (String s : mLostConfigsDbg) {
2954                pw.println(s);
2955            }
2956        }
2957        if (mLocalLog != null) {
2958            pw.println("WifiConfigManager - Log Begin ----");
2959            mLocalLog.dump(fd, pw, args);
2960            pw.println("WifiConfigManager - Log End ----");
2961        }
2962        if (mMOManager.isConfigured()) {
2963            pw.println("Begin dump of ANQP Cache");
2964            mAnqpCache.dump(pw);
2965            pw.println("End dump of ANQP Cache");
2966        }
2967    }
2968
2969    public String getConfigFile() {
2970        return IP_CONFIG_FILE;
2971    }
2972
2973    protected void logd(String s) {
2974        Log.d(TAG, s);
2975    }
2976
2977    protected void loge(String s) {
2978        loge(s, false);
2979    }
2980
2981    protected void loge(String s, boolean stack) {
2982        if (stack) {
2983            Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
2984                    + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
2985                    + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
2986                    + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
2987        } else {
2988            Log.e(TAG, s);
2989        }
2990    }
2991
2992    private void logKernelTime() {
2993        long kernelTimeMs = System.nanoTime() / (1000 * 1000);
2994        StringBuilder builder = new StringBuilder();
2995        builder.append("kernel time = ")
2996                .append(kernelTimeMs / 1000)
2997                .append(".")
2998                .append(kernelTimeMs % 1000)
2999                .append("\n");
3000        localLog(builder.toString());
3001    }
3002
3003    protected void log(String s) {
3004        Log.d(TAG, s);
3005    }
3006
3007    private void localLog(String s) {
3008        if (mLocalLog != null) {
3009            mLocalLog.log(s);
3010        }
3011    }
3012
3013    private void localLogAndLogcat(String s) {
3014        localLog(s);
3015        Log.d(TAG, s);
3016    }
3017
3018    private void localLogNetwork(String s, int netId) {
3019        if (mLocalLog == null) {
3020            return;
3021        }
3022
3023        WifiConfiguration config;
3024        synchronized (mConfiguredNetworks) {             // !!! Useless synchronization
3025            config = mConfiguredNetworks.getForAllUsers(netId);
3026        }
3027
3028        if (config != null) {
3029            mLocalLog.log(s + " " + config.getPrintableSsid() + " " + netId
3030                    + " status=" + config.status
3031                    + " key=" + config.configKey());
3032        } else {
3033            mLocalLog.log(s + " " + netId);
3034        }
3035    }
3036
3037    static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
3038        String client = config.getClientCertificateAlias();
3039        if (!TextUtils.isEmpty(client)) {
3040            // a valid client certificate is configured
3041
3042            // BUGBUG: keyStore.get() never returns certBytes; because it is not
3043            // taking WIFI_UID as a parameter. It always looks for certificate
3044            // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
3045            // all certificates need software keystore until we get the get() API
3046            // fixed.
3047
3048            return true;
3049        }
3050
3051        /*
3052        try {
3053
3054            if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
3055                    .USER_CERTIFICATE + client);
3056
3057            CertificateFactory factory = CertificateFactory.getInstance("X.509");
3058            if (factory == null) {
3059                Slog.e(TAG, "Error getting certificate factory");
3060                return;
3061            }
3062
3063            byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
3064            if (certBytes != null) {
3065                Certificate cert = (X509Certificate) factory.generateCertificate(
3066                        new ByteArrayInputStream(certBytes));
3067
3068                if (cert != null) {
3069                    mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
3070
3071                    if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
3072                            .USER_CERTIFICATE + client);
3073                    if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
3074                            "does not need" ) + " software key store");
3075                } else {
3076                    Slog.d(TAG, "could not generate certificate");
3077                }
3078            } else {
3079                Slog.e(TAG, "Could not load client certificate " + Credentials
3080                        .USER_CERTIFICATE + client);
3081                mNeedsSoftwareKeystore = true;
3082            }
3083
3084        } catch(CertificateException e) {
3085            Slog.e(TAG, "Could not read certificates");
3086            mCaCert = null;
3087            mClientCertificate = null;
3088        }
3089        */
3090
3091        return false;
3092    }
3093
3094    /**
3095     * Checks if the network is a sim config.
3096     * @param config Config corresponding to the network.
3097     * @return true if it is a sim config, false otherwise.
3098     */
3099    public boolean isSimConfig(WifiConfiguration config) {
3100        return mWifiConfigStore.isSimConfig(config);
3101    }
3102
3103    /**
3104     * Resets all sim networks from the network list.
3105     */
3106    public void resetSimNetworks() {
3107        mWifiConfigStore.resetSimNetworks(mConfiguredNetworks.valuesForCurrentUser());
3108    }
3109
3110    boolean isNetworkConfigured(WifiConfiguration config) {
3111        // Check if either we have a network Id or a WifiConfiguration
3112        // matching the one we are trying to add.
3113
3114        if (config.networkId != INVALID_NETWORK_ID) {
3115            return (mConfiguredNetworks.getForCurrentUser(config.networkId) != null);
3116        }
3117
3118        return (mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey()) != null);
3119    }
3120
3121    /**
3122     * Checks if uid has access to modify the configuration corresponding to networkId.
3123     *
3124     * The conditions checked are, in descending priority order:
3125     * - Disallow modification if the the configuration is not visible to the uid.
3126     * - Allow modification if the uid represents the Device Owner app.
3127     * - Allow modification if both of the following are true:
3128     *   - The uid represents the configuration's creator or an app holding OVERRIDE_CONFIG_WIFI.
3129     *   - The modification is only for administrative annotation (e.g. when connecting) or the
3130     *     configuration is not lockdown eligible (which currently means that it was not last
3131     *     updated by the DO).
3132     * - Allow modification if configuration lockdown is explicitly disabled and the uid represents
3133     *   an app holding OVERRIDE_CONFIG_WIFI.
3134     * - In all other cases, disallow modification.
3135     */
3136    boolean canModifyNetwork(int uid, int networkId, boolean onlyAnnotate) {
3137        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(networkId);
3138
3139        if (config == null) {
3140            loge("canModifyNetwork: cannot find config networkId " + networkId);
3141            return false;
3142        }
3143
3144        final DevicePolicyManagerInternal dpmi = LocalServices.getService(
3145                DevicePolicyManagerInternal.class);
3146
3147        final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
3148                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
3149
3150        if (isUidDeviceOwner) {
3151            return true;
3152        }
3153
3154        final boolean isCreator = (config.creatorUid == uid);
3155
3156        if (onlyAnnotate) {
3157            return isCreator || checkConfigOverridePermission(uid);
3158        }
3159
3160        // Check if device has DPM capability. If it has and dpmi is still null, then we
3161        // treat this case with suspicion and bail out.
3162        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
3163                && dpmi == null) {
3164            return false;
3165        }
3166
3167        // WiFi config lockdown related logic. At this point we know uid NOT to be a Device Owner.
3168
3169        final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
3170                config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
3171        if (!isConfigEligibleForLockdown) {
3172            return isCreator || checkConfigOverridePermission(uid);
3173        }
3174
3175        final ContentResolver resolver = mContext.getContentResolver();
3176        final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
3177                Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
3178        return !isLockdownFeatureEnabled && checkConfigOverridePermission(uid);
3179    }
3180
3181    /**
3182     * Checks if uid has access to modify config.
3183     */
3184    boolean canModifyNetwork(int uid, WifiConfiguration config, boolean onlyAnnotate) {
3185        if (config == null) {
3186            loge("canModifyNetowrk recieved null configuration");
3187            return false;
3188        }
3189
3190        // Resolve the correct network id.
3191        int netid;
3192        if (config.networkId != INVALID_NETWORK_ID) {
3193            netid = config.networkId;
3194        } else {
3195            WifiConfiguration test =
3196                    mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
3197            if (test == null) {
3198                return false;
3199            } else {
3200                netid = test.networkId;
3201            }
3202        }
3203
3204        return canModifyNetwork(uid, netid, onlyAnnotate);
3205    }
3206
3207    boolean checkConfigOverridePermission(int uid) {
3208        try {
3209            return (mFacade.checkUidPermission(
3210                    android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid)
3211                    == PackageManager.PERMISSION_GRANTED);
3212        } catch (RemoteException e) {
3213            return false;
3214        }
3215    }
3216
3217    /** called when CS ask WiFistateMachine to disconnect the current network
3218     * because the score is bad.
3219     */
3220    void handleBadNetworkDisconnectReport(int netId, WifiInfo info) {
3221        /* TODO verify the bad network is current */
3222        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
3223        if (config != null) {
3224            if ((info.is24GHz() && info.getRssi()
3225                    <= WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND)
3226                    || (info.is5GHz() && info.getRssi()
3227                    <= WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND)) {
3228                // We do not block due to bad RSSI since network selection should not select bad
3229                // RSSI candidate
3230            } else {
3231                // We got disabled but RSSI is good, so disable hard
3232                updateNetworkSelectionStatus(config,
3233                        WifiConfiguration.NetworkSelectionStatus.DISABLED_BAD_LINK);
3234            }
3235        }
3236        // Record last time Connectivity Service switched us away from WiFi and onto Cell
3237        mLastUnwantedNetworkDisconnectTimestamp = mClock.currentTimeMillis();
3238    }
3239
3240    int getMaxDhcpRetries() {
3241        return mFacade.getIntegerSetting(mContext,
3242                Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
3243                DEFAULT_MAX_DHCP_RETRIES);
3244    }
3245
3246    void clearBssidBlacklist() {
3247        mWifiConfigStore.clearBssidBlacklist();
3248    }
3249
3250    void blackListBssid(String bssid) {
3251        mWifiConfigStore.blackListBssid(bssid);
3252    }
3253
3254    public boolean isBssidBlacklisted(String bssid) {
3255        return mWifiConfigStore.isBssidBlacklisted(bssid);
3256    }
3257
3258    public boolean getEnableAutoJoinWhenAssociated() {
3259        return mEnableAutoJoinWhenAssociated.get();
3260    }
3261
3262    public void setEnableAutoJoinWhenAssociated(boolean enabled) {
3263        mEnableAutoJoinWhenAssociated.set(enabled);
3264    }
3265
3266    public void setActiveScanDetail(ScanDetail activeScanDetail) {
3267        synchronized (mActiveScanDetailLock) {
3268            mActiveScanDetail = activeScanDetail;
3269        }
3270    }
3271
3272    /**
3273     * Check if the provided ephemeral network was deleted by the user or not.
3274     * @param ssid ssid of the network
3275     * @return true if network was deleted, false otherwise.
3276     */
3277    public boolean wasEphemeralNetworkDeleted(String ssid) {
3278        return mDeletedEphemeralSSIDs.contains(ssid);
3279    }
3280}
3281