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