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