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