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