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