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