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