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