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