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