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