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