Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wifi;
     18 
     19 import android.app.AppGlobals;
     20 import android.app.admin.DeviceAdminInfo;
     21 import android.app.admin.DevicePolicyManagerInternal;
     22 import android.content.ContentResolver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.pm.ApplicationInfo;
     26 import android.content.pm.PackageManager;
     27 import android.net.IpConfiguration;
     28 import android.net.IpConfiguration.IpAssignment;
     29 import android.net.IpConfiguration.ProxySettings;
     30 import android.net.NetworkInfo.DetailedState;
     31 import android.net.ProxyInfo;
     32 import android.net.StaticIpConfiguration;
     33 import android.net.wifi.ScanResult;
     34 import android.net.wifi.WifiConfiguration;
     35 import android.net.wifi.WifiConfiguration.KeyMgmt;
     36 import android.net.wifi.WifiConfiguration.Status;
     37 import android.net.wifi.WifiEnterpriseConfig;
     38 import android.net.wifi.WifiInfo;
     39 import android.net.wifi.WifiManager;
     40 import android.net.wifi.WifiSsid;
     41 import android.net.wifi.WpsInfo;
     42 import android.net.wifi.WpsResult;
     43 import android.os.Environment;
     44 import android.os.FileObserver;
     45 import android.os.Process;
     46 import android.os.RemoteException;
     47 import android.os.SystemClock;
     48 import android.os.UserHandle;
     49 import android.provider.Settings;
     50 import android.security.Credentials;
     51 import android.security.KeyChain;
     52 import android.security.KeyStore;
     53 import android.text.TextUtils;
     54 import android.util.LocalLog;
     55 import android.util.Log;
     56 import android.util.SparseArray;
     57 
     58 import com.android.server.LocalServices;
     59 import com.android.internal.R;
     60 import com.android.server.net.DelayedDiskWrite;
     61 import com.android.server.net.IpConfigStore;
     62 import com.android.server.wifi.anqp.ANQPElement;
     63 import com.android.server.wifi.anqp.Constants;
     64 import com.android.server.wifi.hotspot2.ANQPData;
     65 import com.android.server.wifi.hotspot2.AnqpCache;
     66 import com.android.server.wifi.hotspot2.NetworkDetail;
     67 import com.android.server.wifi.hotspot2.PasspointMatch;
     68 import com.android.server.wifi.hotspot2.SupplicantBridge;
     69 import com.android.server.wifi.hotspot2.Utils;
     70 import com.android.server.wifi.hotspot2.omadm.MOManager;
     71 import com.android.server.wifi.hotspot2.pps.Credential;
     72 import com.android.server.wifi.hotspot2.pps.HomeSP;
     73 
     74 import java.io.BufferedInputStream;
     75 import java.io.BufferedReader;
     76 import java.io.DataInputStream;
     77 import java.io.DataOutputStream;
     78 import java.io.EOFException;
     79 import java.io.File;
     80 import java.io.FileDescriptor;
     81 import java.io.FileInputStream;
     82 import java.io.FileNotFoundException;
     83 import java.io.FileReader;
     84 import java.io.IOException;
     85 import java.io.PrintWriter;
     86 import java.nio.charset.StandardCharsets;
     87 import java.security.PrivateKey;
     88 import java.security.cert.Certificate;
     89 import java.security.cert.CertificateException;
     90 import java.util.ArrayList;
     91 import java.util.BitSet;
     92 import java.util.Calendar;
     93 import java.util.Collection;
     94 import java.util.Collections;
     95 import java.util.Comparator;
     96 import java.util.HashMap;
     97 import java.util.HashSet;
     98 import java.util.List;
     99 import java.util.Map;
    100 import java.util.Objects;
    101 import java.util.Set;
    102 import java.util.concurrent.atomic.AtomicBoolean;
    103 import java.util.concurrent.atomic.AtomicInteger;
    104 import java.util.regex.Matcher;
    105 import java.util.regex.Pattern;
    106 import java.util.zip.CRC32;
    107 import java.util.zip.Checksum;
    108 
    109 import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
    110 
    111 
    112 /**
    113  * This class provides the API to manage configured
    114  * wifi networks. The API is not thread safe is being
    115  * used only from WifiStateMachine.
    116  *
    117  * It deals with the following
    118  * - Add/update/remove a WifiConfiguration
    119  *   The configuration contains two types of information.
    120  *     = IP and proxy configuration that is handled by WifiConfigStore and
    121  *       is saved to disk on any change.
    122  *
    123  *       The format of configuration file is as follows:
    124  *       <version>
    125  *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
    126  *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
    127  *       ..
    128  *
    129  *       (key, value) pairs for a given network are grouped together and can
    130  *       be in any order. A EOS at the end of a set of (key, value) pairs
    131  *       indicates that the next set of (key, value) pairs are for a new
    132  *       network. A network is identified by a unique ID_KEY. If there is no
    133  *       ID_KEY in the (key, value) pairs, the data is discarded.
    134  *
    135  *       An invalid version on read would result in discarding the contents of
    136  *       the file. On the next write, the latest version is written to file.
    137  *
    138  *       Any failures during read or write to the configuration file are ignored
    139  *       without reporting to the user since the likelihood of these errors are
    140  *       low and the impact on connectivity is low.
    141  *
    142  *     = SSID & security details that is pushed to the supplicant.
    143  *       supplicant saves these details to the disk on calling
    144  *       saveConfigCommand().
    145  *
    146  *       We have two kinds of APIs exposed:
    147  *        > public API calls that provide fine grained control
    148  *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
    149  *          removeNetwork(). For these calls, the config is not persisted
    150  *          to the disk. (TODO: deprecate these calls in WifiManager)
    151  *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
    152  *          These calls persist the supplicant config to disk.
    153  *
    154  * - Maintain a list of configured networks for quick access
    155  *
    156  */
    157 public class WifiConfigStore extends IpConfigStore {
    158 
    159     private Context mContext;
    160     public static final String TAG = "WifiConfigStore";
    161     private static final boolean DBG = true;
    162     private static boolean VDBG = false;
    163     private static boolean VVDBG = false;
    164 
    165     private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
    166     private static final String SUPPLICANT_CONFIG_FILE_BACKUP = SUPPLICANT_CONFIG_FILE + ".tmp";
    167     private static final String PPS_FILE = "/data/misc/wifi/PerProviderSubscription.conf";
    168 
    169     /* configured networks with network id as the key */
    170     private final ConfigurationMap mConfiguredNetworks = new ConfigurationMap();
    171 
    172     /* A network id is a unique identifier for a network configured in the
    173      * supplicant. Network ids are generated when the supplicant reads
    174      * the configuration file at start and can thus change for networks.
    175      * We store the IP configuration for networks along with a unique id
    176      * that is generated from SSID and security type of the network. A mapping
    177      * from the generated unique id to network id of the network is needed to
    178      * map supplicant config to IP configuration. */
    179 
    180     /* Stores a map of NetworkId to ScanCache */
    181     private HashMap<Integer, ScanDetailCache> mScanDetailCaches;
    182 
    183     /**
    184      * Framework keeps a list of (the CRC32 hashes of) all SSIDs that where deleted by user,
    185      * so as, framework knows not to re-add those SSIDs automatically to the Saved networks
    186      */
    187     private Set<Long> mDeletedSSIDs = new HashSet<Long>();
    188 
    189     /**
    190      * Framework keeps a list of ephemeral SSIDs that where deleted by user,
    191      * so as, framework knows not to autojoin again those SSIDs based on scorer input.
    192      * The list is never cleared up.
    193      *
    194      * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
    195      */
    196     public Set<String> mDeletedEphemeralSSIDs = new HashSet<String>();
    197 
    198     /* Tracks the highest priority of configured networks */
    199     private int mLastPriority = -1;
    200 
    201     private static final String ipConfigFile = Environment.getDataDirectory() +
    202             "/misc/wifi/ipconfig.txt";
    203 
    204     private static final String networkHistoryConfigFile = Environment.getDataDirectory() +
    205             "/misc/wifi/networkHistory.txt";
    206 
    207     private static final String autoJoinConfigFile = Environment.getDataDirectory() +
    208             "/misc/wifi/autojoinconfig.txt";
    209 
    210     /* Network History Keys */
    211     private static final String SSID_KEY = "SSID";
    212     private static final String CONFIG_KEY = "CONFIG";
    213     private static final String CHOICE_KEY = "CHOICE";
    214     private static final String LINK_KEY = "LINK";
    215     private static final String BSSID_KEY = "BSSID";
    216     private static final String BSSID_KEY_END = "/BSSID";
    217     private static final String RSSI_KEY = "RSSI";
    218     private static final String FREQ_KEY = "FREQ";
    219     private static final String DATE_KEY = "DATE";
    220     private static final String MILLI_KEY = "MILLI";
    221     private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI";
    222     private static final String NETWORK_ID_KEY = "ID";
    223     private static final String PRIORITY_KEY = "PRIORITY";
    224     private static final String DEFAULT_GW_KEY = "DEFAULT_GW";
    225     private static final String AUTH_KEY = "AUTH";
    226     private static final String STATUS_KEY = "AUTO_JOIN_STATUS";
    227     private static final String BSSID_STATUS_KEY = "BSSID_STATUS";
    228     private static final String SELF_ADDED_KEY = "SELF_ADDED";
    229     private static final String FAILURE_KEY = "FAILURE";
    230     private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD";
    231     private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION";
    232     private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY";
    233     private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY";
    234     private static final String UPDATE_UID_KEY = "UPDATE_UID";
    235     private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS";
    236     private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON";
    237     private static final String FQDN_KEY = "FQDN";
    238     private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES";
    239     private static final String NUM_IP_CONFIG_FAILURES_KEY = "IP_CONFIG_FAILURES";
    240     private static final String NUM_AUTH_FAILURES_KEY = "AUTH_FAILURES";
    241     private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE";
    242     private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH";
    243     private static final String VALIDATED_INTERNET_ACCESS_KEY = "VALIDATED_INTERNET_ACCESS";
    244     private static final String NO_INTERNET_ACCESS_REPORTS_KEY = "NO_INTERNET_ACCESS_REPORTS";
    245     private static final String EPHEMERAL_KEY = "EPHEMERAL";
    246     private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION";
    247     private static final String DELETED_CRC32_KEY = "DELETED_CRC32";
    248     private static final String DELETED_EPHEMERAL_KEY = "DELETED_EPHEMERAL";
    249     private static final String JOIN_ATTEMPT_BOOST_KEY = "JOIN_ATTEMPT_BOOST";
    250     private static final String CREATOR_NAME_KEY = "CREATOR_NAME";
    251     private static final String UPDATE_NAME_KEY = "UPDATE_NAME";
    252     private static final String USER_APPROVED_KEY = "USER_APPROVED";
    253     private static final String CREATION_TIME_KEY = "CREATION_TIME";
    254     private static final String UPDATE_TIME_KEY = "UPDATE_TIME";
    255 
    256     private static final String SEPARATOR = ":  ";
    257     private static final String NL = "\n";
    258 
    259     private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY
    260             = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G";
    261     private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY
    262             = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G";
    263     private static final String THRESHOLD_UNBLACKLIST_HARD_5G_KEY
    264             = "THRESHOLD_UNBLACKLIST_HARD_5G";
    265     private static final String THRESHOLD_UNBLACKLIST_SOFT_5G_KEY
    266             = "THRESHOLD_UNBLACKLIST_SOFT_5G";
    267     private static final String THRESHOLD_UNBLACKLIST_HARD_24G_KEY
    268             = "THRESHOLD_UNBLACKLIST_HARD_24G";
    269     private static final String THRESHOLD_UNBLACKLIST_SOFT_24G_KEY
    270             = "THRESHOLD_UNBLACKLIST_SOFT_24G";
    271     private static final String THRESHOLD_GOOD_RSSI_5_KEY
    272             = "THRESHOLD_GOOD_RSSI_5";
    273     private static final String THRESHOLD_LOW_RSSI_5_KEY
    274             = "THRESHOLD_LOW_RSSI_5";
    275     private static final String THRESHOLD_BAD_RSSI_5_KEY
    276             = "THRESHOLD_BAD_RSSI_5";
    277     private static final String THRESHOLD_GOOD_RSSI_24_KEY
    278             = "THRESHOLD_GOOD_RSSI_24";
    279     private static final String THRESHOLD_LOW_RSSI_24_KEY
    280             = "THRESHOLD_LOW_RSSI_24";
    281     private static final String THRESHOLD_BAD_RSSI_24_KEY
    282             = "THRESHOLD_BAD_RSSI_24";
    283 
    284     private static final String THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY
    285             = "THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING";
    286     private static final String THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY
    287             = "THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING";
    288 
    289     private static final String THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY
    290             = "THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS";
    291     private static final String THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY
    292             = "THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS";
    293 
    294     private static final String THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY
    295             = "THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS";
    296     private static final String THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY
    297             = "THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS";
    298 
    299     private static final String MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY
    300             = "MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS";
    301     private static final String MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY
    302             = "MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS";
    303 
    304     private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY =
    305             "A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW";
    306     private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
    307             "A_BAND_PREFERENCE_RSSI_THRESHOLD";
    308     private static final String G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
    309             "G_BAND_PREFERENCE_RSSI_THRESHOLD";
    310 
    311     private static final String ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY
    312             = "ENABLE_AUTOJOIN_WHILE_ASSOCIATED:   ";
    313 
    314     private static final String ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY
    315             = "ASSOCIATED_PARTIAL_SCAN_PERIOD";
    316     private static final String ASSOCIATED_FULL_SCAN_BACKOFF_KEY
    317             = "ASSOCIATED_FULL_SCAN_BACKOFF_PERIOD";
    318     private static final String ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY
    319             = "ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED";
    320     private static final String ONLY_LINK_SAME_CREDENTIAL_CONFIGURATIONS_KEY
    321             = "ONLY_LINK_SAME_CREDENTIAL_CONFIGURATIONS";
    322 
    323     private static final String ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY
    324             = "ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED";
    325 
    326     private static final String ENABLE_HAL_BASED_PNO
    327             = "ENABLE_HAL_BASED_PNO";
    328 
    329     // The three below configurations are mainly for power stats and CPU usage tracking
    330     // allowing to incrementally disable framework features
    331     private static final String ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY
    332             = "ENABLE_AUTO_JOIN_WHILE_ASSOCIATED";
    333     private static final String ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY
    334             = "ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED";
    335     private static final String ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY
    336             = "ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY";
    337 
    338     public static final String idStringVarName = "id_str";
    339 
    340     // The Wifi verbose log is provided as a way to persist the verbose logging settings
    341     // for testing purpose.
    342     // It is not intended for normal use.
    343     private static final String WIFI_VERBOSE_LOGS_KEY
    344             = "WIFI_VERBOSE_LOGS";
    345 
    346     // As we keep deleted PSK WifiConfiguration for a while, the PSK of
    347     // those deleted WifiConfiguration is set to this random unused PSK
    348     private static final String DELETED_CONFIG_PSK = "Mjkd86jEMGn79KhKll298Uu7-deleted";
    349 
    350     public int maxTxPacketForFullScans = 8;
    351     public int maxRxPacketForFullScans = 16;
    352 
    353     public int maxTxPacketForPartialScans = 40;
    354     public int maxRxPacketForPartialScans = 80;
    355 
    356     public int associatedFullScanMaxIntervalMilli = 300000;
    357 
    358     // Sane value for roam blacklisting (not switching to a network if already associated)
    359     // 2 days
    360     public int networkSwitchingBlackListPeriodMilli = 2 * 24 * 60 * 60 * 1000;
    361 
    362     public int bandPreferenceBoostFactor5 = 5; // Boost by 5 dB per dB above threshold
    363     public int bandPreferencePenaltyFactor5 = 2; // Penalize by 2 dB per dB below threshold
    364 
    365     public int badLinkSpeed24 = 6;
    366     public int badLinkSpeed5 = 12;
    367     public int goodLinkSpeed24 = 24;
    368     public int goodLinkSpeed5 = 36;
    369 
    370     public int maxAuthErrorsToBlacklist = 4;
    371     public int maxConnectionErrorsToBlacklist = 4;
    372     public int wifiConfigBlacklistMinTimeMilli = 1000 * 60 * 5;
    373 
    374     // How long a disconnected config remain considered as the last user selection
    375     public int wifiConfigLastSelectionHysteresis = 1000 * 60 * 3;
    376 
    377     // Boost RSSI values of associated networks
    378     public int associatedHysteresisHigh = +14;
    379     public int associatedHysteresisLow = +8;
    380 
    381     boolean showNetworks = true; // TODO set this back to false, used for debugging 17516271
    382 
    383     public boolean roamOnAny = false;
    384     public boolean onlyLinkSameCredentialConfigurations = true;
    385 
    386     public boolean enableLinkDebouncing = true;
    387     public boolean enable5GHzPreference = true;
    388     public boolean enableWifiCellularHandoverUserTriggeredAdjustment = true;
    389 
    390     public int currentNetworkBoost = 25;
    391     public int scanResultRssiLevelPatchUp = -85;
    392 
    393     public static final int maxNumScanCacheEntries = 128;
    394 
    395     public final AtomicBoolean enableHalBasedPno = new AtomicBoolean(true);
    396     public final AtomicBoolean enableSsidWhitelist = new AtomicBoolean(true);
    397     public final AtomicBoolean enableAutoJoinWhenAssociated = new AtomicBoolean(true);
    398     public final AtomicBoolean enableFullBandScanWhenAssociated = new AtomicBoolean(true);
    399     public final AtomicBoolean enableChipWakeUpWhenAssociated = new AtomicBoolean(true);
    400     public final AtomicBoolean enableRssiPollWhenAssociated = new AtomicBoolean(true);
    401     public final AtomicInteger thresholdInitialAutoJoinAttemptMin5RSSI =
    402             new AtomicInteger(WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5);
    403     public final AtomicInteger thresholdInitialAutoJoinAttemptMin24RSSI =
    404             new AtomicInteger(WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24);
    405     public final AtomicInteger thresholdUnblacklistThreshold5Hard
    406             = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD);
    407     public final AtomicInteger thresholdUnblacklistThreshold5Soft
    408             = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_5_SOFT);
    409     public final AtomicInteger thresholdUnblacklistThreshold24Hard
    410             = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD);
    411     public final AtomicInteger thresholdUnblacklistThreshold24Soft
    412             = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT);
    413     public final AtomicInteger thresholdGoodRssi5 =
    414             new AtomicInteger(WifiConfiguration.GOOD_RSSI_5);
    415     public final AtomicInteger thresholdLowRssi5 = new AtomicInteger(WifiConfiguration.LOW_RSSI_5);
    416     public final AtomicInteger thresholdBadRssi5 = new AtomicInteger(WifiConfiguration.BAD_RSSI_5);
    417     public final AtomicInteger thresholdGoodRssi24 =
    418             new AtomicInteger(WifiConfiguration.GOOD_RSSI_24);
    419     public final AtomicInteger thresholdLowRssi24 = new AtomicInteger(WifiConfiguration.LOW_RSSI_24);
    420     public final AtomicInteger thresholdBadRssi24 = new AtomicInteger(WifiConfiguration.BAD_RSSI_24);
    421     public final AtomicInteger maxTxPacketForNetworkSwitching = new AtomicInteger(40);
    422     public final AtomicInteger maxRxPacketForNetworkSwitching = new AtomicInteger(80);
    423     public final AtomicInteger enableVerboseLogging = new AtomicInteger(0);
    424     public final AtomicInteger bandPreferenceBoostThreshold5 =
    425             new AtomicInteger(WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD);
    426     public final AtomicInteger associatedFullScanBackoff =
    427             new AtomicInteger(12); // Will be divided by 8 by WifiStateMachine
    428     public final AtomicInteger bandPreferencePenaltyThreshold5 =
    429             new AtomicInteger(WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD);
    430     public final AtomicInteger alwaysEnableScansWhileAssociated = new AtomicInteger(0);
    431     public final AtomicInteger maxNumPassiveChannelsForPartialScans = new AtomicInteger(2);
    432     public final AtomicInteger maxNumActiveChannelsForPartialScans = new AtomicInteger(6);
    433     public final AtomicInteger wifiDisconnectedShortScanIntervalMilli = new AtomicInteger(15000);
    434     public final AtomicInteger wifiDisconnectedLongScanIntervalMilli = new AtomicInteger(120000);
    435     public final AtomicInteger wifiAssociatedShortScanIntervalMilli = new AtomicInteger(20000);
    436     public final AtomicInteger wifiAssociatedLongScanIntervalMilli = new AtomicInteger(180000);
    437 
    438     private static final Map<String, Object> sKeyMap = new HashMap<>();
    439 
    440     /**
    441      * Regex pattern for extracting a connect choice.
    442      * Matches a strings like the following:
    443      * <configKey>=([0:9]+)
    444      */
    445     private static Pattern mConnectChoice =
    446             Pattern.compile("(.*)=([0-9]+)");
    447 
    448 
    449     /* Enterprise configuration keys */
    450     /**
    451      * In old configurations, the "private_key" field was used. However, newer
    452      * configurations use the key_id field with the engine_id set to "keystore".
    453      * If this field is found in the configuration, the migration code is
    454      * triggered.
    455      */
    456     public static final String OLD_PRIVATE_KEY_NAME = "private_key";
    457 
    458     /**
    459      * This represents an empty value of an enterprise field.
    460      * NULL is used at wpa_supplicant to indicate an empty value
    461      */
    462     static final String EMPTY_VALUE = "NULL";
    463 
    464     // Internal use only
    465     private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] {
    466             WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY,
    467             WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY,
    468             WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY,
    469             WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY,
    470             WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
    471             WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY,
    472             WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY
    473     };
    474 
    475 
    476     /**
    477      * If Connectivity Service has triggered an unwanted network disconnect
    478      */
    479     public long lastUnwantedNetworkDisconnectTimestamp = 0;
    480 
    481     /**
    482      * The maximum number of times we will retry a connection to an access point
    483      * for which we have failed in acquiring an IP address from DHCP. A value of
    484      * N means that we will make N+1 connection attempts in all.
    485      * <p>
    486      * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
    487      * value if a Settings value is not present.
    488      */
    489     private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
    490 
    491 
    492     private final LocalLog mLocalLog;
    493     private final WpaConfigFileObserver mFileObserver;
    494 
    495     private WifiNative mWifiNative;
    496     private final KeyStore mKeyStore = KeyStore.getInstance();
    497 
    498     /**
    499      * The lastSelectedConfiguration is used to remember which network
    500      * was selected last by the user.
    501      * The connection to this network may not be successful, as well
    502      * the selection (i.e. network priority) might not be persisted.
    503      * WiFi state machine is the only object that sets this variable.
    504      */
    505     private String lastSelectedConfiguration = null;
    506 
    507     /**
    508      * Cached PNO list, it is updated when WifiConfiguration changes due to user input.
    509      */
    510     ArrayList<WifiNative.WifiPnoNetwork> mCachedPnoList
    511             = new ArrayList<WifiNative.WifiPnoNetwork>();
    512 
    513     /*
    514      * BSSID blacklist, i.e. list of BSSID we want to avoid
    515      */
    516     HashSet<String> mBssidBlacklist = new HashSet<String>();
    517 
    518     /*
    519      * Lost config list, whenever we read a config from networkHistory.txt that was not in
    520      * wpa_supplicant.conf
    521      */
    522     HashSet<String> mLostConfigsDbg = new HashSet<String>();
    523 
    524     private final AnqpCache mAnqpCache;
    525     private final SupplicantBridge mSupplicantBridge;
    526     private final MOManager mMOManager;
    527     private final SIMAccessor mSIMAccessor;
    528 
    529     private WifiStateMachine mWifiStateMachine;
    530 
    531     WifiConfigStore(Context c,  WifiStateMachine w, WifiNative wn) {
    532         mContext = c;
    533         mWifiNative = wn;
    534         mWifiStateMachine = w;
    535 
    536         // A map for value setting in readAutoJoinConfig() - replacing the replicated code.
    537         sKeyMap.put(ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY, enableAutoJoinWhenAssociated);
    538         sKeyMap.put(ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY, enableFullBandScanWhenAssociated);
    539         sKeyMap.put(ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY, enableChipWakeUpWhenAssociated);
    540         sKeyMap.put(ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY, enableRssiPollWhenAssociated);
    541         sKeyMap.put(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY, thresholdInitialAutoJoinAttemptMin5RSSI);
    542         sKeyMap.put(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY, thresholdInitialAutoJoinAttemptMin24RSSI);
    543         sKeyMap.put(THRESHOLD_UNBLACKLIST_HARD_5G_KEY, thresholdUnblacklistThreshold5Hard);
    544         sKeyMap.put(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY, thresholdUnblacklistThreshold5Soft);
    545         sKeyMap.put(THRESHOLD_UNBLACKLIST_HARD_24G_KEY, thresholdUnblacklistThreshold24Hard);
    546         sKeyMap.put(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY, thresholdUnblacklistThreshold24Soft);
    547         sKeyMap.put(THRESHOLD_GOOD_RSSI_5_KEY, thresholdGoodRssi5);
    548         sKeyMap.put(THRESHOLD_LOW_RSSI_5_KEY, thresholdLowRssi5);
    549         sKeyMap.put(THRESHOLD_BAD_RSSI_5_KEY, thresholdBadRssi5);
    550         sKeyMap.put(THRESHOLD_GOOD_RSSI_24_KEY, thresholdGoodRssi24);
    551         sKeyMap.put(THRESHOLD_LOW_RSSI_24_KEY, thresholdLowRssi24);
    552         sKeyMap.put(THRESHOLD_BAD_RSSI_24_KEY, thresholdBadRssi24);
    553         sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY, maxTxPacketForNetworkSwitching);
    554         sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY, maxRxPacketForNetworkSwitching);
    555         sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY, maxTxPacketForNetworkSwitching);
    556         sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY, maxRxPacketForNetworkSwitching);
    557         sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY, maxTxPacketForNetworkSwitching);
    558         sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY, maxRxPacketForNetworkSwitching);
    559         sKeyMap.put(WIFI_VERBOSE_LOGS_KEY, enableVerboseLogging);
    560         sKeyMap.put(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, bandPreferenceBoostThreshold5);
    561         sKeyMap.put(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY, wifiAssociatedShortScanIntervalMilli);
    562         sKeyMap.put(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY, wifiAssociatedShortScanIntervalMilli);
    563 
    564         sKeyMap.put(ASSOCIATED_FULL_SCAN_BACKOFF_KEY, associatedFullScanBackoff);
    565         sKeyMap.put(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, bandPreferencePenaltyThreshold5);
    566         sKeyMap.put(ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY, alwaysEnableScansWhileAssociated);
    567         sKeyMap.put(MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, maxNumPassiveChannelsForPartialScans);
    568         sKeyMap.put(MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, maxNumActiveChannelsForPartialScans);
    569         sKeyMap.put(ENABLE_HAL_BASED_PNO, enableHalBasedPno);
    570         sKeyMap.put(ENABLE_HAL_BASED_PNO, enableSsidWhitelist);
    571 
    572         if (showNetworks) {
    573             mLocalLog = mWifiNative.getLocalLog();
    574             mFileObserver = new WpaConfigFileObserver();
    575             mFileObserver.startWatching();
    576         } else {
    577             mLocalLog = null;
    578             mFileObserver = null;
    579         }
    580 
    581         wifiAssociatedShortScanIntervalMilli.set(mContext.getResources().getInteger(
    582                 R.integer.config_wifi_associated_short_scan_interval));
    583         wifiAssociatedLongScanIntervalMilli.set(mContext.getResources().getInteger(
    584                 R.integer.config_wifi_associated_short_scan_interval));
    585         wifiDisconnectedShortScanIntervalMilli.set(mContext.getResources().getInteger(
    586                 R.integer.config_wifi_disconnected_short_scan_interval));
    587         wifiDisconnectedLongScanIntervalMilli.set(mContext.getResources().getInteger(
    588                 R.integer.config_wifi_disconnected_long_scan_interval));
    589 
    590         onlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
    591                 R.bool.config_wifi_only_link_same_credential_configurations);
    592         maxNumActiveChannelsForPartialScans.set(mContext.getResources().getInteger(
    593                 R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels));
    594         maxNumPassiveChannelsForPartialScans.set(mContext.getResources().getInteger(
    595                 R.integer.config_wifi_framework_associated_partial_scan_max_num_passive_channels));
    596         associatedFullScanMaxIntervalMilli = mContext.getResources().getInteger(
    597                 R.integer.config_wifi_framework_associated_full_scan_max_interval);
    598         associatedFullScanBackoff.set(mContext.getResources().getInteger(
    599                 R.integer.config_wifi_framework_associated_full_scan_backoff));
    600         enableLinkDebouncing = mContext.getResources().getBoolean(
    601                 R.bool.config_wifi_enable_disconnection_debounce);
    602 
    603         enable5GHzPreference = mContext.getResources().getBoolean(
    604                 R.bool.config_wifi_enable_5GHz_preference);
    605 
    606         bandPreferenceBoostFactor5 = mContext.getResources().getInteger(
    607                 R.integer.config_wifi_framework_5GHz_preference_boost_factor);
    608         bandPreferencePenaltyFactor5 = mContext.getResources().getInteger(
    609                 R.integer.config_wifi_framework_5GHz_preference_penalty_factor);
    610 
    611         bandPreferencePenaltyThreshold5.set(mContext.getResources().getInteger(
    612                 R.integer.config_wifi_framework_5GHz_preference_penalty_threshold));
    613         bandPreferenceBoostThreshold5.set(mContext.getResources().getInteger(
    614                 R.integer.config_wifi_framework_5GHz_preference_boost_threshold));
    615 
    616         associatedHysteresisHigh = mContext.getResources().getInteger(
    617                 R.integer.config_wifi_framework_current_association_hysteresis_high);
    618         associatedHysteresisLow = mContext.getResources().getInteger(
    619                 R.integer.config_wifi_framework_current_association_hysteresis_low);
    620 
    621         thresholdBadRssi5.set(mContext.getResources().getInteger(
    622                 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz));
    623         thresholdLowRssi5.set(mContext.getResources().getInteger(
    624                 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz));
    625         thresholdGoodRssi5.set(mContext.getResources().getInteger(
    626                 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz));
    627         thresholdBadRssi24.set(mContext.getResources().getInteger(
    628                 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz));
    629         thresholdLowRssi24.set(mContext.getResources().getInteger(
    630                 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz));
    631         thresholdGoodRssi24.set(mContext.getResources().getInteger(
    632                 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz));
    633 
    634         enableWifiCellularHandoverUserTriggeredAdjustment = mContext.getResources().getBoolean(
    635                 R.bool.config_wifi_framework_cellular_handover_enable_user_triggered_adjustment);
    636 
    637         badLinkSpeed24 = mContext.getResources().getInteger(
    638                 R.integer.config_wifi_framework_wifi_score_bad_link_speed_24);
    639         badLinkSpeed5 = mContext.getResources().getInteger(
    640                 R.integer.config_wifi_framework_wifi_score_bad_link_speed_5);
    641         goodLinkSpeed24 = mContext.getResources().getInteger(
    642                 R.integer.config_wifi_framework_wifi_score_good_link_speed_24);
    643         goodLinkSpeed5 = mContext.getResources().getInteger(
    644                 R.integer.config_wifi_framework_wifi_score_good_link_speed_5);
    645 
    646         maxAuthErrorsToBlacklist = mContext.getResources().getInteger(
    647                 R.integer.config_wifi_framework_max_auth_errors_to_blacklist);
    648         maxConnectionErrorsToBlacklist = mContext.getResources().getInteger(
    649                 R.integer.config_wifi_framework_max_connection_errors_to_blacklist);
    650         wifiConfigBlacklistMinTimeMilli = mContext.getResources().getInteger(
    651                 R.integer.config_wifi_framework_network_black_list_min_time_milli);
    652 
    653         enableAutoJoinWhenAssociated.set(mContext.getResources().getBoolean(
    654                 R.bool.config_wifi_framework_enable_associated_network_selection));
    655 
    656         currentNetworkBoost = mContext.getResources().getInteger(
    657                 R.integer.config_wifi_framework_current_network_boost);
    658 
    659         scanResultRssiLevelPatchUp = mContext.getResources().getInteger(
    660                 R.integer.config_wifi_framework_scan_result_rssi_level_patchup_value);
    661 
    662         networkSwitchingBlackListPeriodMilli = mContext.getResources().getInteger(
    663                 R.integer.config_wifi_network_switching_blacklist_time);
    664 
    665         enableHalBasedPno.set(mContext.getResources().getBoolean(
    666                         R.bool.config_wifi_hal_pno_enable));
    667 
    668         enableSsidWhitelist.set(mContext.getResources().getBoolean(
    669                 R.bool.config_wifi_ssid_white_list_enable));
    670         if (!enableHalBasedPno.get() && enableSsidWhitelist.get()) {
    671             enableSsidWhitelist.set(false);
    672         }
    673 
    674         boolean hs2on = mContext.getResources().getBoolean(R.bool.config_wifi_hotspot2_enabled);
    675         Log.d(Utils.hs2LogTag(getClass()), "Passpoint is " + (hs2on ? "enabled" : "disabled"));
    676 
    677         mMOManager = new MOManager(new File(PPS_FILE), hs2on);
    678         mAnqpCache = new AnqpCache();
    679         mSupplicantBridge = new SupplicantBridge(mWifiNative, this);
    680         mScanDetailCaches = new HashMap<>();
    681 
    682         mSIMAccessor = new SIMAccessor(mContext);
    683     }
    684 
    685     public void trimANQPCache(boolean all) {
    686         mAnqpCache.clear(all, DBG);
    687     }
    688 
    689     void enableVerboseLogging(int verbose) {
    690         enableVerboseLogging.set(verbose);
    691         if (verbose > 0) {
    692             VDBG = true;
    693             showNetworks = true;
    694         } else {
    695             VDBG = false;
    696         }
    697         if (verbose > 1) {
    698             VVDBG = true;
    699         } else {
    700             VVDBG = false;
    701         }
    702     }
    703 
    704     class WpaConfigFileObserver extends FileObserver {
    705 
    706         public WpaConfigFileObserver() {
    707             super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
    708         }
    709 
    710         @Override
    711         public void onEvent(int event, String path) {
    712             if (event == CLOSE_WRITE) {
    713                 File file = new File(SUPPLICANT_CONFIG_FILE);
    714                 if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
    715             }
    716         }
    717     }
    718 
    719 
    720     /**
    721      * Fetch the list of configured networks
    722      * and enable all stored networks in supplicant.
    723      */
    724     void loadAndEnableAllNetworks() {
    725         if (DBG) log("Loading config and enabling all networks ");
    726         loadConfiguredNetworks();
    727         enableAllNetworks();
    728     }
    729 
    730     int getConfiguredNetworksSize() {
    731         return mConfiguredNetworks.size();
    732     }
    733 
    734     private List<WifiConfiguration>
    735     getConfiguredNetworks(Map<String, String> pskMap) {
    736         List<WifiConfiguration> networks = new ArrayList<>();
    737         for(WifiConfiguration config : mConfiguredNetworks.values()) {
    738             WifiConfiguration newConfig = new WifiConfiguration(config);
    739             // When updating this condition, update WifiStateMachine's CONNECT_NETWORK handler to
    740             // correctly handle updating existing configs that are filtered out here.
    741             if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || config.ephemeral) {
    742                 // Do not enumerate and return this configuration to any one,
    743                 // for instance WiFi Picker.
    744                 // instead treat it as unknown. the configuration can still be retrieved
    745                 // directly by the key or networkId
    746                 continue;
    747             }
    748 
    749             if (pskMap != null && config.allowedKeyManagement != null
    750                     && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
    751                     && pskMap.containsKey(config.SSID)) {
    752                 newConfig.preSharedKey = pskMap.get(config.SSID);
    753             }
    754             networks.add(newConfig);
    755         }
    756         return networks;
    757     }
    758 
    759     /**
    760      * This function returns all configuration, and is used for cebug and creating bug reports.
    761      */
    762     private List<WifiConfiguration>
    763     getAllConfiguredNetworks() {
    764         List<WifiConfiguration> networks = new ArrayList<>();
    765         for(WifiConfiguration config : mConfiguredNetworks.values()) {
    766             WifiConfiguration newConfig = new WifiConfiguration(config);
    767             networks.add(newConfig);
    768         }
    769         return networks;
    770     }
    771 
    772     /**
    773      * Fetch the list of currently configured networks
    774      * @return List of networks
    775      */
    776     List<WifiConfiguration> getConfiguredNetworks() {
    777         return getConfiguredNetworks(null);
    778     }
    779 
    780     /**
    781      * Fetch the list of currently configured networks, filled with real preSharedKeys
    782      * @return List of networks
    783      */
    784     List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
    785         Map<String, String> pskMap = getCredentialsBySsidMap();
    786         return getConfiguredNetworks(pskMap);
    787     }
    788 
    789     /**
    790      * Find matching network for this scanResult
    791      */
    792     WifiConfiguration getMatchingConfig(ScanResult scanResult) {
    793 
    794         for (Map.Entry entry : mScanDetailCaches.entrySet()) {
    795             Integer netId = (Integer) entry.getKey();
    796             ScanDetailCache cache = (ScanDetailCache) entry.getValue();
    797             WifiConfiguration config = getWifiConfiguration(netId);
    798             if (config == null)
    799                 continue;
    800             if (cache.get(scanResult.BSSID) != null) {
    801                 return config;
    802             }
    803         }
    804 
    805         return null;
    806     }
    807 
    808     /**
    809      * Fetch the preSharedKeys for all networks.
    810      * @return a map from Ssid to preSharedKey.
    811      */
    812     private Map<String, String> getCredentialsBySsidMap() {
    813         return readNetworkVariablesFromSupplicantFile("psk");
    814     }
    815 
    816     /**
    817      * Fetch the list of currently configured networks that were recently seen
    818      *
    819      * @return List of networks
    820      */
    821     List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) {
    822         List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
    823 
    824         for (WifiConfiguration config : mConfiguredNetworks.values()) {
    825             if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || config.ephemeral) {
    826                 // Do not enumerate and return this configuration to any one,
    827                 // instead treat it as unknown. the configuration can still be retrieved
    828                 // directly by the key or networkId
    829                 continue;
    830             }
    831 
    832             // Calculate the RSSI for scan results that are more recent than milli
    833             ScanDetailCache cache = getScanDetailCache(config);
    834             if (cache == null) {
    835                 continue;
    836             }
    837             config.setVisibility(cache.getVisibility(milli));
    838             if (config.visibility == null) {
    839                 continue;
    840             }
    841             if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI &&
    842                     config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) {
    843                 continue;
    844             }
    845             if (copy) {
    846                 networks.add(new WifiConfiguration(config));
    847             } else {
    848                 networks.add(config);
    849             }
    850         }
    851         return networks;
    852     }
    853 
    854     /**
    855      *  Update the configuration and BSSID with latest RSSI value.
    856      */
    857     void updateConfiguration(WifiInfo info) {
    858         WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
    859         if (config != null && getScanDetailCache(config) != null) {
    860             ScanDetail scanDetail = getScanDetailCache(config).getScanDetail(info.getBSSID());
    861             if (scanDetail != null) {
    862                 ScanResult result = scanDetail.getScanResult();
    863                 long previousSeen = result.seen;
    864                 int previousRssi = result.level;
    865 
    866                 // Update the scan result
    867                 scanDetail.setSeen();
    868                 result.level = info.getRssi();
    869 
    870                 // Average the RSSI value
    871                 result.averageRssi(previousRssi, previousSeen,
    872                         WifiAutoJoinController.mScanResultMaximumAge);
    873                 if (VDBG) {
    874                     loge("updateConfiguration freq=" + result.frequency
    875                         + " BSSID=" + result.BSSID
    876                         + " RSSI=" + result.level
    877                         + " " + config.configKey());
    878                 }
    879             }
    880         }
    881     }
    882 
    883     /**
    884      * get the Wificonfiguration for this netId
    885      *
    886      * @return Wificonfiguration
    887      */
    888     WifiConfiguration getWifiConfiguration(int netId) {
    889         return mConfiguredNetworks.get(netId);
    890     }
    891 
    892     /**
    893      * Get the Wificonfiguration for this key
    894      * @return Wificonfiguration
    895      */
    896     WifiConfiguration getWifiConfiguration(String key) {
    897         return mConfiguredNetworks.getByConfigKey(key);
    898     }
    899 
    900     /**
    901      * Enable all networks and save config. This will be a no-op if the list
    902      * of configured networks indicates all networks as being enabled
    903      */
    904     void enableAllNetworks() {
    905         long now = System.currentTimeMillis();
    906         boolean networkEnabledStateChanged = false;
    907 
    908         for(WifiConfiguration config : mConfiguredNetworks.values()) {
    909 
    910             if(config != null && config.status == Status.DISABLED && !config.ephemeral
    911                     && (config.autoJoinStatus
    912                     <= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)) {
    913 
    914                 // Wait for 5 minutes before reenabling config that have known, repeated connection
    915                 // or DHCP failures
    916                 if (config.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE
    917                         || config.disableReason == WifiConfiguration.DISABLED_ASSOCIATION_REJECT
    918                         || config.disableReason == WifiConfiguration.DISABLED_AUTH_FAILURE) {
    919                     if (config.blackListTimestamp != 0
    920                            && now > config.blackListTimestamp
    921                            && (now - config.blackListTimestamp) < wifiConfigBlacklistMinTimeMilli) {
    922                         continue;
    923                     }
    924                 }
    925 
    926                 if(mWifiNative.enableNetwork(config.networkId, false)) {
    927                     networkEnabledStateChanged = true;
    928                     config.status = Status.ENABLED;
    929 
    930                     // Reset the blacklist condition
    931                     config.numConnectionFailures = 0;
    932                     config.numIpConfigFailures = 0;
    933                     config.numAuthFailures = 0;
    934 
    935                     // Reenable the wifi configuration
    936                     config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
    937                 } else {
    938                     loge("Enable network failed on " + config.networkId);
    939 
    940                 }
    941             }
    942         }
    943 
    944         if (networkEnabledStateChanged) {
    945             mWifiNative.saveConfig();
    946             sendConfiguredNetworksChangedBroadcast();
    947         }
    948     }
    949 
    950     private boolean setNetworkPriorityNative(int netId, int priority) {
    951         return mWifiNative.setNetworkVariable(netId,
    952                 WifiConfiguration.priorityVarName, Integer.toString(priority));
    953     }
    954 
    955     private boolean setSSIDNative(int netId, String ssid) {
    956         return mWifiNative.setNetworkVariable(netId, WifiConfiguration.ssidVarName,
    957                 encodeSSID(ssid));
    958     }
    959 
    960     public boolean updateLastConnectUid(WifiConfiguration config, int uid) {
    961         if (config != null) {
    962             if (config.lastConnectUid != uid) {
    963                 config.lastConnectUid = uid;
    964                 config.dirty = true;
    965                 return true;
    966             }
    967         }
    968         return false;
    969     }
    970 
    971     /**
    972      * Selects the specified network for connection. This involves
    973      * updating the priority of all the networks and enabling the given
    974      * network while disabling others.
    975      *
    976      * Selecting a network will leave the other networks disabled and
    977      * a call to enableAllNetworks() needs to be issued upon a connection
    978      * or a failure event from supplicant
    979      *
    980      * @param config network to select for connection
    981      * @param updatePriorities makes config highest priority network
    982      * @return false if the network id is invalid
    983      */
    984     boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) {
    985         if (VDBG) localLog("selectNetwork", config.networkId);
    986         if (config.networkId == INVALID_NETWORK_ID) return false;
    987 
    988         // Reset the priority of each network at start or if it goes too high.
    989         if (mLastPriority == -1 || mLastPriority > 1000000) {
    990             for(WifiConfiguration config2 : mConfiguredNetworks.values()) {
    991                 if (updatePriorities) {
    992                     if (config2.networkId != INVALID_NETWORK_ID) {
    993                         config2.priority = 0;
    994                         setNetworkPriorityNative(config2.networkId, config.priority);
    995                     }
    996                 }
    997             }
    998             mLastPriority = 0;
    999         }
   1000 
   1001         // Set to the highest priority and save the configuration.
   1002         if (updatePriorities) {
   1003             config.priority = ++mLastPriority;
   1004             setNetworkPriorityNative(config.networkId, config.priority);
   1005             buildPnoList();
   1006         }
   1007 
   1008         if (config.isPasspoint()) {
   1009             /* need to slap on the SSID of selected bssid to work */
   1010             if (getScanDetailCache(config).size() != 0) {
   1011                 ScanDetail result = getScanDetailCache(config).getFirst();
   1012                 if (result == null) {
   1013                     loge("Could not find scan result for " + config.BSSID);
   1014                 } else {
   1015                     log("Setting SSID for " + config.networkId + " to" + result.getSSID());
   1016                     setSSIDNative(config.networkId, result.getSSID());
   1017                     config.SSID = result.getSSID();
   1018                 }
   1019 
   1020             } else {
   1021                 loge("Could not find bssid for " + config);
   1022             }
   1023         }
   1024 
   1025         if (updatePriorities)
   1026             mWifiNative.saveConfig();
   1027         else
   1028             mWifiNative.selectNetwork(config.networkId);
   1029 
   1030         updateLastConnectUid(config, uid);
   1031         writeKnownNetworkHistory(false);
   1032 
   1033         /* Enable the given network while disabling all other networks */
   1034         enableNetworkWithoutBroadcast(config.networkId, true);
   1035 
   1036        /* Avoid saving the config & sending a broadcast to prevent settings
   1037         * from displaying a disabled list of networks */
   1038         return true;
   1039     }
   1040 
   1041     /**
   1042      * Add/update the specified configuration and save config
   1043      *
   1044      * @param config WifiConfiguration to be saved
   1045      * @return network update result
   1046      */
   1047     NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) {
   1048         WifiConfiguration conf;
   1049 
   1050         // A new network cannot have null SSID
   1051         if (config == null || (config.networkId == INVALID_NETWORK_ID &&
   1052                 config.SSID == null)) {
   1053             return new NetworkUpdateResult(INVALID_NETWORK_ID);
   1054         }
   1055         if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);
   1056         if (VDBG) {
   1057             loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()
   1058                     + " SSID=" + config.SSID
   1059                     + " Uid=" + Integer.toString(config.creatorUid)
   1060                     + "/" + Integer.toString(config.lastUpdateUid));
   1061         }
   1062 
   1063         if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
   1064             if (VDBG) {
   1065                 loge("WifiConfigStore: removed from ephemeral blacklist: " + config.SSID);
   1066             }
   1067             // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call
   1068             // below, since we're creating/modifying a config.
   1069         }
   1070 
   1071         boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
   1072         NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
   1073         int netId = result.getNetworkId();
   1074 
   1075         if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);
   1076 
   1077         /* enable a new network */
   1078         if (newNetwork && netId != INVALID_NETWORK_ID) {
   1079             if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);
   1080 
   1081             mWifiNative.enableNetwork(netId, false);
   1082             conf = mConfiguredNetworks.get(netId);
   1083             if (conf != null)
   1084                 conf.status = Status.ENABLED;
   1085         }
   1086 
   1087         conf = mConfiguredNetworks.get(netId);
   1088         if (conf != null) {
   1089             if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
   1090                 if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);
   1091 
   1092                 // reenable autojoin, since new information has been provided
   1093                 conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
   1094                 enableNetworkWithoutBroadcast(conf.networkId, false);
   1095             }
   1096             if (VDBG) {
   1097                 loge("WifiConfigStore: saveNetwork got config back netId="
   1098                         + Integer.toString(netId)
   1099                         + " uid=" + Integer.toString(config.creatorUid));
   1100             }
   1101         }
   1102 
   1103         mWifiNative.saveConfig();
   1104         sendConfiguredNetworksChangedBroadcast(conf, result.isNewNetwork() ?
   1105                 WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
   1106         return result;
   1107     }
   1108 
   1109     /**
   1110      * Firmware is roaming away from this BSSID, and this BSSID was on 5GHz, and it's RSSI was good,
   1111      * this means we have a situation where we would want to remain on this BSSID but firmware
   1112      * is not successful at it.
   1113      * This situation is observed on a small number of Access Points, b/17960587
   1114      * In that situation, blacklist this BSSID really hard so as framework will not attempt to
   1115      * roam to it for the next 8 hours. We do not to keep flipping between 2.4 and 5GHz band..
   1116      * TODO: review the blacklisting strategy so as to make it softer and adaptive
   1117      * @param info
   1118      */
   1119     void driverRoamedFrom(WifiInfo info) {
   1120         if (info != null
   1121             && info.getBSSID() != null
   1122             && ScanResult.is5GHz(info.getFrequency())
   1123             && info.getRssi() > (bandPreferenceBoostThreshold5.get() + 3)) {
   1124             WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
   1125             if (config != null) {
   1126                 if (getScanDetailCache(config) != null) {
   1127                     ScanResult result = getScanDetailCache(config).get(info.getBSSID());
   1128                     if (result != null) {
   1129                         result.setAutoJoinStatus(ScanResult.AUTO_ROAM_DISABLED + 1);
   1130                     }
   1131                 }
   1132             }
   1133         }
   1134     }
   1135 
   1136     void noteRoamingFailure(WifiConfiguration config, int reason) {
   1137         if (config == null) return;
   1138         config.lastRoamingFailure = System.currentTimeMillis();
   1139         config.roamingFailureBlackListTimeMilli
   1140                 = 2 * (config.roamingFailureBlackListTimeMilli + 1000);
   1141         if (config.roamingFailureBlackListTimeMilli
   1142                 > networkSwitchingBlackListPeriodMilli) {
   1143             config.roamingFailureBlackListTimeMilli =
   1144                     networkSwitchingBlackListPeriodMilli;
   1145         }
   1146         config.lastRoamingFailureReason = reason;
   1147     }
   1148 
   1149     void saveWifiConfigBSSID(WifiConfiguration config) {
   1150         // Sanity check the config is valid
   1151         if (config == null || (config.networkId == INVALID_NETWORK_ID &&
   1152                 config.SSID == null)) {
   1153             return;
   1154         }
   1155 
   1156         // If an app specified a BSSID then dont over-write it
   1157         if (config.BSSID != null && config.BSSID != "any") {
   1158             return;
   1159         }
   1160 
   1161         // If autojoin specified a BSSID then write it in the network block
   1162         if (config.autoJoinBSSID != null) {
   1163             loge("saveWifiConfigBSSID Setting BSSID for " + config.configKey()
   1164                     + " to " + config.autoJoinBSSID);
   1165             if (!mWifiNative.setNetworkVariable(
   1166                     config.networkId,
   1167                     WifiConfiguration.bssidVarName,
   1168                     config.autoJoinBSSID)) {
   1169                 loge("failed to set BSSID: " + config.autoJoinBSSID);
   1170             } else if (config.autoJoinBSSID.equals("any")) {
   1171                 // Paranoia, we just want to make sure that we restore the config to normal
   1172                 mWifiNative.saveConfig();
   1173             }
   1174         }
   1175     }
   1176 
   1177 
   1178     void updateStatus(int netId, DetailedState state) {
   1179         if (netId != INVALID_NETWORK_ID) {
   1180             WifiConfiguration config = mConfiguredNetworks.get(netId);
   1181             if (config == null) return;
   1182             switch (state) {
   1183                 case CONNECTED:
   1184                     config.status = Status.CURRENT;
   1185                     //we successfully connected, hence remove the blacklist
   1186                     config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
   1187                     break;
   1188                 case DISCONNECTED:
   1189                     //If network is already disabled, keep the status
   1190                     if (config.status == Status.CURRENT) {
   1191                         config.status = Status.ENABLED;
   1192                     }
   1193                     break;
   1194                 default:
   1195                     //do nothing, retain the existing state
   1196                     break;
   1197             }
   1198         }
   1199     }
   1200 
   1201 
   1202     /**
   1203      * Disable an ephemeral SSID for the purpose of auto-joining thru scored.
   1204      * This SSID will never be scored anymore.
   1205      * The only way to "un-disable it" is if the user create a network for that SSID and then
   1206      * forget it.
   1207      *
   1208      * @param SSID caller must ensure that the SSID passed thru this API match
   1209      *            the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
   1210      * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
   1211      *         disconnect if this is the current network.
   1212      */
   1213     WifiConfiguration disableEphemeralNetwork(String SSID) {
   1214         if (SSID == null) {
   1215             return null;
   1216         }
   1217 
   1218         WifiConfiguration foundConfig = mConfiguredNetworks.getEphemeral(SSID);
   1219 
   1220         mDeletedEphemeralSSIDs.add(SSID);
   1221         loge("Forget ephemeral SSID " + SSID + " num=" + mDeletedEphemeralSSIDs.size());
   1222 
   1223         if (foundConfig != null) {
   1224             loge("Found ephemeral config in disableEphemeralNetwork: " + foundConfig.networkId);
   1225         }
   1226 
   1227         // Force a write, because the mDeletedEphemeralSSIDs list has changed even though the
   1228         // configurations may not have.
   1229         writeKnownNetworkHistory(true);
   1230 
   1231         return foundConfig;
   1232     }
   1233 
   1234     /**
   1235      * Forget the specified network and save config
   1236      *
   1237      * @param netId network to forget
   1238      * @return {@code true} if it succeeds, {@code false} otherwise
   1239      */
   1240     boolean forgetNetwork(int netId) {
   1241         if (showNetworks) localLog("forgetNetwork", netId);
   1242 
   1243         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1244         boolean remove = removeConfigAndSendBroadcastIfNeeded(netId);
   1245         if (!remove) {
   1246             //success but we dont want to remove the network from supplicant conf file
   1247             return true;
   1248         }
   1249         if (mWifiNative.removeNetwork(netId)) {
   1250             if (config != null && config.isPasspoint()) {
   1251                 writePasspointConfigs(config.FQDN, null);
   1252             }
   1253             mWifiNative.saveConfig();
   1254             writeKnownNetworkHistory(true);
   1255             return true;
   1256         } else {
   1257             loge("Failed to remove network " + netId);
   1258             return false;
   1259         }
   1260     }
   1261 
   1262     /**
   1263      * Add/update a network. Note that there is no saveConfig operation.
   1264      * This function is retained for compatibility with the public
   1265      * API. The more powerful saveNetwork() is used by the
   1266      * state machine
   1267      *
   1268      * @param config wifi configuration to add/update
   1269      * @return network Id
   1270      */
   1271     int addOrUpdateNetwork(WifiConfiguration config, int uid) {
   1272         if (showNetworks) localLog("addOrUpdateNetwork id=", config.networkId);
   1273         //adding unconditional message to chase b/15111865
   1274         Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId)
   1275                 + " uid=" + Integer.toString(config.creatorUid)
   1276                 + "/" + Integer.toString(config.lastUpdateUid));
   1277 
   1278         if (config.isPasspoint()) {
   1279             /* create a temporary SSID with providerFriendlyName */
   1280             Long csum = getChecksum(config.FQDN);
   1281             config.SSID = csum.toString();
   1282             config.enterpriseConfig.setDomainSuffixMatch(config.FQDN);
   1283         }
   1284 
   1285         NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
   1286         if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
   1287             WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId());
   1288             if (conf != null) {
   1289                 sendConfiguredNetworksChangedBroadcast(conf,
   1290                     result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
   1291                             WifiManager.CHANGE_REASON_CONFIG_CHANGE);
   1292             }
   1293         }
   1294 
   1295         return result.getNetworkId();
   1296     }
   1297 
   1298 
   1299     /**
   1300      * Get the Wifi PNO list
   1301      *
   1302      * @return list of WifiNative.WifiPnoNetwork
   1303      */
   1304     private void buildPnoList() {
   1305         mCachedPnoList = new ArrayList<WifiNative.WifiPnoNetwork>();
   1306 
   1307         ArrayList<WifiConfiguration> sortedWifiConfigurations
   1308                 = new ArrayList<WifiConfiguration>(getConfiguredNetworks());
   1309         Log.e(TAG, "buildPnoList sortedWifiConfigurations size " + sortedWifiConfigurations.size());
   1310         if (sortedWifiConfigurations.size() != 0) {
   1311             // Sort by descending priority
   1312             Collections.sort(sortedWifiConfigurations, new Comparator<WifiConfiguration>() {
   1313                 public int compare(WifiConfiguration a, WifiConfiguration b) {
   1314                     return a.priority >= b.priority ? 1 : -1;
   1315                 }
   1316             });
   1317         }
   1318 
   1319         for (WifiConfiguration config : sortedWifiConfigurations) {
   1320             // Initialize the RSSI threshold with sane value:
   1321             // Use the 2.4GHz threshold since most WifiConfigurations are dual bands
   1322             // There is very little penalty with triggering too soon, i.e. if PNO finds a network
   1323             // that has an RSSI too low for us to attempt joining it.
   1324             int threshold = thresholdInitialAutoJoinAttemptMin24RSSI.get();
   1325             Log.e(TAG, "found sortedWifiConfigurations : " + config.configKey());
   1326             WifiNative.WifiPnoNetwork network = mWifiNative.new WifiPnoNetwork(config, threshold);
   1327             mCachedPnoList.add(network);
   1328         }
   1329     }
   1330 
   1331     String[] getWhiteListedSsids(WifiConfiguration config) {
   1332         int num_ssids = 0;
   1333         String nonQuoteSSID;
   1334         int length;
   1335         if (enableSsidWhitelist.get() == false)
   1336             return null;
   1337         List<String> list = new ArrayList<String>();
   1338         if (config == null)
   1339             return null;
   1340         if (config.linkedConfigurations == null) {
   1341             return null;
   1342         }
   1343         if (config.SSID == null || TextUtils.isEmpty(config.SSID)) {
   1344             return null;
   1345         }
   1346         for (String configKey : config.linkedConfigurations.keySet()) {
   1347 
   1348             // Sanity check that the linked configuration is still valid
   1349             WifiConfiguration link = getWifiConfiguration(configKey);
   1350             if (link == null) {
   1351                 continue;
   1352             }
   1353 
   1354             if (link.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
   1355                 continue;
   1356             }
   1357 
   1358             if (link.hiddenSSID == true) {
   1359                 continue;
   1360             }
   1361 
   1362             if (link.SSID == null || TextUtils.isEmpty(link.SSID)) {
   1363                 continue;
   1364             }
   1365 
   1366             length = link.SSID.length();
   1367             if (length > 2 && (link.SSID.charAt(0) == '"') && link.SSID.charAt(length - 1) == '"') {
   1368                 nonQuoteSSID = link.SSID.substring(1, length - 1);
   1369             } else {
   1370                 nonQuoteSSID = link.SSID;
   1371             }
   1372 
   1373             list.add(nonQuoteSSID);
   1374         }
   1375 
   1376         if (list.size() != 0) {
   1377             length = config.SSID.length();
   1378             if (length > 2 && (config.SSID.charAt(0) == '"')
   1379                     && config.SSID.charAt(length - 1) == '"') {
   1380                 nonQuoteSSID = config.SSID.substring(1, length - 1);
   1381             } else {
   1382                 nonQuoteSSID = config.SSID;
   1383             }
   1384 
   1385             list.add(nonQuoteSSID);
   1386         }
   1387 
   1388         return (String[])list.toArray(new String[0]);
   1389     }
   1390 
   1391     /**
   1392      * Remove a network. Note that there is no saveConfig operation.
   1393      * This function is retained for compatibility with the public
   1394      * API. The more powerful forgetNetwork() is used by the
   1395      * state machine for network removal
   1396      *
   1397      * @param netId network to be removed
   1398      * @return {@code true} if it succeeds, {@code false} otherwise
   1399      */
   1400     boolean removeNetwork(int netId) {
   1401         if (showNetworks) localLog("removeNetwork", netId);
   1402         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1403         boolean ret = mWifiNative.removeNetwork(netId);
   1404         if (ret) {
   1405             removeConfigAndSendBroadcastIfNeeded(netId);
   1406             if (config != null && config.isPasspoint()) {
   1407                 writePasspointConfigs(config.FQDN, null);
   1408             }
   1409         }
   1410         return ret;
   1411     }
   1412 
   1413 
   1414     static private Long getChecksum(String source) {
   1415         Checksum csum = new CRC32();
   1416         csum.update(source.getBytes(), 0, source.getBytes().length);
   1417         return csum.getValue();
   1418     }
   1419 
   1420     private boolean removeConfigAndSendBroadcastIfNeeded(int netId) {
   1421         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1422         if (config != null) {
   1423             if (VDBG) {
   1424                 loge("removeNetwork " + Integer.toString(netId) + " key=" +
   1425                         config.configKey() + " config.id=" + Integer.toString(config.networkId));
   1426             }
   1427 
   1428             // cancel the last user choice
   1429             if (config.configKey().equals(lastSelectedConfiguration)) {
   1430                 lastSelectedConfiguration = null;
   1431             }
   1432 
   1433             // Remove any associated keys
   1434             if (config.enterpriseConfig != null) {
   1435                 removeKeys(config.enterpriseConfig);
   1436             }
   1437 
   1438             if (config.selfAdded || config.linkedConfigurations != null
   1439                     || config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
   1440                 if (!TextUtils.isEmpty(config.SSID)) {
   1441                     /* Remember that we deleted this PSK SSID */
   1442                     if (config.SSID != null) {
   1443                         Long csum = getChecksum(config.SSID);
   1444                         mDeletedSSIDs.add(csum);
   1445                         loge("removeNetwork " + Integer.toString(netId)
   1446                                 + " key=" + config.configKey()
   1447                                 + " config.id=" + Integer.toString(config.networkId)
   1448                                 + "  crc=" + csum);
   1449                     } else {
   1450                         loge("removeNetwork " + Integer.toString(netId)
   1451                                 + " key=" + config.configKey()
   1452                                 + " config.id=" + Integer.toString(config.networkId));
   1453                     }
   1454                 }
   1455             }
   1456 
   1457             mConfiguredNetworks.remove(netId);
   1458             mScanDetailCaches.remove(netId);
   1459 
   1460             writeIpAndProxyConfigurations();
   1461             sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
   1462             writeKnownNetworkHistory(true);
   1463         }
   1464         return true;
   1465     }
   1466 
   1467     /*
   1468      * Remove all networks associated with an application
   1469      *
   1470      * @param packageName name of the package of networks to remove
   1471      * @return {@code true} if all networks removed successfully, {@code false} otherwise
   1472      */
   1473     boolean removeNetworksForApp(ApplicationInfo app) {
   1474         if (app == null || app.packageName == null) {
   1475             return false;
   1476         }
   1477 
   1478         boolean success = true;
   1479 
   1480         WifiConfiguration [] copiedConfigs =
   1481                 mConfiguredNetworks.values().toArray(new WifiConfiguration[0]);
   1482         for (WifiConfiguration config : copiedConfigs) {
   1483             if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) {
   1484                 continue;
   1485             }
   1486             if (showNetworks) {
   1487                 localLog("Removing network " + config.SSID
   1488                          + ", application \"" + app.packageName + "\" uninstalled"
   1489                          + " from user " + UserHandle.getUserId(app.uid));
   1490             }
   1491             success &= removeNetwork(config.networkId);
   1492         }
   1493 
   1494         mWifiNative.saveConfig();
   1495 
   1496         return success;
   1497     }
   1498 
   1499     boolean removeNetworksForUser(int userId) {
   1500         boolean success = true;
   1501 
   1502         WifiConfiguration[] copiedConfigs =
   1503                 mConfiguredNetworks.values().toArray(new WifiConfiguration[0]);
   1504         for (WifiConfiguration config : copiedConfigs) {
   1505             if (userId != UserHandle.getUserId(config.creatorUid)) {
   1506                 continue;
   1507             }
   1508             success &= removeNetwork(config.networkId);
   1509             if (showNetworks) {
   1510                 localLog("Removing network " + config.SSID
   1511                         + ", user " + userId + " removed");
   1512             }
   1513         }
   1514 
   1515         return success;
   1516     }
   1517 
   1518     /**
   1519      * Enable a network. Note that there is no saveConfig operation.
   1520      * This function is retained for compatibility with the public
   1521      * API. The more powerful selectNetwork()/saveNetwork() is used by the
   1522      * state machine for connecting to a network
   1523      *
   1524      * @param netId network to be enabled
   1525      * @return {@code true} if it succeeds, {@code false} otherwise
   1526      */
   1527     boolean enableNetwork(int netId, boolean disableOthers, int uid) {
   1528         boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
   1529         if (disableOthers) {
   1530             if (VDBG) localLog("enableNetwork(disableOthers=true, uid=" + uid + ") ", netId);
   1531             updateLastConnectUid(getWifiConfiguration(netId), uid);
   1532             writeKnownNetworkHistory(false);
   1533             sendConfiguredNetworksChangedBroadcast();
   1534         } else {
   1535             if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId);
   1536             WifiConfiguration enabledNetwork;
   1537             synchronized(mConfiguredNetworks) {                     // !!! Useless synchronization!
   1538                 enabledNetwork = mConfiguredNetworks.get(netId);
   1539             }
   1540             // check just in case the network was removed by someone else.
   1541             if (enabledNetwork != null) {
   1542                 sendConfiguredNetworksChangedBroadcast(enabledNetwork,
   1543                         WifiManager.CHANGE_REASON_CONFIG_CHANGE);
   1544             }
   1545         }
   1546         return ret;
   1547     }
   1548 
   1549     boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
   1550         boolean ret = mWifiNative.enableNetwork(netId, disableOthers);
   1551 
   1552         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1553         if (config != null) config.status = Status.ENABLED;
   1554 
   1555         if (disableOthers) {
   1556             markAllNetworksDisabledExcept(netId);
   1557         }
   1558         return ret;
   1559     }
   1560 
   1561     void disableAllNetworks() {
   1562         if (VDBG) localLog("disableAllNetworks");
   1563         boolean networkDisabled = false;
   1564         for (WifiConfiguration enabled : mConfiguredNetworks.getEnabledNetworks()) {
   1565             if(mWifiNative.disableNetwork(enabled.networkId)) {
   1566                 networkDisabled = true;
   1567                 enabled.status = Status.DISABLED;
   1568             } else {
   1569                 loge("Disable network failed on " + enabled.networkId);
   1570             }
   1571         }
   1572 
   1573         if (networkDisabled) {
   1574             sendConfiguredNetworksChangedBroadcast();
   1575         }
   1576     }
   1577     /**
   1578      * Disable a network. Note that there is no saveConfig operation.
   1579      * @param netId network to be disabled
   1580      * @return {@code true} if it succeeds, {@code false} otherwise
   1581      */
   1582     boolean disableNetwork(int netId) {
   1583         boolean ret = disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
   1584         if (ret) {
   1585             mWifiStateMachine.registerNetworkDisabled(netId);
   1586         }
   1587         return ret;
   1588     }
   1589 
   1590     /**
   1591      * Disable a network. Note that there is no saveConfig operation.
   1592      * @param netId network to be disabled
   1593      * @param reason reason code network was disabled
   1594      * @return {@code true} if it succeeds, {@code false} otherwise
   1595      */
   1596     boolean disableNetwork(int netId, int reason) {
   1597         if (VDBG) localLog("disableNetwork", netId);
   1598         boolean ret = mWifiNative.disableNetwork(netId);
   1599         WifiConfiguration network = null;
   1600         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1601 
   1602         if (VDBG) {
   1603             if (config != null) {
   1604                 loge("disableNetwork netId=" + Integer.toString(netId)
   1605                         + " SSID=" + config.SSID
   1606                         + " disabled=" + (config.status == Status.DISABLED)
   1607                         + " reason=" + Integer.toString(config.disableReason));
   1608             }
   1609         }
   1610         /* Only change the reason if the network was not previously disabled
   1611         /* and the reason is not DISABLED_BY_WIFI_MANAGER, that is, if a 3rd party
   1612          * set its configuration as disabled, then leave it disabled */
   1613         if (config != null) {
   1614             if (config.status != Status.DISABLED
   1615                 && config.disableReason != WifiConfiguration.DISABLED_BY_WIFI_MANAGER) {
   1616                 config.status = Status.DISABLED;
   1617                 config.disableReason = reason;
   1618                 network = config;
   1619             }
   1620             if (reason == WifiConfiguration.DISABLED_BY_WIFI_MANAGER) {
   1621                 // Make sure autojoin wont reenable this configuration without further user
   1622                 // intervention
   1623                 config.status = Status.DISABLED;
   1624                 config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_DISABLED_USER_ACTION;
   1625             }
   1626         }
   1627         if (network != null) {
   1628             sendConfiguredNetworksChangedBroadcast(network,
   1629                     WifiManager.CHANGE_REASON_CONFIG_CHANGE);
   1630         }
   1631         return ret;
   1632     }
   1633 
   1634     /**
   1635      * Save the configured networks in supplicant to disk
   1636      * @return {@code true} if it succeeds, {@code false} otherwise
   1637      */
   1638     boolean saveConfig() {
   1639         return mWifiNative.saveConfig();
   1640     }
   1641 
   1642     /**
   1643      * Start WPS pin method configuration with pin obtained
   1644      * from the access point
   1645      * @param config WPS configuration
   1646      * @return Wps result containing status and pin
   1647      */
   1648     WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
   1649         WpsResult result = new WpsResult();
   1650         if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
   1651             /* WPS leaves all networks disabled */
   1652             markAllNetworksDisabled();
   1653             result.status = WpsResult.Status.SUCCESS;
   1654         } else {
   1655             loge("Failed to start WPS pin method configuration");
   1656             result.status = WpsResult.Status.FAILURE;
   1657         }
   1658         return result;
   1659     }
   1660 
   1661     /**
   1662      * Start WPS pin method configuration with pin obtained
   1663      * from the device
   1664      * @return WpsResult indicating status and pin
   1665      */
   1666     WpsResult startWpsWithPinFromDevice(WpsInfo config) {
   1667         WpsResult result = new WpsResult();
   1668         result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
   1669         /* WPS leaves all networks disabled */
   1670         if (!TextUtils.isEmpty(result.pin)) {
   1671             markAllNetworksDisabled();
   1672             result.status = WpsResult.Status.SUCCESS;
   1673         } else {
   1674             loge("Failed to start WPS pin method configuration");
   1675             result.status = WpsResult.Status.FAILURE;
   1676         }
   1677         return result;
   1678     }
   1679 
   1680     /**
   1681      * Start WPS push button configuration
   1682      * @param config WPS configuration
   1683      * @return WpsResult indicating status and pin
   1684      */
   1685     WpsResult startWpsPbc(WpsInfo config) {
   1686         WpsResult result = new WpsResult();
   1687         if (mWifiNative.startWpsPbc(config.BSSID)) {
   1688             /* WPS leaves all networks disabled */
   1689             markAllNetworksDisabled();
   1690             result.status = WpsResult.Status.SUCCESS;
   1691         } else {
   1692             loge("Failed to start WPS push button configuration");
   1693             result.status = WpsResult.Status.FAILURE;
   1694         }
   1695         return result;
   1696     }
   1697 
   1698     /**
   1699      * Fetch the static IP configuration for a given network id
   1700      */
   1701     StaticIpConfiguration getStaticIpConfiguration(int netId) {
   1702         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1703         if (config != null) {
   1704             return config.getStaticIpConfiguration();
   1705         }
   1706         return null;
   1707     }
   1708 
   1709     /**
   1710      * Set the static IP configuration for a given network id
   1711      */
   1712     void setStaticIpConfiguration(int netId, StaticIpConfiguration staticIpConfiguration) {
   1713         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1714         if (config != null) {
   1715             config.setStaticIpConfiguration(staticIpConfiguration);
   1716         }
   1717     }
   1718 
   1719     /**
   1720      * set default GW MAC address
   1721      */
   1722     void setDefaultGwMacAddress(int netId, String macAddress) {
   1723         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1724         if (config != null) {
   1725             //update defaultGwMacAddress
   1726             config.defaultGwMacAddress = macAddress;
   1727         }
   1728     }
   1729 
   1730 
   1731     /**
   1732      * Fetch the proxy properties for a given network id
   1733      * @param netId id
   1734      * @return ProxyInfo for the network id
   1735      */
   1736     ProxyInfo getProxyProperties(int netId) {
   1737         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1738         if (config != null) {
   1739             return config.getHttpProxy();
   1740         }
   1741         return null;
   1742     }
   1743 
   1744     /**
   1745      * Return if the specified network is using static IP
   1746      * @param netId id
   1747      * @return {@code true} if using static ip for netId
   1748      */
   1749     boolean isUsingStaticIp(int netId) {
   1750         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1751         if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {
   1752             return true;
   1753         }
   1754         return false;
   1755     }
   1756 
   1757     boolean isEphemeral(int netId) {
   1758         WifiConfiguration config = mConfiguredNetworks.get(netId);
   1759         return config != null && config.ephemeral;
   1760     }
   1761 
   1762     /**
   1763      * Should be called when a single network configuration is made.
   1764      * @param network The network configuration that changed.
   1765      * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
   1766      * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
   1767      */
   1768     private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
   1769             int reason) {
   1770         Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
   1771         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1772         intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
   1773         intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
   1774         intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
   1775         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
   1776     }
   1777 
   1778     /**
   1779      * Should be called when multiple network configuration changes are made.
   1780      */
   1781     private void sendConfiguredNetworksChangedBroadcast() {
   1782         Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
   1783         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1784         intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
   1785         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
   1786     }
   1787 
   1788     void loadConfiguredNetworks() {
   1789 
   1790         mLastPriority = 0;
   1791 
   1792         mConfiguredNetworks.clear();
   1793 
   1794         int last_id = -1;
   1795         boolean done = false;
   1796         while (!done) {
   1797 
   1798             String listStr = mWifiNative.listNetworks(last_id);
   1799             if (listStr == null)
   1800                 return;
   1801 
   1802             String[] lines = listStr.split("\n");
   1803 
   1804             if (showNetworks) {
   1805                 localLog("WifiConfigStore: loadConfiguredNetworks:  ");
   1806                 for (String net : lines) {
   1807                     localLog(net);
   1808                 }
   1809             }
   1810 
   1811             // Skip the first line, which is a header
   1812             for (int i = 1; i < lines.length; i++) {
   1813                 String[] result = lines[i].split("\t");
   1814                 // network-id | ssid | bssid | flags
   1815                 WifiConfiguration config = new WifiConfiguration();
   1816                 try {
   1817                     config.networkId = Integer.parseInt(result[0]);
   1818                     last_id = config.networkId;
   1819                 } catch(NumberFormatException e) {
   1820                     loge("Failed to read network-id '" + result[0] + "'");
   1821                     continue;
   1822                 }
   1823                 if (result.length > 3) {
   1824                     if (result[3].indexOf("[CURRENT]") != -1)
   1825                         config.status = WifiConfiguration.Status.CURRENT;
   1826                     else if (result[3].indexOf("[DISABLED]") != -1)
   1827                         config.status = WifiConfiguration.Status.DISABLED;
   1828                     else
   1829                         config.status = WifiConfiguration.Status.ENABLED;
   1830                 } else {
   1831                     config.status = WifiConfiguration.Status.ENABLED;
   1832                 }
   1833 
   1834                 readNetworkVariables(config);
   1835 
   1836                 Checksum csum = new CRC32();
   1837                 if (config.SSID != null) {
   1838                     csum.update(config.SSID.getBytes(), 0, config.SSID.getBytes().length);
   1839                     long d = csum.getValue();
   1840                     if (mDeletedSSIDs.contains(d)) {
   1841                         loge(" got CRC for SSID " + config.SSID + " -> " + d + ", was deleted");
   1842                     }
   1843                 }
   1844 
   1845                 if (config.priority > mLastPriority) {
   1846                     mLastPriority = config.priority;
   1847                 }
   1848 
   1849                 config.setIpAssignment(IpAssignment.DHCP);
   1850                 config.setProxySettings(ProxySettings.NONE);
   1851 
   1852                 if (mConfiguredNetworks.getByConfigKey(config.configKey()) != null) {
   1853                     // That SSID is already known, just ignore this duplicate entry
   1854                     if (showNetworks) localLog("discarded duplicate network ", config.networkId);
   1855                 } else if(WifiServiceImpl.isValid(config)){
   1856                     mConfiguredNetworks.put(config.networkId, config);
   1857                     if (showNetworks) localLog("loaded configured network", config.networkId);
   1858                 } else {
   1859                     if (showNetworks) log("Ignoring loaded configured for network " + config.networkId
   1860                         + " because config are not valid");
   1861                 }
   1862             }
   1863 
   1864             done = (lines.length == 1);
   1865         }
   1866 
   1867         readPasspointConfig();
   1868         readIpAndProxyConfigurations();
   1869         readNetworkHistory();
   1870         readAutoJoinConfig();
   1871 
   1872         buildPnoList();
   1873 
   1874         sendConfiguredNetworksChangedBroadcast();
   1875 
   1876         if (showNetworks) localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.size() + " networks");
   1877 
   1878         if (mConfiguredNetworks.isEmpty()) {
   1879             // no networks? Lets log if the file contents
   1880             logKernelTime();
   1881             logContents(SUPPLICANT_CONFIG_FILE);
   1882             logContents(SUPPLICANT_CONFIG_FILE_BACKUP);
   1883             logContents(networkHistoryConfigFile);
   1884         }
   1885     }
   1886 
   1887     private void logContents(String file) {
   1888         localLog("--- Begin " + file + " ---", true);
   1889         BufferedReader reader = null;
   1890         try {
   1891             reader = new BufferedReader(new FileReader(file));
   1892             for (String line = reader.readLine(); line != null; line = reader.readLine()) {
   1893                 localLog(line, true);
   1894             }
   1895         } catch (FileNotFoundException e) {
   1896             localLog("Could not open " + file + ", " + e, true);
   1897         } catch (IOException e) {
   1898             localLog("Could not read " + file + ", " + e, true);
   1899         } finally {
   1900             try {
   1901                 if (reader != null) {
   1902                     reader.close();
   1903                 }
   1904             } catch (IOException e) {
   1905                 // Just ignore the fact that we couldn't close
   1906             }
   1907         }
   1908         localLog("--- End " + file + " Contents ---", true);
   1909     }
   1910 
   1911     private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
   1912         Map<String, String> result = new HashMap<>();
   1913         BufferedReader reader = null;
   1914         if (VDBG) loge("readNetworkVariablesFromSupplicantFile key=" + key);
   1915 
   1916         try {
   1917             reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
   1918             boolean found = false;
   1919             String networkSsid = null;
   1920             String value = null;
   1921 
   1922             for (String line = reader.readLine(); line != null; line = reader.readLine()) {
   1923 
   1924                 if (line.matches("[ \\t]*network=\\{")) {
   1925                     found = true;
   1926                     networkSsid = null;
   1927                     value = null;
   1928                 } else if (line.matches("[ \\t]*\\}")) {
   1929                     found = false;
   1930                     networkSsid = null;
   1931                     value = null;
   1932                 }
   1933 
   1934                 if (found) {
   1935                     String trimmedLine = line.trim();
   1936                     if (trimmedLine.startsWith("ssid=")) {
   1937                         networkSsid = trimmedLine.substring(5);
   1938                     } else if (trimmedLine.startsWith(key + "=")) {
   1939                         value = trimmedLine.substring(key.length() + 1);
   1940                     }
   1941 
   1942                     if (networkSsid != null && value != null) {
   1943                         result.put(networkSsid, value);
   1944                     }
   1945                 }
   1946             }
   1947         } catch (FileNotFoundException e) {
   1948             if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
   1949         } catch (IOException e) {
   1950             if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
   1951         } finally {
   1952             try {
   1953                 if (reader != null) {
   1954                     reader.close();
   1955                 }
   1956             } catch (IOException e) {
   1957                 // Just ignore the fact that we couldn't close
   1958             }
   1959         }
   1960 
   1961         return result;
   1962     }
   1963 
   1964     private String readNetworkVariableFromSupplicantFile(String ssid, String key) {
   1965         long start = SystemClock.elapsedRealtimeNanos();
   1966         Map<String, String> data = readNetworkVariablesFromSupplicantFile(key);
   1967         long end = SystemClock.elapsedRealtimeNanos();
   1968 
   1969         if (VDBG) {
   1970             loge("readNetworkVariableFromSupplicantFile ssid=[" + ssid + "] key=" + key
   1971                     + " duration=" + (long)(end - start));
   1972         }
   1973         return data.get(ssid);
   1974     }
   1975 
   1976     /* Mark all networks except specified netId as disabled */
   1977     private void markAllNetworksDisabledExcept(int netId) {
   1978         for(WifiConfiguration config : mConfiguredNetworks.values()) {
   1979             if(config != null && config.networkId != netId) {
   1980                 if (config.status != Status.DISABLED) {
   1981                     config.status = Status.DISABLED;
   1982                     config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
   1983                 }
   1984             }
   1985         }
   1986     }
   1987 
   1988     private void markAllNetworksDisabled() {
   1989         markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
   1990     }
   1991 
   1992     boolean needsUnlockedKeyStore() {
   1993 
   1994         // Any network using certificates to authenticate access requires
   1995         // unlocked key store; unless the certificates can be stored with
   1996         // hardware encryption
   1997 
   1998         for(WifiConfiguration config : mConfiguredNetworks.values()) {
   1999 
   2000             if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
   2001                     && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
   2002 
   2003                 if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
   2004                     return true;
   2005                 }
   2006             }
   2007         }
   2008 
   2009         return false;
   2010     }
   2011 
   2012     void readPasspointConfig() {
   2013 
   2014         List<HomeSP> homeSPs;
   2015         try {
   2016             homeSPs = mMOManager.loadAllSPs();
   2017         } catch (IOException e) {
   2018             loge("Could not read " + PPS_FILE + " : " + e);
   2019             return;
   2020         }
   2021 
   2022         mConfiguredNetworks.populatePasspointData(homeSPs, mWifiNative);
   2023     }
   2024 
   2025     public void writePasspointConfigs(final String fqdn, final HomeSP homeSP) {
   2026         mWriter.write(PPS_FILE, new DelayedDiskWrite.Writer() {
   2027             @Override
   2028             public void onWriteCalled(DataOutputStream out) throws IOException {
   2029                 try {
   2030                     if (homeSP != null) {
   2031                         mMOManager.addSP(homeSP);
   2032                     }
   2033                     else {
   2034                         mMOManager.removeSP(fqdn);
   2035                     }
   2036                 } catch (IOException e) {
   2037                     loge("Could not write " + PPS_FILE + " : " + e);
   2038                 }
   2039             }
   2040         }, false);
   2041     }
   2042 
   2043     public void writeKnownNetworkHistory(boolean force) {
   2044         boolean needUpdate = force;
   2045 
   2046         /* Make a copy */
   2047         final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
   2048         for (WifiConfiguration config : mConfiguredNetworks.values()) {
   2049             networks.add(new WifiConfiguration(config));
   2050             if (config.dirty == true) {
   2051                 loge(" rewrite network history for " + config.configKey());
   2052                 config.dirty = false;
   2053                 needUpdate = true;
   2054             }
   2055         }
   2056         if (VDBG) {
   2057             loge(" writeKnownNetworkHistory() num networks:" +
   2058                     mConfiguredNetworks.size() + " needWrite=" + needUpdate);
   2059         }
   2060         if (needUpdate == false) {
   2061             return;
   2062         }
   2063         mWriter.write(networkHistoryConfigFile, new DelayedDiskWrite.Writer() {
   2064             public void onWriteCalled(DataOutputStream out) throws IOException {
   2065                 for (WifiConfiguration config : networks) {
   2066                     //loge("onWriteCalled write SSID: " + config.SSID);
   2067                    /* if (config.getLinkProperties() != null)
   2068                         loge(" lp " + config.getLinkProperties().toString());
   2069                     else
   2070                         loge("attempt config w/o lp");
   2071                     */
   2072 
   2073                     if (VDBG) {
   2074                         int num = 0;
   2075                         int numlink = 0;
   2076                         if (config.connectChoices != null) {
   2077                             num = config.connectChoices.size();
   2078                         }
   2079                         if (config.linkedConfigurations != null) {
   2080                             numlink = config.linkedConfigurations.size();
   2081                         }
   2082                         loge("saving network history: " + config.configKey()  + " gw: " +
   2083                                 config.defaultGwMacAddress + " autojoin-status: " +
   2084                                 config.autoJoinStatus + " ephemeral=" + config.ephemeral
   2085                                 + " choices:" + Integer.toString(num)
   2086                                 + " link:" + Integer.toString(numlink)
   2087                                 + " status:" + Integer.toString(config.status)
   2088                                 + " nid:" + Integer.toString(config.networkId));
   2089                     }
   2090 
   2091                     if (!WifiServiceImpl.isValid(config))
   2092                         continue;
   2093 
   2094                     if (config.SSID == null) {
   2095                         if (VDBG) {
   2096                             loge("writeKnownNetworkHistory trying to write config with null SSID");
   2097                         }
   2098                         continue;
   2099                     }
   2100                     if (VDBG) {
   2101                         loge("writeKnownNetworkHistory write config " + config.configKey());
   2102                     }
   2103                     out.writeUTF(CONFIG_KEY + SEPARATOR + config.configKey() + NL);
   2104 
   2105                     if (config.SSID != null) {
   2106                         out.writeUTF(SSID_KEY + SEPARATOR + config.SSID + NL);
   2107                     }
   2108                     if (config.FQDN != null) {
   2109                         out.writeUTF(FQDN_KEY + SEPARATOR + config.FQDN + NL);
   2110                     }
   2111 
   2112                     out.writeUTF(PRIORITY_KEY + SEPARATOR +
   2113                             Integer.toString(config.priority) + NL);
   2114                     out.writeUTF(STATUS_KEY + SEPARATOR +
   2115                             Integer.toString(config.autoJoinStatus) + NL);
   2116                     out.writeUTF(SUPPLICANT_STATUS_KEY + SEPARATOR +
   2117                             Integer.toString(config.status) + NL);
   2118                     out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY + SEPARATOR +
   2119                             Integer.toString(config.disableReason) + NL);
   2120                     out.writeUTF(NETWORK_ID_KEY + SEPARATOR +
   2121                             Integer.toString(config.networkId) + NL);
   2122                     out.writeUTF(SELF_ADDED_KEY + SEPARATOR +
   2123                             Boolean.toString(config.selfAdded) + NL);
   2124                     out.writeUTF(DID_SELF_ADD_KEY + SEPARATOR +
   2125                             Boolean.toString(config.didSelfAdd) + NL);
   2126                     out.writeUTF(NO_INTERNET_ACCESS_REPORTS_KEY + SEPARATOR +
   2127                             Integer.toString(config.numNoInternetAccessReports) + NL);
   2128                     out.writeUTF(VALIDATED_INTERNET_ACCESS_KEY + SEPARATOR +
   2129                             Boolean.toString(config.validatedInternetAccess) + NL);
   2130                     out.writeUTF(EPHEMERAL_KEY + SEPARATOR +
   2131                             Boolean.toString(config.ephemeral) + NL);
   2132                     if (config.creationTime != null) {
   2133                         out.writeUTF(CREATION_TIME_KEY + SEPARATOR + config.creationTime + NL);
   2134                     }
   2135                     if (config.updateTime != null) {
   2136                         out.writeUTF(UPDATE_TIME_KEY + SEPARATOR + config.updateTime + NL);
   2137                     }
   2138                     if (config.peerWifiConfiguration != null) {
   2139                         out.writeUTF(PEER_CONFIGURATION_KEY + SEPARATOR +
   2140                                 config.peerWifiConfiguration + NL);
   2141                     }
   2142                     out.writeUTF(NUM_CONNECTION_FAILURES_KEY + SEPARATOR +
   2143                             Integer.toString(config.numConnectionFailures) + NL);
   2144                     out.writeUTF(NUM_AUTH_FAILURES_KEY + SEPARATOR +
   2145                             Integer.toString(config.numAuthFailures) + NL);
   2146                     out.writeUTF(NUM_IP_CONFIG_FAILURES_KEY + SEPARATOR +
   2147                             Integer.toString(config.numIpConfigFailures) + NL);
   2148                     out.writeUTF(SCORER_OVERRIDE_KEY + SEPARATOR +
   2149                             Integer.toString(config.numScorerOverride) + NL);
   2150                     out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY + SEPARATOR +
   2151                             Integer.toString(config.numScorerOverrideAndSwitchedNetwork) + NL);
   2152                     out.writeUTF(NUM_ASSOCIATION_KEY + SEPARATOR +
   2153                             Integer.toString(config.numAssociation) + NL);
   2154                     out.writeUTF(JOIN_ATTEMPT_BOOST_KEY + SEPARATOR +
   2155                             Integer.toString(config.autoJoinUseAggressiveJoinAttemptThreshold)+ NL);
   2156                     //out.writeUTF(BLACKLIST_MILLI_KEY + SEPARATOR +
   2157                     // Long.toString(config.blackListTimestamp) + NL);
   2158                     out.writeUTF(CREATOR_UID_KEY + SEPARATOR +
   2159                             Integer.toString(config.creatorUid) + NL);
   2160                     out.writeUTF(CONNECT_UID_KEY + SEPARATOR +
   2161                             Integer.toString(config.lastConnectUid) + NL);
   2162                     out.writeUTF(UPDATE_UID_KEY + SEPARATOR +
   2163                             Integer.toString(config.lastUpdateUid) + NL);
   2164                     out.writeUTF(CREATOR_NAME_KEY + SEPARATOR +
   2165                             config.creatorName + NL);
   2166                     out.writeUTF(UPDATE_NAME_KEY + SEPARATOR +
   2167                             config.lastUpdateName + NL);
   2168                     out.writeUTF(USER_APPROVED_KEY + SEPARATOR +
   2169                             Integer.toString(config.userApproved) + NL);
   2170                     String allowedKeyManagementString =
   2171                             makeString(config.allowedKeyManagement,
   2172                                     WifiConfiguration.KeyMgmt.strings);
   2173                     out.writeUTF(AUTH_KEY + SEPARATOR +
   2174                             allowedKeyManagementString + NL);
   2175 
   2176                     if (config.connectChoices != null) {
   2177                         for (String key : config.connectChoices.keySet()) {
   2178                             Integer choice = config.connectChoices.get(key);
   2179                             out.writeUTF(CHOICE_KEY + SEPARATOR +
   2180                                     key + "=" + choice.toString() + NL);
   2181                         }
   2182                     }
   2183                     if (config.linkedConfigurations != null) {
   2184                         log("writeKnownNetworkHistory write linked "
   2185                                 + config.linkedConfigurations.size());
   2186 
   2187                         for (String key : config.linkedConfigurations.keySet()) {
   2188                             out.writeUTF(LINK_KEY + SEPARATOR + key + NL);
   2189                         }
   2190                     }
   2191 
   2192                     String macAddress = config.defaultGwMacAddress;
   2193                     if (macAddress != null) {
   2194                         out.writeUTF(DEFAULT_GW_KEY + SEPARATOR + macAddress + NL);
   2195                     }
   2196 
   2197                     if (getScanDetailCache(config) != null) {
   2198                         for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
   2199                             ScanResult result = scanDetail.getScanResult();
   2200                             out.writeUTF(BSSID_KEY + SEPARATOR +
   2201                                     result.BSSID + NL);
   2202 
   2203                             out.writeUTF(FREQ_KEY + SEPARATOR +
   2204                                     Integer.toString(result.frequency) + NL);
   2205 
   2206                             out.writeUTF(RSSI_KEY + SEPARATOR +
   2207                                     Integer.toString(result.level) + NL);
   2208 
   2209                             out.writeUTF(BSSID_STATUS_KEY + SEPARATOR +
   2210                                     Integer.toString(result.autoJoinStatus) + NL);
   2211 
   2212                             //if (result.seen != 0) {
   2213                             //    out.writeUTF(MILLI_KEY + SEPARATOR + Long.toString(result.seen)
   2214                             //            + NL);
   2215                             //}
   2216                             out.writeUTF(BSSID_KEY_END + NL);
   2217                         }
   2218                     }
   2219                     if (config.lastFailure != null) {
   2220                         out.writeUTF(FAILURE_KEY + SEPARATOR + config.lastFailure + NL);
   2221                     }
   2222                     out.writeUTF(NL);
   2223                     // Add extra blank lines for clarity
   2224                     out.writeUTF(NL);
   2225                     out.writeUTF(NL);
   2226                 }
   2227                 if (mDeletedSSIDs != null && mDeletedSSIDs.size() > 0) {
   2228                     for (Long i : mDeletedSSIDs) {
   2229                         out.writeUTF(DELETED_CRC32_KEY);
   2230                         out.writeUTF(String.valueOf(i));
   2231                         out.writeUTF(NL);
   2232                     }
   2233                 }
   2234                 if (mDeletedEphemeralSSIDs != null && mDeletedEphemeralSSIDs.size() > 0) {
   2235                     for (String ssid : mDeletedEphemeralSSIDs) {
   2236                         out.writeUTF(DELETED_EPHEMERAL_KEY);
   2237                         out.writeUTF(ssid);
   2238                         out.writeUTF(NL);
   2239                     }
   2240                 }
   2241             }
   2242         });
   2243     }
   2244 
   2245     public void setLastSelectedConfiguration(int netId) {
   2246         if (VDBG) {
   2247             loge("setLastSelectedConfiguration " + Integer.toString(netId));
   2248         }
   2249         if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
   2250             lastSelectedConfiguration = null;
   2251         } else {
   2252             WifiConfiguration selected = getWifiConfiguration(netId);
   2253             if (selected == null) {
   2254                 lastSelectedConfiguration = null;
   2255             } else {
   2256                 lastSelectedConfiguration = selected.configKey();
   2257                 selected.numConnectionFailures = 0;
   2258                 selected.numIpConfigFailures = 0;
   2259                 selected.numAuthFailures = 0;
   2260                 selected.numNoInternetAccessReports = 0;
   2261                 if (VDBG) {
   2262                     loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration);
   2263                 }
   2264             }
   2265         }
   2266     }
   2267 
   2268     public String getLastSelectedConfiguration() {
   2269         return lastSelectedConfiguration;
   2270     }
   2271 
   2272     public boolean isLastSelectedConfiguration(WifiConfiguration config) {
   2273         return (lastSelectedConfiguration != null
   2274                 && config != null
   2275                 && lastSelectedConfiguration.equals(config.configKey()));
   2276     }
   2277 
   2278     private void readNetworkHistory() {
   2279         if (showNetworks) {
   2280             localLog("readNetworkHistory() path:" + networkHistoryConfigFile);
   2281         }
   2282 
   2283         try (DataInputStream in =
   2284                      new DataInputStream(new BufferedInputStream(
   2285                              new FileInputStream(networkHistoryConfigFile)))) {
   2286 
   2287             String bssid = null;
   2288             String ssid = null;
   2289 
   2290             int freq = 0;
   2291             int status = 0;
   2292             long seen = 0;
   2293             int rssi = WifiConfiguration.INVALID_RSSI;
   2294             String caps = null;
   2295 
   2296             WifiConfiguration config = null;
   2297             while (true) {
   2298                 String line = in.readUTF();
   2299                 if (line == null) {
   2300                     break;
   2301                 }
   2302                 int colon = line.indexOf(':');
   2303                 if (colon < 0) {
   2304                     continue;
   2305                 }
   2306 
   2307                 String key = line.substring(0, colon).trim();
   2308                 String value = line.substring(colon + 1).trim();
   2309 
   2310                 if (key.equals(CONFIG_KEY)) {
   2311 
   2312                     config = mConfiguredNetworks.getByConfigKey(value);
   2313 
   2314                     // skip reading that configuration data
   2315                     // since we don't have a corresponding network ID
   2316                     if (config == null) {
   2317                         localLog("readNetworkHistory didnt find netid for hash="
   2318                                 + Integer.toString(value.hashCode())
   2319                                 + " key: " + value);
   2320                         mLostConfigsDbg.add(value);
   2321                         continue;
   2322                     } else {
   2323                         // After an upgrade count old connections as owned by system
   2324                         if (config.creatorName == null || config.lastUpdateName == null) {
   2325                             config.creatorName =
   2326                                 mContext.getPackageManager().getNameForUid(Process.SYSTEM_UID);
   2327                             config.lastUpdateName = config.creatorName;
   2328 
   2329                             if (DBG) Log.w(TAG, "Upgrading network " + config.networkId
   2330                                     + " to " + config.creatorName);
   2331                         }
   2332                     }
   2333                 } else if (config != null) {
   2334                     switch (key) {
   2335                         case SSID_KEY:
   2336                             if (config.isPasspoint()) {
   2337                                 break;
   2338                             }
   2339                             ssid = value;
   2340                             if (config.SSID != null && !config.SSID.equals(ssid)) {
   2341                                 loge("Error parsing network history file, mismatched SSIDs");
   2342                                 config = null; //error
   2343                                 ssid = null;
   2344                             } else {
   2345                                 config.SSID = ssid;
   2346                             }
   2347                             break;
   2348                         case FQDN_KEY:
   2349                             // Check for literal 'null' to be backwards compatible.
   2350                             config.FQDN = value.equals("null") ? null : value;
   2351                             break;
   2352                         case DEFAULT_GW_KEY:
   2353                             config.defaultGwMacAddress = value;
   2354                             break;
   2355                         case STATUS_KEY:
   2356                             config.autoJoinStatus = Integer.parseInt(value);
   2357                             break;
   2358                         case SUPPLICANT_DISABLE_REASON_KEY:
   2359                             config.disableReason = Integer.parseInt(value);
   2360                             break;
   2361                         case SELF_ADDED_KEY:
   2362                             config.selfAdded = Boolean.parseBoolean(value);
   2363                             break;
   2364                         case DID_SELF_ADD_KEY:
   2365                             config.didSelfAdd = Boolean.parseBoolean(value);
   2366                             break;
   2367                         case NO_INTERNET_ACCESS_REPORTS_KEY:
   2368                             config.numNoInternetAccessReports = Integer.parseInt(value);
   2369                             break;
   2370                         case VALIDATED_INTERNET_ACCESS_KEY:
   2371                             config.validatedInternetAccess = Boolean.parseBoolean(value);
   2372                             break;
   2373                         case CREATION_TIME_KEY:
   2374                             config.creationTime = value;
   2375                             break;
   2376                         case UPDATE_TIME_KEY:
   2377                             config.updateTime = value;
   2378                             break;
   2379                         case EPHEMERAL_KEY:
   2380                             config.ephemeral = Boolean.parseBoolean(value);
   2381                             break;
   2382                         case CREATOR_UID_KEY:
   2383                             config.creatorUid = Integer.parseInt(value);
   2384                             break;
   2385                         case BLACKLIST_MILLI_KEY:
   2386                             config.blackListTimestamp = Long.parseLong(value);
   2387                             break;
   2388                         case NUM_CONNECTION_FAILURES_KEY:
   2389                             config.numConnectionFailures = Integer.parseInt(value);
   2390                             break;
   2391                         case NUM_IP_CONFIG_FAILURES_KEY:
   2392                             config.numIpConfigFailures = Integer.parseInt(value);
   2393                             break;
   2394                         case NUM_AUTH_FAILURES_KEY:
   2395                             config.numIpConfigFailures = Integer.parseInt(value);
   2396                             break;
   2397                         case SCORER_OVERRIDE_KEY:
   2398                             config.numScorerOverride = Integer.parseInt(value);
   2399                             break;
   2400                         case SCORER_OVERRIDE_AND_SWITCH_KEY:
   2401                             config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(value);
   2402                             break;
   2403                         case NUM_ASSOCIATION_KEY:
   2404                             config.numAssociation = Integer.parseInt(value);
   2405                             break;
   2406                         case JOIN_ATTEMPT_BOOST_KEY:
   2407                             config.autoJoinUseAggressiveJoinAttemptThreshold =
   2408                                     Integer.parseInt(value);
   2409                             break;
   2410                         case CONNECT_UID_KEY:
   2411                             config.lastConnectUid = Integer.parseInt(value);
   2412                             break;
   2413                         case UPDATE_UID_KEY:
   2414                             config.lastUpdateUid = Integer.parseInt(value);
   2415                             break;
   2416                         case FAILURE_KEY:
   2417                             config.lastFailure = value;
   2418                             break;
   2419                         case PEER_CONFIGURATION_KEY:
   2420                             config.peerWifiConfiguration = value;
   2421                             break;
   2422                         case CHOICE_KEY:
   2423                             String configKey = "";
   2424                             int choice = 0;
   2425                             Matcher match = mConnectChoice.matcher(value);
   2426                             if (!match.find()) {
   2427                                 if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " +
   2428                                         " Couldnt match pattern : " + value);
   2429                             } else {
   2430                                 configKey = match.group(1);
   2431                                 try {
   2432                                     choice = Integer.parseInt(match.group(2));
   2433                                 } catch (NumberFormatException e) {
   2434                                     choice = 0;
   2435                                 }
   2436                                 if (choice > 0) {
   2437                                     if (config.connectChoices == null) {
   2438                                         config.connectChoices = new HashMap<>();
   2439                                     }
   2440                                     config.connectChoices.put(configKey, choice);
   2441                                 }
   2442                             }
   2443                             break;
   2444                         case LINK_KEY:
   2445                             if (config.linkedConfigurations == null) {
   2446                                 config.linkedConfigurations = new HashMap<>();
   2447                             }
   2448                             else {
   2449                                 config.linkedConfigurations.put(value, -1);
   2450                             }
   2451                             break;
   2452                         case BSSID_KEY:
   2453                             status = 0;
   2454                             ssid = null;
   2455                             bssid = null;
   2456                             freq = 0;
   2457                             seen = 0;
   2458                             rssi = WifiConfiguration.INVALID_RSSI;
   2459                             caps = "";
   2460                             break;
   2461                         case RSSI_KEY:
   2462                             rssi = Integer.parseInt(value);
   2463                             break;
   2464                         case BSSID_STATUS_KEY:
   2465                             status = Integer.parseInt(value);
   2466                             break;
   2467                         case FREQ_KEY:
   2468                             freq = Integer.parseInt(value);
   2469                             break;
   2470                         case DATE_KEY:
   2471                             /*
   2472                              * when reading the configuration from file we don't update the date
   2473                              * so as to avoid reading back stale or non-sensical data that would
   2474                              * depend on network time.
   2475                              * The date of a WifiConfiguration should only come from actual scan result.
   2476                              *
   2477                             String s = key.replace(FREQ_KEY, "");
   2478                             seen = Integer.getInteger(s);
   2479                             */
   2480                             break;
   2481                         case BSSID_KEY_END:
   2482                             if ((bssid != null) && (ssid != null)) {
   2483 
   2484                                 if (getScanDetailCache(config) != null) {
   2485                                     WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
   2486                                     ScanDetail scanDetail = new ScanDetail(wssid, bssid,
   2487                                             caps, rssi, freq, (long) 0, seen);
   2488                                     getScanDetailCache(config).put(scanDetail);
   2489                                     scanDetail.getScanResult().autoJoinStatus = status;
   2490                                 }
   2491                             }
   2492                             break;
   2493                         case DELETED_CRC32_KEY:
   2494                             mDeletedSSIDs.add(Long.parseLong(value));
   2495                             break;
   2496                         case DELETED_EPHEMERAL_KEY:
   2497                             if (!TextUtils.isEmpty(value)) {
   2498                                 mDeletedEphemeralSSIDs.add(value);
   2499                             }
   2500                             break;
   2501                         case CREATOR_NAME_KEY:
   2502                             config.creatorName = value;
   2503                             break;
   2504                         case UPDATE_NAME_KEY:
   2505                             config.lastUpdateName = value;
   2506                             break;
   2507                         case USER_APPROVED_KEY:
   2508                             config.userApproved = Integer.parseInt(value);
   2509                             break;
   2510                     }
   2511                 }
   2512             }
   2513         } catch (NumberFormatException e) {
   2514             Log.e(TAG, "readNetworkHistory: failed to read, revert to default, " + e, e);
   2515         } catch (EOFException e) {
   2516             // do nothing
   2517         } catch (IOException e) {
   2518             Log.e(TAG, "readNetworkHistory: No config file, revert to default, " + e, e);
   2519         }
   2520     }
   2521 
   2522     private void readAutoJoinConfig() {
   2523         try (BufferedReader reader = new BufferedReader(new FileReader(autoJoinConfigFile))) {
   2524             for (String key = reader.readLine(); key != null; key = reader.readLine()) {
   2525                 Log.d(TAG, "readAutoJoinConfig line: " + key);
   2526 
   2527                 int split = key.indexOf(':');
   2528                 if (split < 0) {
   2529                     continue;
   2530                 }
   2531 
   2532                 String name = key.substring(0, split);
   2533                 Object reference = sKeyMap.get(name);
   2534                 if (reference == null) {
   2535                     continue;
   2536                 }
   2537 
   2538                 try {
   2539                     int value = Integer.parseInt(key.substring(split+1).trim());
   2540                     if (reference.getClass() == AtomicBoolean.class) {
   2541                         ((AtomicBoolean)reference).set(value != 0);
   2542                     }
   2543                     else {
   2544                         ((AtomicInteger)reference).set(value);
   2545                     }
   2546                     Log.d(TAG,"readAutoJoinConfig: " + name + " = " + value);
   2547                 }
   2548                 catch (NumberFormatException nfe) {
   2549                     Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
   2550                 }
   2551             }
   2552         } catch (IOException e) {
   2553             loge("readAutoJoinStatus: Error parsing configuration" + e);
   2554         }
   2555     }
   2556 
   2557 
   2558     private void writeIpAndProxyConfigurations() {
   2559         final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
   2560         for(WifiConfiguration config : mConfiguredNetworks.values()) {
   2561             if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) {
   2562                 networks.put(configKey(config), config.getIpConfiguration());
   2563             }
   2564         }
   2565 
   2566         super.writeIpAndProxyConfigurations(ipConfigFile, networks);
   2567     }
   2568 
   2569     private void readIpAndProxyConfigurations() {
   2570         SparseArray<IpConfiguration> networks = super.readIpAndProxyConfigurations(ipConfigFile);
   2571 
   2572         if (networks == null || networks.size() == 0) {
   2573             // IpConfigStore.readIpAndProxyConfigurations has already logged an error.
   2574             return;
   2575         }
   2576 
   2577         for (int i = 0; i < networks.size(); i++) {
   2578             int id = networks.keyAt(i);
   2579             WifiConfiguration config = mConfiguredNetworks.getByConfigKeyID(id);
   2580             // This is the only place the map is looked up through a (dangerous) hash-value!
   2581 
   2582             if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED ||
   2583                     config.ephemeral) {
   2584                 loge("configuration found for missing network, nid=" + id
   2585                         +", ignored, networks.size=" + Integer.toString(networks.size()));
   2586             } else {
   2587                 config.setIpConfiguration(networks.valueAt(i));
   2588             }
   2589         }
   2590     }
   2591 
   2592     /*
   2593      * Convert string to Hexadecimal before passing to wifi native layer
   2594      * In native function "doCommand()" have trouble in converting Unicode character string to UTF8
   2595      * conversion to hex is required because SSIDs can have space characters in them;
   2596      * and that can confuses the supplicant because it uses space charaters as delimiters
   2597      */
   2598 
   2599     public static String encodeSSID(String str){
   2600         return Utils.toHex(removeDoubleQuotes(str).getBytes(StandardCharsets.UTF_8));
   2601     }
   2602 
   2603     private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config, int uid) {
   2604         /*
   2605          * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
   2606          * network configuration. Otherwise, the networkId should
   2607          * refer to an existing configuration.
   2608          */
   2609 
   2610         if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
   2611         if (config.isPasspoint() && !mMOManager.isEnabled()) {
   2612             Log.e(TAG, "Passpoint is not enabled");
   2613             return new NetworkUpdateResult(INVALID_NETWORK_ID);
   2614         }
   2615 
   2616         int netId = config.networkId;
   2617         boolean newNetwork = false;
   2618         // networkId of INVALID_NETWORK_ID means we want to create a new network
   2619         if (netId == INVALID_NETWORK_ID) {
   2620             WifiConfiguration savedConfig = mConfiguredNetworks.getByConfigKey(config.configKey());
   2621             if (savedConfig != null) {
   2622                 netId = savedConfig.networkId;
   2623             } else {
   2624                 if (mMOManager.getHomeSP(config.FQDN) != null) {
   2625                     loge("addOrUpdateNetworkNative passpoint " + config.FQDN
   2626                             + " was found, but no network Id");
   2627                 }
   2628                 newNetwork = true;
   2629                 netId = mWifiNative.addNetwork();
   2630                 if (netId < 0) {
   2631                     loge("Failed to add a network!");
   2632                     return new NetworkUpdateResult(INVALID_NETWORK_ID);
   2633                 } else {
   2634                     loge("addOrUpdateNetworkNative created netId=" + netId);
   2635                 }
   2636             }
   2637         }
   2638 
   2639         boolean updateFailed = true;
   2640 
   2641         setVariables: {
   2642 
   2643             if (config.SSID != null &&
   2644                     !mWifiNative.setNetworkVariable(
   2645                         netId,
   2646                         WifiConfiguration.ssidVarName,
   2647                         encodeSSID(config.SSID))) {
   2648                 loge("failed to set SSID: "+config.SSID);
   2649                 break setVariables;
   2650             }
   2651 
   2652             if (config.isPasspoint()) {
   2653                 if (!mWifiNative.setNetworkVariable(
   2654                             netId,
   2655                             idStringVarName,
   2656                             '"' + config.FQDN + '"')) {
   2657                     loge("failed to set id_str: " + config.FQDN);
   2658                     break setVariables;
   2659                 }
   2660             }
   2661 
   2662             if (config.BSSID != null) {
   2663                 log("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
   2664                 if (!mWifiNative.setNetworkVariable(
   2665                         netId,
   2666                         WifiConfiguration.bssidVarName,
   2667                         config.BSSID)) {
   2668                     loge("failed to set BSSID: " + config.BSSID);
   2669                     break setVariables;
   2670                 }
   2671             }
   2672 
   2673             String allowedKeyManagementString =
   2674                 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
   2675             if (config.allowedKeyManagement.cardinality() != 0 &&
   2676                     !mWifiNative.setNetworkVariable(
   2677                         netId,
   2678                         WifiConfiguration.KeyMgmt.varName,
   2679                         allowedKeyManagementString)) {
   2680                 loge("failed to set key_mgmt: "+
   2681                         allowedKeyManagementString);
   2682                 break setVariables;
   2683             }
   2684 
   2685             String allowedProtocolsString =
   2686                 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
   2687             if (config.allowedProtocols.cardinality() != 0 &&
   2688                     !mWifiNative.setNetworkVariable(
   2689                         netId,
   2690                         WifiConfiguration.Protocol.varName,
   2691                         allowedProtocolsString)) {
   2692                 loge("failed to set proto: "+
   2693                         allowedProtocolsString);
   2694                 break setVariables;
   2695             }
   2696 
   2697             String allowedAuthAlgorithmsString =
   2698                 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
   2699             if (config.allowedAuthAlgorithms.cardinality() != 0 &&
   2700                     !mWifiNative.setNetworkVariable(
   2701                         netId,
   2702                         WifiConfiguration.AuthAlgorithm.varName,
   2703                         allowedAuthAlgorithmsString)) {
   2704                 loge("failed to set auth_alg: "+
   2705                         allowedAuthAlgorithmsString);
   2706                 break setVariables;
   2707             }
   2708 
   2709             String allowedPairwiseCiphersString =
   2710                     makeString(config.allowedPairwiseCiphers,
   2711                     WifiConfiguration.PairwiseCipher.strings);
   2712             if (config.allowedPairwiseCiphers.cardinality() != 0 &&
   2713                     !mWifiNative.setNetworkVariable(
   2714                         netId,
   2715                         WifiConfiguration.PairwiseCipher.varName,
   2716                         allowedPairwiseCiphersString)) {
   2717                 loge("failed to set pairwise: "+
   2718                         allowedPairwiseCiphersString);
   2719                 break setVariables;
   2720             }
   2721 
   2722             String allowedGroupCiphersString =
   2723                 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
   2724             if (config.allowedGroupCiphers.cardinality() != 0 &&
   2725                     !mWifiNative.setNetworkVariable(
   2726                         netId,
   2727                         WifiConfiguration.GroupCipher.varName,
   2728                         allowedGroupCiphersString)) {
   2729                 loge("failed to set group: "+
   2730                         allowedGroupCiphersString);
   2731                 break setVariables;
   2732             }
   2733 
   2734             // Prevent client screw-up by passing in a WifiConfiguration we gave it
   2735             // by preventing "*" as a key.
   2736             if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
   2737                     !mWifiNative.setNetworkVariable(
   2738                         netId,
   2739                         WifiConfiguration.pskVarName,
   2740                         config.preSharedKey)) {
   2741                 loge("failed to set psk");
   2742                 break setVariables;
   2743             }
   2744 
   2745             boolean hasSetKey = false;
   2746             if (config.wepKeys != null) {
   2747                 for (int i = 0; i < config.wepKeys.length; i++) {
   2748                     // Prevent client screw-up by passing in a WifiConfiguration we gave it
   2749                     // by preventing "*" as a key.
   2750                     if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
   2751                         if (!mWifiNative.setNetworkVariable(
   2752                                     netId,
   2753                                     WifiConfiguration.wepKeyVarNames[i],
   2754                                     config.wepKeys[i])) {
   2755                             loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
   2756                             break setVariables;
   2757                         }
   2758                         hasSetKey = true;
   2759                     }
   2760                 }
   2761             }
   2762 
   2763             if (hasSetKey) {
   2764                 if (!mWifiNative.setNetworkVariable(
   2765                             netId,
   2766                             WifiConfiguration.wepTxKeyIdxVarName,
   2767                             Integer.toString(config.wepTxKeyIndex))) {
   2768                     loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
   2769                     break setVariables;
   2770                 }
   2771             }
   2772 
   2773             if (!mWifiNative.setNetworkVariable(
   2774                         netId,
   2775                         WifiConfiguration.priorityVarName,
   2776                         Integer.toString(config.priority))) {
   2777                 loge(config.SSID + ": failed to set priority: "
   2778                         +config.priority);
   2779                 break setVariables;
   2780             }
   2781 
   2782             if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
   2783                         netId,
   2784                         WifiConfiguration.hiddenSSIDVarName,
   2785                         Integer.toString(config.hiddenSSID ? 1 : 0))) {
   2786                 loge(config.SSID + ": failed to set hiddenSSID: "+
   2787                         config.hiddenSSID);
   2788                 break setVariables;
   2789             }
   2790 
   2791             if (config.requirePMF && !mWifiNative.setNetworkVariable(
   2792                         netId,
   2793                         WifiConfiguration.pmfVarName,
   2794                         "2")) {
   2795                 loge(config.SSID + ": failed to set requirePMF: "+
   2796                         config.requirePMF);
   2797                 break setVariables;
   2798             }
   2799 
   2800             if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable(
   2801                     netId,
   2802                     WifiConfiguration.updateIdentiferVarName,
   2803                     config.updateIdentifier)) {
   2804                 loge(config.SSID + ": failed to set updateIdentifier: "+
   2805                         config.updateIdentifier);
   2806                 break setVariables;
   2807             }
   2808 
   2809             if (config.enterpriseConfig != null &&
   2810                     config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
   2811 
   2812                 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
   2813 
   2814                 if (needsKeyStore(enterpriseConfig)) {
   2815                     /**
   2816                      * Keyguard settings may eventually be controlled by device policy.
   2817                      * We check here if keystore is unlocked before installing
   2818                      * credentials.
   2819                      * TODO: Do we need a dialog here ?
   2820                      */
   2821                     if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
   2822                         loge(config.SSID + ": key store is locked");
   2823                         break setVariables;
   2824                     }
   2825 
   2826                     try {
   2827                         /* config passed may include only fields being updated.
   2828                          * In order to generate the key id, fetch uninitialized
   2829                          * fields from the currently tracked configuration
   2830                          */
   2831                         WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
   2832                         String keyId = config.getKeyIdForCredentials(currentConfig);
   2833 
   2834                         if (!installKeys(enterpriseConfig, keyId)) {
   2835                             loge(config.SSID + ": failed to install keys");
   2836                             break setVariables;
   2837                         }
   2838                     } catch (IllegalStateException e) {
   2839                         loge(config.SSID + " invalid config for key installation");
   2840                         break setVariables;
   2841                     }
   2842                 }
   2843 
   2844                 HashMap<String, String> enterpriseFields = enterpriseConfig.getFields();
   2845                 for (String key : enterpriseFields.keySet()) {
   2846                         String value = enterpriseFields.get(key);
   2847                         if (key.equals("password") && value != null && value.equals("*")) {
   2848                             // No need to try to set an obfuscated password, which will fail
   2849                             continue;
   2850                         }
   2851                         if (key.equals(WifiEnterpriseConfig.REALM_KEY)
   2852                                 || key.equals(WifiEnterpriseConfig.PLMN_KEY)) {
   2853                             // No need to save realm or PLMN in supplicant
   2854                             continue;
   2855                         }
   2856                         if (!mWifiNative.setNetworkVariable(
   2857                                     netId,
   2858                                     key,
   2859                                     value)) {
   2860                             removeKeys(enterpriseConfig);
   2861                             loge(config.SSID + ": failed to set " + key +
   2862                                     ": " + value);
   2863                             break setVariables;
   2864                         }
   2865                 }
   2866             }
   2867             updateFailed = false;
   2868         } // End of setVariables
   2869 
   2870         if (updateFailed) {
   2871             if (newNetwork) {
   2872                 mWifiNative.removeNetwork(netId);
   2873                 loge("Failed to set a network variable, removed network: " + netId);
   2874             }
   2875             return new NetworkUpdateResult(INVALID_NETWORK_ID);
   2876         }
   2877 
   2878         /* An update of the network variables requires reading them
   2879          * back from the supplicant to update mConfiguredNetworks.
   2880          * This is because some of the variables (SSID, wep keys &
   2881          * passphrases) reflect different values when read back than
   2882          * when written. For example, wep key is stored as * irrespective
   2883          * of the value sent to the supplicant
   2884          */
   2885         WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
   2886         if (currentConfig == null) {
   2887             currentConfig = new WifiConfiguration();
   2888             currentConfig.setIpAssignment(IpAssignment.DHCP);
   2889             currentConfig.setProxySettings(ProxySettings.NONE);
   2890             currentConfig.networkId = netId;
   2891             if (config != null) {
   2892                 // Carry over the creation parameters
   2893                 currentConfig.selfAdded = config.selfAdded;
   2894                 currentConfig.didSelfAdd = config.didSelfAdd;
   2895                 currentConfig.ephemeral = config.ephemeral;
   2896                 currentConfig.autoJoinUseAggressiveJoinAttemptThreshold
   2897                         = config.autoJoinUseAggressiveJoinAttemptThreshold;
   2898                 currentConfig.lastConnectUid = config.lastConnectUid;
   2899                 currentConfig.lastUpdateUid = config.lastUpdateUid;
   2900                 currentConfig.creatorUid = config.creatorUid;
   2901                 currentConfig.creatorName = config.creatorName;
   2902                 currentConfig.lastUpdateName = config.lastUpdateName;
   2903                 currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
   2904                 currentConfig.FQDN = config.FQDN;
   2905                 currentConfig.providerFriendlyName = config.providerFriendlyName;
   2906                 currentConfig.roamingConsortiumIds = config.roamingConsortiumIds;
   2907                 currentConfig.validatedInternetAccess = config.validatedInternetAccess;
   2908                 currentConfig.numNoInternetAccessReports = config.numNoInternetAccessReports;
   2909                 currentConfig.updateTime = config.updateTime;
   2910                 currentConfig.creationTime = config.creationTime;
   2911             }
   2912             if (DBG) {
   2913                 log("created new config netId=" + Integer.toString(netId)
   2914                         + " uid=" + Integer.toString(currentConfig.creatorUid)
   2915                         + " name=" + currentConfig.creatorName);
   2916             }
   2917         }
   2918 
   2919         /* save HomeSP object for passpoint networks */
   2920         HomeSP homeSP = null;
   2921 
   2922         if (config.isPasspoint()) {
   2923             try {
   2924                 Credential credential =
   2925                         new Credential(config.enterpriseConfig, mKeyStore, !newNetwork);
   2926                 HashSet<Long> roamingConsortiumIds = new HashSet<Long>();
   2927                 for (Long roamingConsortiumId : config.roamingConsortiumIds) {
   2928                     roamingConsortiumIds.add(roamingConsortiumId);
   2929                 }
   2930 
   2931                 homeSP = new HomeSP(Collections.<String, Long>emptyMap(), config.FQDN,
   2932                         roamingConsortiumIds, Collections.<String>emptySet(),
   2933                         Collections.<Long>emptySet(), Collections.<Long>emptyList(),
   2934                         config.providerFriendlyName, null, credential);
   2935 
   2936                 log("created a homeSP object for " + config.networkId + ":" + config.SSID);
   2937 
   2938                 /* fix enterprise config properties for passpoint */
   2939                 currentConfig.enterpriseConfig.setRealm(config.enterpriseConfig.getRealm());
   2940                 currentConfig.enterpriseConfig.setPlmn(config.enterpriseConfig.getPlmn());
   2941             }
   2942             catch (IOException ioe) {
   2943                 Log.e(TAG, "Failed to create Passpoint config: " + ioe);
   2944                 return new NetworkUpdateResult(INVALID_NETWORK_ID);
   2945             }
   2946         }
   2947 
   2948         if (uid != WifiConfiguration.UNKNOWN_UID) {
   2949             if (newNetwork) {
   2950                 currentConfig.creatorUid = uid;
   2951             } else {
   2952                 currentConfig.lastUpdateUid = uid;
   2953             }
   2954         }
   2955 
   2956         // For debug, record the time the configuration was modified
   2957         StringBuilder sb = new StringBuilder();
   2958         sb.append("time=");
   2959         Calendar c = Calendar.getInstance();
   2960         c.setTimeInMillis(System.currentTimeMillis());
   2961         sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
   2962 
   2963         if (newNetwork) {
   2964             currentConfig.dirty = true;
   2965             currentConfig.creationTime = sb.toString();
   2966         } else {
   2967             currentConfig.updateTime = sb.toString();
   2968         }
   2969 
   2970         if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
   2971             // Make sure the configuration is not deleted anymore since we just
   2972             // added or modified it.
   2973             currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
   2974             currentConfig.selfAdded = false;
   2975             currentConfig.didSelfAdd = false;
   2976             if (DBG) {
   2977                 log("remove deleted status netId=" + Integer.toString(netId)
   2978                         + " " + currentConfig.configKey());
   2979             }
   2980         }
   2981 
   2982         if (currentConfig.status == WifiConfiguration.Status.ENABLED) {
   2983             // Make sure autojoin remain in sync with user modifying the configuration
   2984             currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
   2985         }
   2986 
   2987         if (currentConfig.configKey().equals(getLastSelectedConfiguration()) &&
   2988                 currentConfig.ephemeral) {
   2989             // Make the config non-ephemeral since the user just explicitly clicked it.
   2990             currentConfig.ephemeral = false;
   2991             if (DBG) log("remove ephemeral status netId=" + Integer.toString(netId)
   2992                     + " " + currentConfig.configKey());
   2993         }
   2994 
   2995         if (VDBG) log("will read network variables netId=" + Integer.toString(netId));
   2996 
   2997         readNetworkVariables(currentConfig);
   2998 
   2999         // Persist configuration paramaters that are not saved by supplicant.
   3000         if (config.lastUpdateName != null) {
   3001             currentConfig.lastUpdateName = config.lastUpdateName;
   3002         }
   3003         if (config.lastUpdateUid != WifiConfiguration.UNKNOWN_UID) {
   3004             currentConfig.lastUpdateUid = config.lastUpdateUid;
   3005         }
   3006 
   3007         mConfiguredNetworks.put(netId, currentConfig);
   3008 
   3009         NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
   3010         result.setIsNewNetwork(newNetwork);
   3011         result.setNetworkId(netId);
   3012 
   3013         if (homeSP != null) {
   3014             writePasspointConfigs(null, homeSP);
   3015         }
   3016         writeKnownNetworkHistory(false);
   3017 
   3018         return result;
   3019     }
   3020 
   3021     public WifiConfiguration getWifiConfigForHomeSP(HomeSP homeSP) {
   3022         WifiConfiguration config = mConfiguredNetworks.getByFQDN(homeSP.getFQDN());
   3023         if (config == null) {
   3024             Log.e(TAG, "Could not find network for homeSP " + homeSP.getFQDN());
   3025         }
   3026         return config;
   3027     }
   3028 
   3029     private HomeSP getHomeSPForConfig(WifiConfiguration config) {
   3030         WifiConfiguration storedConfig = mConfiguredNetworks.get(config.networkId);
   3031         return storedConfig != null && storedConfig.isPasspoint() ?
   3032                 mMOManager.getHomeSP(storedConfig.FQDN) : null;
   3033     }
   3034 
   3035     public ScanDetailCache getScanDetailCache(WifiConfiguration config) {
   3036         if (config == null) return null;
   3037         ScanDetailCache cache = mScanDetailCaches.get(config.networkId);
   3038         if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
   3039             cache = new ScanDetailCache(config);
   3040             mScanDetailCaches.put(config.networkId, cache);
   3041         }
   3042         return cache;
   3043     }
   3044 
   3045     /**
   3046      * This function run thru the Saved WifiConfigurations and check if some should be linked.
   3047      * @param config
   3048      */
   3049     public void linkConfiguration(WifiConfiguration config) {
   3050 
   3051         if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 6) {
   3052             // Ignore configurations with large number of BSSIDs
   3053             return;
   3054         }
   3055         if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
   3056             // Only link WPA_PSK config
   3057             return;
   3058         }
   3059         for (WifiConfiguration link : mConfiguredNetworks.values()) {
   3060             boolean doLink = false;
   3061 
   3062             if (link.configKey().equals(config.configKey())) {
   3063                 continue;
   3064             }
   3065 
   3066             if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.ephemeral) {
   3067                 continue;
   3068             }
   3069 
   3070             // Autojoin will be allowed to dynamically jump from a linked configuration
   3071             // to another, hence only link configurations that have equivalent level of security
   3072             if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) {
   3073                 continue;
   3074             }
   3075 
   3076             ScanDetailCache linkedScanDetailCache = getScanDetailCache(link);
   3077             if (linkedScanDetailCache != null && linkedScanDetailCache.size() > 6) {
   3078                 // Ignore configurations with large number of BSSIDs
   3079                 continue;
   3080             }
   3081 
   3082             if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) {
   3083                 // If both default GW are known, link only if they are equal
   3084                 if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) {
   3085                     if (VDBG) {
   3086                         loge("linkConfiguration link due to same gw " + link.SSID +
   3087                                 " and " + config.SSID + " GW " + config.defaultGwMacAddress);
   3088                     }
   3089                     doLink = true;
   3090                 }
   3091             } else {
   3092                 // We do not know BOTH default gateways hence we will try to link
   3093                 // hoping that WifiConfigurations are indeed behind the same gateway.
   3094                 // once both WifiConfiguration have been tried and thus once both efault gateways
   3095                 // are known we will revisit the choice of linking them
   3096                 if ((getScanDetailCache(config) != null)
   3097                         && (getScanDetailCache(config).size() <= 6)) {
   3098 
   3099                     for (String abssid : getScanDetailCache(config).keySet()) {
   3100                         for (String bbssid : linkedScanDetailCache.keySet()) {
   3101                             if (VVDBG) {
   3102                                 loge("linkConfiguration try to link due to DBDC BSSID match "
   3103                                         + link.SSID +
   3104                                         " and " + config.SSID + " bssida " + abssid
   3105                                         + " bssidb " + bbssid);
   3106                             }
   3107                             if (abssid.regionMatches(true, 0, bbssid, 0, 16)) {
   3108                                 // If first 16 ascii characters of BSSID matches,
   3109                                 // we assume this is a DBDC
   3110                                 doLink = true;
   3111                             }
   3112                         }
   3113                     }
   3114                 }
   3115             }
   3116 
   3117             if (doLink == true && onlyLinkSameCredentialConfigurations) {
   3118                 String apsk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
   3119                 String bpsk = readNetworkVariableFromSupplicantFile(config.SSID, "psk");
   3120                 if (apsk == null || bpsk == null
   3121                         || TextUtils.isEmpty(apsk) || TextUtils.isEmpty(apsk)
   3122                         || apsk.equals("*") || apsk.equals(DELETED_CONFIG_PSK)
   3123                         || !apsk.equals(bpsk)) {
   3124                     doLink = false;
   3125                 }
   3126             }
   3127 
   3128             if (doLink) {
   3129                 if (VDBG) {
   3130                     loge("linkConfiguration: will link " + link.configKey()
   3131                             + " and " + config.configKey());
   3132                 }
   3133                 if (link.linkedConfigurations == null) {
   3134                     link.linkedConfigurations = new HashMap<String, Integer>();
   3135                 }
   3136                 if (config.linkedConfigurations == null) {
   3137                     config.linkedConfigurations = new HashMap<String, Integer>();
   3138                 }
   3139                 if (link.linkedConfigurations.get(config.configKey()) == null) {
   3140                     link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
   3141                     link.dirty = true;
   3142                 }
   3143                 if (config.linkedConfigurations.get(link.configKey()) == null) {
   3144                     config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
   3145                     config.dirty = true;
   3146                 }
   3147             } else {
   3148                 if (link.linkedConfigurations != null
   3149                         && (link.linkedConfigurations.get(config.configKey()) != null)) {
   3150                     if (VDBG) {
   3151                         loge("linkConfiguration: un-link " + config.configKey()
   3152                                 + " from " + link.configKey());
   3153                     }
   3154                     link.dirty = true;
   3155                     link.linkedConfigurations.remove(config.configKey());
   3156                 }
   3157                 if (config.linkedConfigurations != null
   3158                         && (config.linkedConfigurations.get(link.configKey()) != null)) {
   3159                     if (VDBG) {
   3160                         loge("linkConfiguration: un-link " + link.configKey()
   3161                                 + " from " + config.configKey());
   3162                     }
   3163                     config.dirty = true;
   3164                     config.linkedConfigurations.remove(link.configKey());
   3165                 }
   3166             }
   3167         }
   3168     }
   3169 
   3170     public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) {
   3171         if (config == null)
   3172             return null;
   3173         long now_ms = System.currentTimeMillis();
   3174 
   3175         HashSet<Integer> channels = new HashSet<Integer>();
   3176 
   3177         //get channels for this configuration, if there are at least 2 BSSIDs
   3178         if (getScanDetailCache(config) == null && config.linkedConfigurations == null) {
   3179             return null;
   3180         }
   3181 
   3182         if (VDBG) {
   3183             StringBuilder dbg = new StringBuilder();
   3184             dbg.append("makeChannelList age=" + Integer.toString(age)
   3185                     + " for " + config.configKey()
   3186                     + " max=" + maxNumActiveChannelsForPartialScans);
   3187             if (getScanDetailCache(config) != null) {
   3188                 dbg.append(" bssids=" + getScanDetailCache(config).size());
   3189             }
   3190             if (config.linkedConfigurations != null) {
   3191                 dbg.append(" linked=" + config.linkedConfigurations.size());
   3192             }
   3193             loge(dbg.toString());
   3194         }
   3195 
   3196         int numChannels = 0;
   3197         if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 0) {
   3198             for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
   3199                 ScanResult result = scanDetail.getScanResult();
   3200                 //TODO : cout active and passive channels separately
   3201                 if (numChannels > maxNumActiveChannelsForPartialScans.get()) {
   3202                     break;
   3203                 }
   3204                 if (VDBG) {
   3205                     boolean test = (now_ms - result.seen) < age;
   3206                     loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
   3207                             + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
   3208                 }
   3209                 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
   3210                     channels.add(result.frequency);
   3211                     numChannels++;
   3212                 }
   3213             }
   3214         }
   3215 
   3216         //get channels for linked configurations
   3217         if (config.linkedConfigurations != null) {
   3218             for (String key : config.linkedConfigurations.keySet()) {
   3219                 WifiConfiguration linked = getWifiConfiguration(key);
   3220                 if (linked == null)
   3221                     continue;
   3222                 if (getScanDetailCache(linked) == null) {
   3223                     continue;
   3224                 }
   3225                 for (ScanDetail scanDetail : getScanDetailCache(linked).values()) {
   3226                     ScanResult result = scanDetail.getScanResult();
   3227                     if (VDBG) {
   3228                         loge("has link: " + result.BSSID
   3229                                 + " freq=" + Integer.toString(result.frequency)
   3230                                 + " age=" + Long.toString(now_ms - result.seen));
   3231                     }
   3232                     if (numChannels > maxNumActiveChannelsForPartialScans.get()) {
   3233                         break;
   3234                     }
   3235                     if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
   3236                         channels.add(result.frequency);
   3237                         numChannels++;
   3238                     }
   3239                 }
   3240             }
   3241         }
   3242         return channels;
   3243     }
   3244 
   3245     private Map<HomeSP, PasspointMatch> matchPasspointNetworks(ScanDetail scanDetail) {
   3246         if (!mMOManager.isConfigured()) {
   3247             return null;
   3248         }
   3249         NetworkDetail networkDetail = scanDetail.getNetworkDetail();
   3250         if (!networkDetail.hasInterworking()) {
   3251             return null;
   3252         }
   3253         updateAnqpCache(scanDetail, networkDetail.getANQPElements());
   3254 
   3255         Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, true);
   3256         Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() +
   3257                 " pass 1 matches: " + toMatchString(matches));
   3258         return matches;
   3259     }
   3260 
   3261     private Map<HomeSP, PasspointMatch> matchNetwork(ScanDetail scanDetail, boolean query) {
   3262         NetworkDetail networkDetail = scanDetail.getNetworkDetail();
   3263 
   3264         ANQPData anqpData = mAnqpCache.getEntry(networkDetail);
   3265 
   3266         Map<Constants.ANQPElementType, ANQPElement> anqpElements =
   3267                 anqpData != null ? anqpData.getANQPElements() : null;
   3268 
   3269         boolean queried = !query;
   3270         Collection<HomeSP> homeSPs = mMOManager.getLoadedSPs().values();
   3271         Map<HomeSP, PasspointMatch> matches = new HashMap<>(homeSPs.size());
   3272         Log.d(Utils.hs2LogTag(getClass()), "match nwk " + scanDetail.toKeyString() +
   3273                 ", anqp " + ( anqpData != null ? "present" : "missing" ) +
   3274                 ", query " + query + ", home sps: " + homeSPs.size());
   3275 
   3276         for (HomeSP homeSP : homeSPs) {
   3277             PasspointMatch match = homeSP.match(networkDetail, anqpElements, mSIMAccessor);
   3278 
   3279             Log.d(Utils.hs2LogTag(getClass()), " -- " +
   3280                     homeSP.getFQDN() + ": match " + match + ", queried " + queried);
   3281 
   3282             if (match == PasspointMatch.Incomplete && !queried) {
   3283                 if (mAnqpCache.initiate(networkDetail)) {
   3284                     mSupplicantBridge.startANQP(scanDetail);
   3285                 }
   3286                 queried = true;
   3287             }
   3288             matches.put(homeSP, match);
   3289         }
   3290         return matches;
   3291     }
   3292 
   3293     public void notifyANQPDone(Long bssid, boolean success) {
   3294         mSupplicantBridge.notifyANQPDone(bssid, success);
   3295     }
   3296 
   3297     public void notifyANQPResponse(ScanDetail scanDetail,
   3298                                    Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
   3299 
   3300         updateAnqpCache(scanDetail, anqpElements);
   3301         if (anqpElements == null || anqpElements.isEmpty()) {
   3302             return;
   3303         }
   3304         scanDetail.propagateANQPInfo(anqpElements);
   3305 
   3306         Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, false);
   3307         Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() +
   3308                 " pass 2 matches: " + toMatchString(matches));
   3309 
   3310         cacheScanResultForPasspointConfigs(scanDetail, matches);
   3311     }
   3312 
   3313 
   3314     private void updateAnqpCache(ScanDetail scanDetail,
   3315                                  Map<Constants.ANQPElementType,ANQPElement> anqpElements)
   3316     {
   3317         NetworkDetail networkDetail = scanDetail.getNetworkDetail();
   3318 
   3319         if (anqpElements == null) {
   3320             // Try to pull cached data if query failed.
   3321             ANQPData data = mAnqpCache.getEntry(networkDetail);
   3322             if (data != null) {
   3323                 scanDetail.propagateANQPInfo(data.getANQPElements());
   3324             }
   3325             return;
   3326         }
   3327 
   3328         mAnqpCache.update(networkDetail, anqpElements);
   3329     }
   3330 
   3331     private static String toMatchString(Map<HomeSP, PasspointMatch> matches) {
   3332         StringBuilder sb = new StringBuilder();
   3333         for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
   3334             sb.append(' ').append(entry.getKey().getFQDN()).append("->").append(entry.getValue());
   3335         }
   3336         return sb.toString();
   3337     }
   3338 
   3339     private void cacheScanResultForPasspointConfigs(ScanDetail scanDetail,
   3340                                            Map<HomeSP,PasspointMatch> matches) {
   3341 
   3342         for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
   3343             PasspointMatch match = entry.getValue();
   3344             if (match == PasspointMatch.HomeProvider || match == PasspointMatch.RoamingProvider) {
   3345                 WifiConfiguration config = getWifiConfigForHomeSP(entry.getKey());
   3346                 if (config != null) {
   3347                     cacheScanResultForConfig(config, scanDetail, entry.getValue());
   3348                 } else {
   3349 		            Log.w(Utils.hs2LogTag(getClass()), "Failed to find config for '" +
   3350                             entry.getKey().getFQDN() + "'");
   3351                     /* perhaps the configuration was deleted?? */
   3352                 }
   3353             }
   3354         }
   3355     }
   3356 
   3357     private void cacheScanResultForConfig(
   3358             WifiConfiguration config, ScanDetail scanDetail, PasspointMatch passpointMatch) {
   3359 
   3360         ScanResult scanResult = scanDetail.getScanResult();
   3361 
   3362         if (config.autoJoinStatus >= WifiConfiguration.AUTO_JOIN_DELETED) {
   3363             if (VVDBG) {
   3364                 loge("updateSavedNetworkHistory(): found a deleted, skip it...  "
   3365                         + config.configKey());
   3366             }
   3367             // The scan result belongs to a deleted config:
   3368             //   - increment numConfigFound to remember that we found a config
   3369             //            matching for this scan result
   3370             //   - dont do anything since the config was deleted, just skip...
   3371             return;
   3372         }
   3373 
   3374         ScanDetailCache scanDetailCache = getScanDetailCache(config);
   3375         if (scanDetailCache == null) {
   3376             Log.w(TAG, "Could not allocate scan cache for " + config.SSID);
   3377             return;
   3378         }
   3379 
   3380         // Adding a new BSSID
   3381         ScanResult result = scanDetailCache.get(scanResult.BSSID);
   3382         if (result != null) {
   3383             // transfer the black list status
   3384             scanResult.autoJoinStatus = result.autoJoinStatus;
   3385             scanResult.blackListTimestamp = result.blackListTimestamp;
   3386             scanResult.numIpConfigFailures = result.numIpConfigFailures;
   3387             scanResult.numConnection = result.numConnection;
   3388             scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate;
   3389         }
   3390 
   3391         if (config.ephemeral) {
   3392             // For an ephemeral Wi-Fi config, the ScanResult should be considered
   3393             // untrusted.
   3394             scanResult.untrusted = true;
   3395         }
   3396 
   3397         if (scanDetailCache.size() > (maxNumScanCacheEntries + 64)) {
   3398             long now_dbg = 0;
   3399             if (VVDBG) {
   3400                 loge(" Will trim config " + config.configKey()
   3401                         + " size " + scanDetailCache.size());
   3402 
   3403                 for (ScanDetail sd : scanDetailCache.values()) {
   3404                     loge("     " + sd.getBSSIDString() + " " + sd.getSeen());
   3405                 }
   3406                 now_dbg = SystemClock.elapsedRealtimeNanos();
   3407             }
   3408             // Trim the scan result cache to maxNumScanCacheEntries entries max
   3409             // Since this operation is expensive, make sure it is not performed
   3410             // until the cache has grown significantly above the trim treshold
   3411             scanDetailCache.trim(maxNumScanCacheEntries);
   3412             if (VVDBG) {
   3413                 long diff = SystemClock.elapsedRealtimeNanos() - now_dbg;
   3414                 loge(" Finished trimming config, time(ns) " + diff);
   3415                 for (ScanDetail sd : scanDetailCache.values()) {
   3416                     loge("     " + sd.getBSSIDString() + " " + sd.getSeen());
   3417                 }
   3418             }
   3419         }
   3420 
   3421         // Add the scan result to this WifiConfiguration
   3422         if (passpointMatch != null)
   3423             scanDetailCache.put(scanDetail, passpointMatch, getHomeSPForConfig(config));
   3424         else
   3425             scanDetailCache.put(scanDetail);
   3426 
   3427         // Since we added a scan result to this configuration, re-attempt linking
   3428         linkConfiguration(config);
   3429     }
   3430 
   3431 
   3432     // Update the WifiConfiguration database with the new scan result
   3433     // A scan result can be associated to multiple WifiConfigurations
   3434     public boolean updateSavedNetworkHistory(ScanDetail scanDetail) {
   3435 
   3436         ScanResult scanResult = scanDetail.getScanResult();
   3437         NetworkDetail networkDetail = scanDetail.getNetworkDetail();
   3438 
   3439         int numConfigFound = 0;
   3440         if (scanResult == null)
   3441             return false;
   3442 
   3443         String SSID = "\"" + scanResult.SSID + "\"";
   3444 
   3445         if (networkDetail.hasInterworking()) {
   3446             Map<HomeSP, PasspointMatch> matches = matchPasspointNetworks(scanDetail);
   3447             if (matches != null) {
   3448                 cacheScanResultForPasspointConfigs(scanDetail, matches);
   3449                 return matches.size() != 0;
   3450             }
   3451         }
   3452 
   3453         for (WifiConfiguration config : mConfiguredNetworks.values()) {
   3454             boolean found = false;
   3455 
   3456             if (config.SSID == null || !config.SSID.equals(SSID)) {
   3457                 // SSID mismatch
   3458                 if (VVDBG) {
   3459                     loge("updateSavedNetworkHistory(): SSID mismatch " + config.configKey()
   3460                             + " SSID=" + config.SSID + " " + SSID);
   3461                 }
   3462                 continue;
   3463             }
   3464             if (VDBG) {
   3465                 loge("updateSavedNetworkHistory(): try " + config.configKey()
   3466                         + " SSID=" + config.SSID + " " + scanResult.SSID
   3467                         + " " + scanResult.capabilities
   3468                         + " ajst=" + config.autoJoinStatus);
   3469             }
   3470             if (scanResult.capabilities.contains("WEP")
   3471                     && config.configKey().contains("WEP")) {
   3472                 found = true;
   3473             } else if (scanResult.capabilities.contains("PSK")
   3474                     && config.configKey().contains("PSK")) {
   3475                 found = true;
   3476             } else if (scanResult.capabilities.contains("EAP")
   3477                     && config.configKey().contains("EAP")) {
   3478                 found = true;
   3479             } else if (!scanResult.capabilities.contains("WEP")
   3480                 && !scanResult.capabilities.contains("PSK")
   3481                 && !scanResult.capabilities.contains("EAP")
   3482                 && !config.configKey().contains("WEP")
   3483                     && !config.configKey().contains("PSK")
   3484                     && !config.configKey().contains("EAP")) {
   3485                 found = true;
   3486             }
   3487 
   3488             if (found) {
   3489                 numConfigFound ++;
   3490                 cacheScanResultForConfig(config, scanDetail, null);
   3491             }
   3492 
   3493             if (VDBG && found) {
   3494                 String status = "";
   3495                 if (scanResult.autoJoinStatus > 0) {
   3496                     status = " status=" + Integer.toString(scanResult.autoJoinStatus);
   3497                 }
   3498                 loge("        got known scan result " +
   3499                         scanResult.BSSID + " key : "
   3500                         + config.configKey() + " num: " +
   3501                         Integer.toString(getScanDetailCache(config).size())
   3502                         + " rssi=" + Integer.toString(scanResult.level)
   3503                         + " freq=" + Integer.toString(scanResult.frequency)
   3504                         + status);
   3505             }
   3506         }
   3507         return numConfigFound != 0;
   3508     }
   3509 
   3510     /* Compare current and new configuration and write to file on change */
   3511     private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
   3512             WifiConfiguration currentConfig,
   3513             WifiConfiguration newConfig) {
   3514         boolean ipChanged = false;
   3515         boolean proxyChanged = false;
   3516 
   3517         if (VDBG) {
   3518             loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +
   3519                     newConfig.SSID + " path: " + ipConfigFile);
   3520         }
   3521 
   3522 
   3523         switch (newConfig.getIpAssignment()) {
   3524             case STATIC:
   3525                 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
   3526                     ipChanged = true;
   3527                 } else {
   3528                     ipChanged = !Objects.equals(
   3529                             currentConfig.getStaticIpConfiguration(),
   3530                             newConfig.getStaticIpConfiguration());
   3531                 }
   3532                 break;
   3533             case DHCP:
   3534                 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
   3535                     ipChanged = true;
   3536                 }
   3537                 break;
   3538             case UNASSIGNED:
   3539                 /* Ignore */
   3540                 break;
   3541             default:
   3542                 loge("Ignore invalid ip assignment during write");
   3543                 break;
   3544         }
   3545 
   3546         switch (newConfig.getProxySettings()) {
   3547             case STATIC:
   3548             case PAC:
   3549                 ProxyInfo newHttpProxy = newConfig.getHttpProxy();
   3550                 ProxyInfo currentHttpProxy = currentConfig.getHttpProxy();
   3551 
   3552                 if (newHttpProxy != null) {
   3553                     proxyChanged = !newHttpProxy.equals(currentHttpProxy);
   3554                 } else {
   3555                     proxyChanged = (currentHttpProxy != null);
   3556                 }
   3557                 break;
   3558             case NONE:
   3559                 if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
   3560                     proxyChanged = true;
   3561                 }
   3562                 break;
   3563             case UNASSIGNED:
   3564                 /* Ignore */
   3565                 break;
   3566             default:
   3567                 loge("Ignore invalid proxy configuration during write");
   3568                 break;
   3569         }
   3570 
   3571         if (ipChanged) {
   3572             currentConfig.setIpAssignment(newConfig.getIpAssignment());
   3573             currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration());
   3574             log("IP config changed SSID = " + currentConfig.SSID);
   3575             if (currentConfig.getStaticIpConfiguration() != null) {
   3576                 log(" static configuration: " +
   3577                     currentConfig.getStaticIpConfiguration().toString());
   3578             }
   3579         }
   3580 
   3581         if (proxyChanged) {
   3582             currentConfig.setProxySettings(newConfig.getProxySettings());
   3583             currentConfig.setHttpProxy(newConfig.getHttpProxy());
   3584             log("proxy changed SSID = " + currentConfig.SSID);
   3585             if (currentConfig.getHttpProxy() != null) {
   3586                 log(" proxyProperties: " + currentConfig.getHttpProxy().toString());
   3587             }
   3588         }
   3589 
   3590         if (ipChanged || proxyChanged) {
   3591             writeIpAndProxyConfigurations();
   3592             sendConfiguredNetworksChangedBroadcast(currentConfig,
   3593                     WifiManager.CHANGE_REASON_CONFIG_CHANGE);
   3594         }
   3595         return new NetworkUpdateResult(ipChanged, proxyChanged);
   3596     }
   3597 
   3598     /** Returns true if a particular config key needs to be quoted when passed to the supplicant. */
   3599     private boolean enterpriseConfigKeyShouldBeQuoted(String key) {
   3600         switch (key) {
   3601             case WifiEnterpriseConfig.EAP_KEY:
   3602             case WifiEnterpriseConfig.ENGINE_KEY:
   3603                 return false;
   3604             default:
   3605                 return true;
   3606         }
   3607     }
   3608 
   3609     /**
   3610      * Read the variables from the supplicant daemon that are needed to
   3611      * fill in the WifiConfiguration object.
   3612      *
   3613      * @param config the {@link WifiConfiguration} object to be filled in.
   3614      */
   3615     private void readNetworkVariables(WifiConfiguration config) {
   3616 
   3617         int netId = config.networkId;
   3618         if (netId < 0)
   3619             return;
   3620 
   3621         /*
   3622          * TODO: maybe should have a native method that takes an array of
   3623          * variable names and returns an array of values. But we'd still
   3624          * be doing a round trip to the supplicant daemon for each variable.
   3625          */
   3626         String value;
   3627 
   3628         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
   3629         if (!TextUtils.isEmpty(value)) {
   3630             if (value.charAt(0) != '"') {
   3631                 config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
   3632                 //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
   3633                 //supplicant string
   3634             } else {
   3635                 config.SSID = value;
   3636             }
   3637         } else {
   3638             config.SSID = null;
   3639         }
   3640 
   3641         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
   3642         if (!TextUtils.isEmpty(value)) {
   3643             config.BSSID = value;
   3644         } else {
   3645             config.BSSID = null;
   3646         }
   3647 
   3648         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
   3649         config.priority = -1;
   3650         if (!TextUtils.isEmpty(value)) {
   3651             try {
   3652                 config.priority = Integer.parseInt(value);
   3653             } catch (NumberFormatException ignore) {
   3654             }
   3655         }
   3656 
   3657         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
   3658         config.hiddenSSID = false;
   3659         if (!TextUtils.isEmpty(value)) {
   3660             try {
   3661                 config.hiddenSSID = Integer.parseInt(value) != 0;
   3662             } catch (NumberFormatException ignore) {
   3663             }
   3664         }
   3665 
   3666         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
   3667         config.wepTxKeyIndex = -1;
   3668         if (!TextUtils.isEmpty(value)) {
   3669             try {
   3670                 config.wepTxKeyIndex = Integer.parseInt(value);
   3671             } catch (NumberFormatException ignore) {
   3672             }
   3673         }
   3674 
   3675         for (int i = 0; i < 4; i++) {
   3676             value = mWifiNative.getNetworkVariable(netId,
   3677                     WifiConfiguration.wepKeyVarNames[i]);
   3678             if (!TextUtils.isEmpty(value)) {
   3679                 config.wepKeys[i] = value;
   3680             } else {
   3681                 config.wepKeys[i] = null;
   3682             }
   3683         }
   3684 
   3685         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
   3686         if (!TextUtils.isEmpty(value)) {
   3687             config.preSharedKey = value;
   3688         } else {
   3689             config.preSharedKey = null;
   3690         }
   3691 
   3692         readNetworkBitsetVariable(config.networkId, config.allowedProtocols,
   3693                 WifiConfiguration.Protocol.varName, WifiConfiguration.Protocol.strings);
   3694 
   3695         readNetworkBitsetVariable(config.networkId, config.allowedKeyManagement,
   3696                 WifiConfiguration.KeyMgmt.varName, WifiConfiguration.KeyMgmt.strings);
   3697 
   3698         readNetworkBitsetVariable(config.networkId, config.allowedAuthAlgorithms,
   3699                 WifiConfiguration.AuthAlgorithm.varName, WifiConfiguration.AuthAlgorithm.strings);
   3700 
   3701         readNetworkBitsetVariable(config.networkId, config.allowedPairwiseCiphers,
   3702                 WifiConfiguration.PairwiseCipher.varName, WifiConfiguration.PairwiseCipher.strings);
   3703 
   3704         readNetworkBitsetVariable(config.networkId, config.allowedGroupCiphers,
   3705                 WifiConfiguration.GroupCipher.varName, WifiConfiguration.GroupCipher.strings);
   3706 
   3707         if (config.enterpriseConfig == null) {
   3708             config.enterpriseConfig = new WifiEnterpriseConfig();
   3709         }
   3710         HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
   3711         for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) {
   3712             value = mWifiNative.getNetworkVariable(netId, key);
   3713             if (!TextUtils.isEmpty(value)) {
   3714                 if (!enterpriseConfigKeyShouldBeQuoted(key)) {
   3715                     value = removeDoubleQuotes(value);
   3716                 }
   3717                 enterpriseFields.put(key, value);
   3718             } else {
   3719                 enterpriseFields.put(key, EMPTY_VALUE);
   3720             }
   3721         }
   3722 
   3723         if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) {
   3724             saveConfig();
   3725         }
   3726 
   3727         migrateCerts(config.enterpriseConfig);
   3728         // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore);
   3729     }
   3730 
   3731     private static String removeDoubleQuotes(String string) {
   3732         int length = string.length();
   3733         if ((length > 1) && (string.charAt(0) == '"')
   3734                 && (string.charAt(length - 1) == '"')) {
   3735             return string.substring(1, length - 1);
   3736         }
   3737         return string;
   3738     }
   3739 
   3740     private static String makeString(BitSet set, String[] strings) {
   3741         StringBuffer buf = new StringBuffer();
   3742         int nextSetBit = -1;
   3743 
   3744         /* Make sure all set bits are in [0, strings.length) to avoid
   3745          * going out of bounds on strings.  (Shouldn't happen, but...) */
   3746         set = set.get(0, strings.length);
   3747 
   3748         while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
   3749             buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
   3750         }
   3751 
   3752         // remove trailing space
   3753         if (set.cardinality() > 0) {
   3754             buf.setLength(buf.length() - 1);
   3755         }
   3756 
   3757         return buf.toString();
   3758     }
   3759 
   3760     private int lookupString(String string, String[] strings) {
   3761         int size = strings.length;
   3762 
   3763         string = string.replace('-', '_');
   3764 
   3765         for (int i = 0; i < size; i++)
   3766             if (string.equals(strings[i]))
   3767                 return i;
   3768 
   3769         // if we ever get here, we should probably add the
   3770         // value to WifiConfiguration to reflect that it's
   3771         // supported by the WPA supplicant
   3772         loge("Failed to look-up a string: " + string);
   3773 
   3774         return -1;
   3775     }
   3776 
   3777     /* return the allowed key management based on a scan result */
   3778 
   3779     public WifiConfiguration wifiConfigurationFromScanResult(ScanDetail scanDetail) {
   3780 
   3781         ScanResult result = scanDetail.getScanResult();
   3782         WifiConfiguration config = new WifiConfiguration();
   3783 
   3784         config.SSID = "\"" + result.SSID + "\"";
   3785 
   3786         if (VDBG) {
   3787             loge("WifiConfiguration from scan results " +
   3788                     config.SSID + " cap " + result.capabilities);
   3789         }
   3790         if (result.capabilities.contains("WEP")) {
   3791             config.allowedKeyManagement.set(KeyMgmt.NONE);
   3792             config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //?
   3793             config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
   3794         }
   3795 
   3796         if (result.capabilities.contains("PSK")) {
   3797             config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
   3798         }
   3799 
   3800         if (result.capabilities.contains("EAP")) {
   3801             //this is probably wrong, as we don't have a way to enter the enterprise config
   3802             config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
   3803             config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
   3804         }
   3805 
   3806         /* getScanDetailCache(config).put(scanDetail); */
   3807 
   3808         return config;
   3809     }
   3810 
   3811 
   3812     /* Returns a unique for a given configuration */
   3813     private static int configKey(WifiConfiguration config) {
   3814         String key = config.configKey();
   3815         return key.hashCode();
   3816     }
   3817 
   3818     void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   3819         pw.println("Dump of WifiConfigStore");
   3820         pw.println("mLastPriority " + mLastPriority);
   3821         pw.println("Configured networks");
   3822         for (WifiConfiguration conf : getAllConfiguredNetworks()) {
   3823             pw.println(conf);
   3824         }
   3825         pw.println();
   3826         if (mLostConfigsDbg != null && mLostConfigsDbg.size() > 0) {
   3827             pw.println("LostConfigs: ");
   3828             for (String s : mLostConfigsDbg) {
   3829                 pw.println(s);
   3830             }
   3831         }
   3832         if (mLocalLog != null) {
   3833             pw.println("WifiConfigStore - Log Begin ----");
   3834             mLocalLog.dump(fd, pw, args);
   3835             pw.println("WifiConfigStore - Log End ----");
   3836         }
   3837         if (mMOManager.isConfigured()) {
   3838             pw.println("Begin dump of ANQP Cache");
   3839             mAnqpCache.dump(pw);
   3840             pw.println("End dump of ANQP Cache");
   3841         }
   3842     }
   3843 
   3844     public String getConfigFile() {
   3845         return ipConfigFile;
   3846     }
   3847 
   3848     protected void loge(String s) {
   3849         loge(s, false);
   3850     }
   3851 
   3852     protected void loge(String s, boolean stack) {
   3853         if (stack) {
   3854             Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
   3855                     + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
   3856                     + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
   3857                     + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
   3858         } else {
   3859             Log.e(TAG, s);
   3860         }
   3861     }
   3862 
   3863     private void logKernelTime() {
   3864         long kernelTimeMs = System.nanoTime()/(1000*1000);
   3865         StringBuilder builder = new StringBuilder();
   3866         builder.append("kernel time = ").append(kernelTimeMs/1000).append(".").append
   3867                 (kernelTimeMs%1000).append("\n");
   3868         localLog(builder.toString());
   3869     }
   3870 
   3871     protected void log(String s) {
   3872         Log.d(TAG, s);
   3873     }
   3874 
   3875     private void localLog(String s) {
   3876         if (mLocalLog != null) {
   3877             mLocalLog.log(s);
   3878         }
   3879     }
   3880 
   3881     private void localLog(String s, boolean force) {
   3882         localLog(s);
   3883         if (force) loge(s);
   3884     }
   3885 
   3886     private void localLog(String s, int netId) {
   3887         if (mLocalLog == null) {
   3888             return;
   3889         }
   3890 
   3891         WifiConfiguration config;
   3892         synchronized(mConfiguredNetworks) {             // !!! Useless synchronization
   3893             config = mConfiguredNetworks.get(netId);
   3894         }
   3895 
   3896         if (config != null) {
   3897             mLocalLog.log(s + " " + config.getPrintableSsid() + " " + netId
   3898                     + " status=" + config.status
   3899                     + " key=" + config.configKey());
   3900         } else {
   3901             mLocalLog.log(s + " " + netId);
   3902         }
   3903     }
   3904 
   3905     // Certificate and private key management for EnterpriseConfig
   3906     static boolean needsKeyStore(WifiEnterpriseConfig config) {
   3907         // Has no keys to be installed
   3908         if (config.getClientCertificate() == null && config.getCaCertificate() == null)
   3909             return false;
   3910         return true;
   3911     }
   3912 
   3913     static boolean isHardwareBackedKey(PrivateKey key) {
   3914         return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
   3915     }
   3916 
   3917     static boolean hasHardwareBackedKey(Certificate certificate) {
   3918         return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
   3919     }
   3920 
   3921     static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
   3922         String client = config.getClientCertificateAlias();
   3923         if (!TextUtils.isEmpty(client)) {
   3924             // a valid client certificate is configured
   3925 
   3926             // BUGBUG: keyStore.get() never returns certBytes; because it is not
   3927             // taking WIFI_UID as a parameter. It always looks for certificate
   3928             // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
   3929             // all certificates need software keystore until we get the get() API
   3930             // fixed.
   3931 
   3932             return true;
   3933         }
   3934 
   3935         /*
   3936         try {
   3937 
   3938             if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
   3939                     .USER_CERTIFICATE + client);
   3940 
   3941             CertificateFactory factory = CertificateFactory.getInstance("X.509");
   3942             if (factory == null) {
   3943                 Slog.e(TAG, "Error getting certificate factory");
   3944                 return;
   3945             }
   3946 
   3947             byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
   3948             if (certBytes != null) {
   3949                 Certificate cert = (X509Certificate) factory.generateCertificate(
   3950                         new ByteArrayInputStream(certBytes));
   3951 
   3952                 if (cert != null) {
   3953                     mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
   3954 
   3955                     if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
   3956                             .USER_CERTIFICATE + client);
   3957                     if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
   3958                             "does not need" ) + " software key store");
   3959                 } else {
   3960                     Slog.d(TAG, "could not generate certificate");
   3961                 }
   3962             } else {
   3963                 Slog.e(TAG, "Could not load client certificate " + Credentials
   3964                         .USER_CERTIFICATE + client);
   3965                 mNeedsSoftwareKeystore = true;
   3966             }
   3967 
   3968         } catch(CertificateException e) {
   3969             Slog.e(TAG, "Could not read certificates");
   3970             mCaCert = null;
   3971             mClientCertificate = null;
   3972         }
   3973         */
   3974 
   3975         return false;
   3976     }
   3977 
   3978     boolean isNetworkConfigured(WifiConfiguration config) {
   3979         // Check if either we have a network Id or a WifiConfiguration
   3980         // matching the one we are trying to add.
   3981 
   3982         if(config.networkId != INVALID_NETWORK_ID) {
   3983             return (mConfiguredNetworks.get(config.networkId) != null);
   3984         }
   3985 
   3986         return (mConfiguredNetworks.getByConfigKey(config.configKey()) != null);
   3987     }
   3988 
   3989     /**
   3990      * Checks if uid has access to modify the configuration corresponding to networkId.
   3991      *
   3992      * Factors involved in modifiability of a config are as follows.
   3993      *    If uid is a Device Owner app then it has full control over the device, including WiFi
   3994      * configs.
   3995      *    If the modification is only for administrative annotation (e.g. when connecting) or the
   3996      * config is not lockdown eligible (currently that means any config not last updated by the DO)
   3997      * then the creator of config or an app holding OVERRIDE_CONFIG_WIFI can modify the config.
   3998      *    If the config is lockdown eligible and the modification is substantial (not annotation)
   3999      * then the requirement to be able to modify the config by the uid is as follows:
   4000      *    a) the uid has to hold OVERRIDE_CONFIG_WIFI and
   4001      *    b) the lockdown feature should be disabled.
   4002      */
   4003     boolean canModifyNetwork(int uid, int networkId, boolean onlyAnnotate) {
   4004         WifiConfiguration config = mConfiguredNetworks.get(networkId);
   4005 
   4006         if (config == null) {
   4007             loge("canModifyNetwork: cannot find config networkId " + networkId);
   4008             return false;
   4009         }
   4010 
   4011         final DevicePolicyManagerInternal dpmi = LocalServices.getService(
   4012                 DevicePolicyManagerInternal.class);
   4013 
   4014         final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
   4015                 DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
   4016 
   4017         if (isUidDeviceOwner) {
   4018             // Device Owner has full control over the device, including WiFi Configs
   4019             return true;
   4020         }
   4021 
   4022         final boolean isCreator = (config.creatorUid == uid);
   4023 
   4024         if (onlyAnnotate) {
   4025             return isCreator || checkConfigOverridePermission(uid);
   4026         }
   4027 
   4028         // Check if device has DPM capability. If it has and dpmi is still null, then we
   4029         // treat this case with suspicion and bail out.
   4030         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
   4031                 && dpmi == null) {
   4032             return false;
   4033         }
   4034 
   4035         // WiFi config lockdown related logic. At this point we know uid NOT to be a Device Owner.
   4036 
   4037         final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
   4038                 config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
   4039         if (!isConfigEligibleForLockdown) {
   4040             return isCreator || checkConfigOverridePermission(uid);
   4041         }
   4042 
   4043         final ContentResolver resolver = mContext.getContentResolver();
   4044         final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
   4045                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
   4046         return !isLockdownFeatureEnabled && checkConfigOverridePermission(uid);
   4047     }
   4048 
   4049     /**
   4050      * Checks if uid has access to modify config.
   4051      */
   4052     boolean canModifyNetwork(int uid, WifiConfiguration config, boolean onlyAnnotate) {
   4053         if (config == null) {
   4054             loge("canModifyNetowrk recieved null configuration");
   4055             return false;
   4056         }
   4057 
   4058         // Resolve the correct network id.
   4059         int netid;
   4060         if (config.networkId != INVALID_NETWORK_ID){
   4061             netid = config.networkId;
   4062         } else {
   4063             WifiConfiguration test = mConfiguredNetworks.getByConfigKey(config.configKey());
   4064             if (test == null) {
   4065                 return false;
   4066             } else {
   4067                 netid = test.networkId;
   4068             }
   4069         }
   4070 
   4071         return canModifyNetwork(uid, netid, onlyAnnotate);
   4072     }
   4073 
   4074     boolean checkConfigOverridePermission(int uid) {
   4075         try {
   4076             return (AppGlobals.getPackageManager().checkUidPermission(
   4077                     android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid)
   4078                     == PackageManager.PERMISSION_GRANTED);
   4079         } catch (RemoteException e) {
   4080             return false;
   4081         }
   4082     }
   4083 
   4084     /** called when CS ask WiFistateMachine to disconnect the current network
   4085      * because the score is bad.
   4086      */
   4087     void handleBadNetworkDisconnectReport(int netId, WifiInfo info) {
   4088         /* TODO verify the bad network is current */
   4089         WifiConfiguration config = mConfiguredNetworks.get(netId);
   4090         if (config != null) {
   4091             if ((info.getRssi() < WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_24
   4092                     && info.is24GHz()) || (info.getRssi() <
   4093                             WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_5 && info.is5GHz())) {
   4094                 // We got disconnected and RSSI was bad, so disable light
   4095                 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
   4096                         + WifiConfiguration.UNWANTED_BLACKLIST_SOFT_BUMP);
   4097                 loge("handleBadNetworkDisconnectReport (+4) "
   4098                         + Integer.toString(netId) + " " + info);
   4099             } else {
   4100                 // We got disabled but RSSI is good, so disable hard
   4101                 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
   4102                         + WifiConfiguration.UNWANTED_BLACKLIST_HARD_BUMP);
   4103                 loge("handleBadNetworkDisconnectReport (+8) "
   4104                         + Integer.toString(netId) + " " + info);
   4105             }
   4106         }
   4107         // Record last time Connectivity Service switched us away from WiFi and onto Cell
   4108         lastUnwantedNetworkDisconnectTimestamp = System.currentTimeMillis();
   4109     }
   4110 
   4111     boolean handleBSSIDBlackList(int netId, String BSSID, boolean enable) {
   4112         boolean found = false;
   4113         if (BSSID == null)
   4114             return found;
   4115 
   4116         // Look for the BSSID in our config store
   4117         for (WifiConfiguration config : mConfiguredNetworks.values()) {
   4118             if (getScanDetailCache(config) != null) {
   4119                 for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
   4120                     if (scanDetail.getBSSIDString().equals(BSSID)) {
   4121                         if (enable) {
   4122                             scanDetail.getScanResult().setAutoJoinStatus(ScanResult.ENABLED);
   4123                         } else {
   4124                             // Black list the BSSID we were trying to join
   4125                             // so as the Roam state machine
   4126                             // doesn't pick it up over and over
   4127                             scanDetail.getScanResult().setAutoJoinStatus(
   4128                                     ScanResult.AUTO_ROAM_DISABLED);
   4129                             found = true;
   4130                         }
   4131                     }
   4132                 }
   4133             }
   4134         }
   4135         return found;
   4136     }
   4137 
   4138     int getMaxDhcpRetries() {
   4139         return Settings.Global.getInt(mContext.getContentResolver(),
   4140                 Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
   4141                 DEFAULT_MAX_DHCP_RETRIES);
   4142     }
   4143 
   4144     void clearBssidBlacklist() {
   4145         if (!mWifiStateMachine.useHalBasedAutoJoinOffload()) {
   4146             if(DBG) {
   4147                 Log.d(TAG, "No blacklist allowed without epno enabled");
   4148             }
   4149             return;
   4150         }
   4151         mBssidBlacklist = new HashSet<String>();
   4152         mWifiNative.clearBlacklist();
   4153         mWifiNative.setBssidBlacklist(null);
   4154     }
   4155 
   4156     void blackListBssid(String BSSID) {
   4157         if (!mWifiStateMachine.useHalBasedAutoJoinOffload()) {
   4158             if(DBG) {
   4159                 Log.d(TAG, "No blacklist allowed without epno enabled");
   4160             }
   4161             return;
   4162         }
   4163         if (BSSID == null)
   4164             return;
   4165         mBssidBlacklist.add(BSSID);
   4166         // Blacklist at wpa_supplicant
   4167         mWifiNative.addToBlacklist(BSSID);
   4168         // Blacklist at firmware
   4169         String list[] = new String[mBssidBlacklist.size()];
   4170         int count = 0;
   4171         for (String bssid : mBssidBlacklist) {
   4172             list[count++] = bssid;
   4173         }
   4174         mWifiNative.setBssidBlacklist(list);
   4175     }
   4176 
   4177     void handleSSIDStateChange(int netId, boolean enabled, String message, String BSSID) {
   4178         WifiConfiguration config = mConfiguredNetworks.get(netId);
   4179         if (config != null) {
   4180             if (enabled) {
   4181                 loge("Ignoring SSID re-enabled from supplicant:  " + config.configKey() +
   4182                         " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
   4183                         + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
   4184                 //We should not re-enable the BSSID based on Supplicant reanable.
   4185                 // Framework will re-enable it after its own blacklist timer expires
   4186             } else {
   4187                 loge("SSID temp disabled for  " + config.configKey() +
   4188                         " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
   4189                         + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
   4190                 if (message != null) {
   4191                     loge(" message=" + message);
   4192                 }
   4193                 if (config.selfAdded && config.lastConnected == 0) {
   4194                     // This is a network we self added, and we never succeeded,
   4195                     // the user did not create this network and never entered its credentials,
   4196                     // so we want to be very aggressive in disabling it completely.
   4197                     removeConfigAndSendBroadcastIfNeeded(config.networkId);
   4198                 } else {
   4199                     if (message != null) {
   4200                         if (message.contains("no identity")) {
   4201                             config.setAutoJoinStatus(
   4202                                     WifiConfiguration.AUTO_JOIN_DISABLED_NO_CREDENTIALS);
   4203                             if (DBG) {
   4204                                 loge("no identity blacklisted " + config.configKey() + " to "
   4205                                         + Integer.toString(config.autoJoinStatus));
   4206                             }
   4207                         } else if (message.contains("WRONG_KEY")
   4208                                 || message.contains("AUTH_FAILED")) {
   4209                             // This configuration has received an auth failure, so disable it
   4210                             // temporarily because we don't want auto-join to try it out.
   4211                             // this network may be re-enabled by the "usual"
   4212                             // enableAllNetwork function
   4213                             config.numAuthFailures++;
   4214                             if (config.numAuthFailures > maxAuthErrorsToBlacklist) {
   4215                                 config.setAutoJoinStatus
   4216                                         (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
   4217                                 disableNetwork(netId,
   4218                                         WifiConfiguration.DISABLED_AUTH_FAILURE);
   4219                                 loge("Authentication failure, blacklist " + config.configKey() + " "
   4220                                             + Integer.toString(config.networkId)
   4221                                             + " num failures " + config.numAuthFailures);
   4222                             }
   4223                         } else if (message.contains("DHCP FAILURE")) {
   4224                             config.numIpConfigFailures++;
   4225                             config.lastConnectionFailure = System.currentTimeMillis();
   4226                             int maxRetries = getMaxDhcpRetries();
   4227                             // maxRetries == 0 means keep trying forever
   4228                             if (maxRetries > 0 && config.numIpConfigFailures > maxRetries) {
   4229                                 /**
   4230                                  * If we've exceeded the maximum number of retries for DHCP
   4231                                  * to a given network, disable the network
   4232                                  */
   4233                                 config.setAutoJoinStatus
   4234                                         (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
   4235                                 disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE);
   4236                                 loge("DHCP failure, blacklist " + config.configKey() + " "
   4237                                         + Integer.toString(config.networkId)
   4238                                         + " num failures " + config.numIpConfigFailures);
   4239                             }
   4240 
   4241                             // Also blacklist the BSSId if we find it
   4242                             ScanResult result = null;
   4243                             String bssidDbg = "";
   4244                             if (getScanDetailCache(config) != null && BSSID != null) {
   4245                                 result = getScanDetailCache(config).get(BSSID);
   4246                             }
   4247                             if (result != null) {
   4248                                 result.numIpConfigFailures ++;
   4249                                 bssidDbg = BSSID + " ipfail=" + result.numIpConfigFailures;
   4250                                 if (result.numIpConfigFailures > 3) {
   4251                                     // Tell supplicant to stop trying this BSSID
   4252                                     mWifiNative.addToBlacklist(BSSID);
   4253                                     result.setAutoJoinStatus(ScanResult.AUTO_JOIN_DISABLED);
   4254                                 }
   4255                             }
   4256 
   4257                             if (DBG) {
   4258                                 loge("blacklisted " + config.configKey() + " to "
   4259                                         + config.autoJoinStatus
   4260                                         + " due to IP config failures, count="
   4261                                         + config.numIpConfigFailures
   4262                                         + " disableReason=" + config.disableReason
   4263                                         + " " + bssidDbg);
   4264                             }
   4265                         } else if (message.contains("CONN_FAILED")) {
   4266                             config.numConnectionFailures++;
   4267                             if (config.numConnectionFailures > maxConnectionErrorsToBlacklist) {
   4268                                 config.setAutoJoinStatus
   4269                                         (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
   4270                                 disableNetwork(netId,
   4271                                         WifiConfiguration.DISABLED_ASSOCIATION_REJECT);
   4272                                 loge("Connection failure, blacklist " + config.configKey() + " "
   4273                                         + config.networkId
   4274                                         + " num failures " + config.numConnectionFailures);
   4275                             }
   4276                         }
   4277                         message.replace("\n", "");
   4278                         message.replace("\r", "");
   4279                         config.lastFailure = message;
   4280                     }
   4281                 }
   4282             }
   4283         }
   4284     }
   4285 
   4286     boolean installKeys(WifiEnterpriseConfig config, String name) {
   4287         boolean ret = true;
   4288         String privKeyName = Credentials.USER_PRIVATE_KEY + name;
   4289         String userCertName = Credentials.USER_CERTIFICATE + name;
   4290         String caCertName = Credentials.CA_CERTIFICATE + name;
   4291         if (config.getClientCertificate() != null) {
   4292             byte[] privKeyData = config.getClientPrivateKey().getEncoded();
   4293             if (isHardwareBackedKey(config.getClientPrivateKey())) {
   4294                 // Hardware backed key store is secure enough to store keys un-encrypted, this
   4295                 // removes the need for user to punch a PIN to get access to these keys
   4296                 if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store");
   4297                 ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID,
   4298                         KeyStore.FLAG_NONE);
   4299             } else {
   4300                 // Software backed key store is NOT secure enough to store keys un-encrypted.
   4301                 // Save keys encrypted so they are protected with user's PIN. User will
   4302                 // have to unlock phone before being able to use these keys and connect to
   4303                 // networks.
   4304                 if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store");
   4305                 ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
   4306                         KeyStore.FLAG_ENCRYPTED);
   4307             }
   4308             if (ret == false) {
   4309                 return ret;
   4310             }
   4311 
   4312             ret = putCertInKeyStore(userCertName, config.getClientCertificate());
   4313             if (ret == false) {
   4314                 // Remove private key installed
   4315                 mKeyStore.delete(privKeyName, Process.WIFI_UID);
   4316                 return ret;
   4317             }
   4318         }
   4319 
   4320         if (config.getCaCertificate() != null) {
   4321             ret = putCertInKeyStore(caCertName, config.getCaCertificate());
   4322             if (ret == false) {
   4323                 if (config.getClientCertificate() != null) {
   4324                     // Remove client key+cert
   4325                     mKeyStore.delete(privKeyName, Process.WIFI_UID);
   4326                     mKeyStore.delete(userCertName, Process.WIFI_UID);
   4327                 }
   4328                 return ret;
   4329             }
   4330         }
   4331 
   4332         // Set alias names
   4333         if (config.getClientCertificate() != null) {
   4334             config.setClientCertificateAlias(name);
   4335             config.resetClientKeyEntry();
   4336         }
   4337 
   4338         if (config.getCaCertificate() != null) {
   4339             config.setCaCertificateAlias(name);
   4340             config.resetCaCertificate();
   4341         }
   4342 
   4343         return ret;
   4344     }
   4345 
   4346     private boolean putCertInKeyStore(String name, Certificate cert) {
   4347         try {
   4348             byte[] certData = Credentials.convertToPem(cert);
   4349             if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore");
   4350             return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
   4351 
   4352         } catch (IOException e1) {
   4353             return false;
   4354         } catch (CertificateException e2) {
   4355             return false;
   4356         }
   4357     }
   4358 
   4359     void removeKeys(WifiEnterpriseConfig config) {
   4360         String client = config.getClientCertificateAlias();
   4361         // a valid client certificate is configured
   4362         if (!TextUtils.isEmpty(client)) {
   4363             if (DBG) Log.d(TAG, "removing client private key and user cert");
   4364             mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
   4365             mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
   4366         }
   4367 
   4368         String ca = config.getCaCertificateAlias();
   4369         // a valid ca certificate is configured
   4370         if (!TextUtils.isEmpty(ca)) {
   4371             if (DBG) Log.d(TAG, "removing CA cert");
   4372             mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
   4373         }
   4374     }
   4375 
   4376 
   4377     /** Migrates the old style TLS config to the new config style. This should only be used
   4378      * when restoring an old wpa_supplicant.conf or upgrading from a previous
   4379      * platform version.
   4380      * @return true if the config was updated
   4381      * @hide
   4382      */
   4383     boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) {
   4384         String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME);
   4385         /*
   4386          * If the old configuration value is not present, then there is nothing
   4387          * to do.
   4388          */
   4389         if (TextUtils.isEmpty(oldPrivateKey)) {
   4390             return false;
   4391         } else {
   4392             // Also ignore it if it's empty quotes.
   4393             oldPrivateKey = removeDoubleQuotes(oldPrivateKey);
   4394             if (TextUtils.isEmpty(oldPrivateKey)) {
   4395                 return false;
   4396             }
   4397         }
   4398 
   4399         config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE);
   4400         config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY,
   4401                 WifiEnterpriseConfig.ENGINE_ID_KEYSTORE);
   4402 
   4403         /*
   4404         * The old key started with the keystore:// URI prefix, but we don't
   4405         * need that anymore. Trim it off if it exists.
   4406         */
   4407         final String keyName;
   4408         if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) {
   4409             keyName = new String(
   4410                     oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length()));
   4411         } else {
   4412             keyName = oldPrivateKey;
   4413         }
   4414         config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName);
   4415 
   4416         mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY,
   4417                 config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""));
   4418 
   4419         mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY,
   4420                 config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""));
   4421 
   4422         mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY,
   4423                 config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""));
   4424 
   4425         // Remove old private_key string so we don't run this again.
   4426         mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE);
   4427 
   4428         return true;
   4429     }
   4430 
   4431     /** Migrate certs from global pool to wifi UID if not already done */
   4432     void migrateCerts(WifiEnterpriseConfig config) {
   4433         String client = config.getClientCertificateAlias();
   4434         // a valid client certificate is configured
   4435         if (!TextUtils.isEmpty(client)) {
   4436             if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) {
   4437                 mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1,
   4438                         Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
   4439                 mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1,
   4440                         Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
   4441             }
   4442         }
   4443 
   4444         String ca = config.getCaCertificateAlias();
   4445         // a valid ca certificate is configured
   4446         if (!TextUtils.isEmpty(ca)) {
   4447             if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) {
   4448                 mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1,
   4449                         Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
   4450             }
   4451         }
   4452     }
   4453 
   4454     private void readNetworkBitsetVariable(int netId, BitSet variable, String varName,
   4455             String[] strings) {
   4456         String value = mWifiNative.getNetworkVariable(netId, varName);
   4457         if (!TextUtils.isEmpty(value)) {
   4458             variable.clear();
   4459             String vals[] = value.split(" ");
   4460             for (String val : vals) {
   4461                 int index = lookupString(val, strings);
   4462                 if (0 <= index) {
   4463                     variable.set(index);
   4464                 }
   4465             }
   4466         }
   4467     }
   4468 }
   4469