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