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