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