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