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