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