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