AccessPoint.java revision 402be0628c3c27273e826344ec7b12d53f861152
1/* 2 * Copyright (C) 2015 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.settingslib.wifi; 18 19import android.content.Context; 20import android.net.ConnectivityManager; 21import android.net.Network; 22import android.net.NetworkCapabilities; 23import android.net.NetworkInfo; 24import android.net.NetworkInfo.DetailedState; 25import android.net.NetworkInfo.State; 26import android.net.wifi.IWifiManager; 27import android.net.wifi.ScanResult; 28import android.net.wifi.WifiConfiguration; 29import android.net.wifi.WifiConfiguration.KeyMgmt; 30import android.net.wifi.WifiInfo; 31import android.net.wifi.WifiManager; 32import android.os.Bundle; 33import android.text.TextUtils; 34import android.os.RemoteException; 35import android.os.ServiceManager; 36import android.util.Log; 37import android.util.LruCache; 38import android.content.pm.ApplicationInfo; 39import android.content.pm.IPackageManager; 40import android.content.pm.PackageManager; 41import android.os.UserHandle; 42import android.os.RemoteException; 43import android.app.AppGlobals; 44 45import com.android.settingslib.R; 46 47import java.util.Map; 48 49 50public class AccessPoint implements Comparable<AccessPoint> { 51 static final String TAG = "SettingsLib.AccessPoint"; 52 53 /** 54 * Lower bound on the 2.4 GHz (802.11b/g/n) WLAN channels 55 */ 56 public static final int LOWER_FREQ_24GHZ = 2400; 57 58 /** 59 * Upper bound on the 2.4 GHz (802.11b/g/n) WLAN channels 60 */ 61 public static final int HIGHER_FREQ_24GHZ = 2500; 62 63 /** 64 * Lower bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels 65 */ 66 public static final int LOWER_FREQ_5GHZ = 4900; 67 68 /** 69 * Upper bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels 70 */ 71 public static final int HIGHER_FREQ_5GHZ = 5900; 72 73 74 /** 75 * Experimental: we should be able to show the user the list of BSSIDs and bands 76 * for that SSID. 77 * For now this data is used only with Verbose Logging so as to show the band and number 78 * of BSSIDs on which that network is seen. 79 */ 80 public LruCache<String, ScanResult> mScanResultCache; 81 private static final String KEY_NETWORKINFO = "key_networkinfo"; 82 private static final String KEY_WIFIINFO = "key_wifiinfo"; 83 private static final String KEY_SCANRESULT = "key_scanresult"; 84 private static final String KEY_CONFIG = "key_config"; 85 86 /** 87 * These values are matched in string arrays -- changes must be kept in sync 88 */ 89 public static final int SECURITY_NONE = 0; 90 public static final int SECURITY_WEP = 1; 91 public static final int SECURITY_PSK = 2; 92 public static final int SECURITY_EAP = 3; 93 94 private static final int PSK_UNKNOWN = 0; 95 private static final int PSK_WPA = 1; 96 private static final int PSK_WPA2 = 2; 97 private static final int PSK_WPA_WPA2 = 3; 98 99 private static final int VISIBILITY_OUTDATED_AGE_IN_MILLI = 20000; 100 private final Context mContext; 101 102 private String ssid; 103 private int security; 104 private int networkId = WifiConfiguration.INVALID_NETWORK_ID; 105 106 private int pskType = PSK_UNKNOWN; 107 108 private WifiConfiguration mConfig; 109 private ScanResult mScanResult; 110 111 private int mRssi = Integer.MAX_VALUE; 112 private long mSeen = 0; 113 114 private WifiInfo mInfo; 115 private NetworkInfo mNetworkInfo; 116 private AccessPointListener mAccessPointListener; 117 118 private Object mTag; 119 120 public AccessPoint(Context context, Bundle savedState) { 121 mContext = context; 122 mConfig = savedState.getParcelable(KEY_CONFIG); 123 if (mConfig != null) { 124 loadConfig(mConfig); 125 } 126 mScanResult = (ScanResult) savedState.getParcelable(KEY_SCANRESULT); 127 if (mScanResult != null) { 128 loadResult(mScanResult); 129 } 130 mInfo = (WifiInfo) savedState.getParcelable(KEY_WIFIINFO); 131 if (savedState.containsKey(KEY_NETWORKINFO)) { 132 mNetworkInfo = savedState.getParcelable(KEY_NETWORKINFO); 133 } 134 update(mInfo, mNetworkInfo); 135 } 136 137 AccessPoint(Context context, ScanResult result) { 138 mContext = context; 139 loadResult(result); 140 } 141 142 AccessPoint(Context context, WifiConfiguration config) { 143 mContext = context; 144 loadConfig(config); 145 } 146 147 @Override 148 public int compareTo(AccessPoint other) { 149 // Active one goes first. 150 if (isActive() && !other.isActive()) return -1; 151 if (!isActive() && other.isActive()) return 1; 152 153 // Reachable one goes before unreachable one. 154 if (mRssi != Integer.MAX_VALUE && other.mRssi == Integer.MAX_VALUE) return -1; 155 if (mRssi == Integer.MAX_VALUE && other.mRssi != Integer.MAX_VALUE) return 1; 156 157 // Configured one goes before unconfigured one. 158 if (networkId != WifiConfiguration.INVALID_NETWORK_ID 159 && other.networkId == WifiConfiguration.INVALID_NETWORK_ID) return -1; 160 if (networkId == WifiConfiguration.INVALID_NETWORK_ID 161 && other.networkId != WifiConfiguration.INVALID_NETWORK_ID) return 1; 162 163 // Sort by signal strength. 164 int difference = WifiManager.compareSignalLevel(other.mRssi, mRssi); 165 if (difference != 0) { 166 return difference; 167 } 168 // Sort by ssid. 169 return ssid.compareToIgnoreCase(other.ssid); 170 } 171 172 @Override 173 public boolean equals(Object other) { 174 if (!(other instanceof AccessPoint)) return false; 175 return (this.compareTo((AccessPoint) other) == 0); 176 } 177 178 @Override 179 public int hashCode() { 180 int result = 0; 181 if (mInfo != null) result += 13 * mInfo.hashCode(); 182 result += 19 * mRssi; 183 result += 23 * networkId; 184 result += 29 * ssid.hashCode(); 185 return result; 186 } 187 188 @Override 189 public String toString() { 190 StringBuilder builder = new StringBuilder().append("AccessPoint(") 191 .append(ssid); 192 if (isSaved()) { 193 builder.append(',').append("saved"); 194 } 195 if (isActive()) { 196 builder.append(',').append("active"); 197 } 198 if (isEphemeral()) { 199 builder.append(',').append("ephemeral"); 200 } 201 if (isConnectable()) { 202 builder.append(',').append("connectable"); 203 } 204 if (security != SECURITY_NONE) { 205 builder.append(',').append(securityToString(security, pskType)); 206 } 207 return builder.append(')').toString(); 208 } 209 210 public boolean matches(ScanResult result) { 211 return ssid.equals(result.SSID) && security == getSecurity(result); 212 } 213 214 public boolean matches(WifiConfiguration config) { 215 if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) 216 return config.FQDN.equals(mConfig.providerFriendlyName); 217 else 218 return ssid.equals(removeDoubleQuotes(config.SSID)) && security == getSecurity(config); 219 } 220 221 public WifiConfiguration getConfig() { 222 return mConfig; 223 } 224 225 public void clearConfig() { 226 mConfig = null; 227 networkId = WifiConfiguration.INVALID_NETWORK_ID; 228 } 229 230 public WifiInfo getInfo() { 231 return mInfo; 232 } 233 234 public int getLevel() { 235 if (mRssi == Integer.MAX_VALUE) { 236 return -1; 237 } 238 return WifiManager.calculateSignalLevel(mRssi, 4); 239 } 240 241 public NetworkInfo getNetworkInfo() { 242 return mNetworkInfo; 243 } 244 245 public int getSecurity() { 246 return security; 247 } 248 249 public String getSecurityString(boolean concise) { 250 Context context = mContext; 251 if (mConfig != null && mConfig.isPasspoint()) { 252 return context.getString(R.string.wifi_security_passpoint); 253 } 254 switch(security) { 255 case SECURITY_EAP: 256 return concise ? context.getString(R.string.wifi_security_short_eap) : 257 context.getString(R.string.wifi_security_eap); 258 case SECURITY_PSK: 259 switch (pskType) { 260 case PSK_WPA: 261 return concise ? context.getString(R.string.wifi_security_short_wpa) : 262 context.getString(R.string.wifi_security_wpa); 263 case PSK_WPA2: 264 return concise ? context.getString(R.string.wifi_security_short_wpa2) : 265 context.getString(R.string.wifi_security_wpa2); 266 case PSK_WPA_WPA2: 267 return concise ? context.getString(R.string.wifi_security_short_wpa_wpa2) : 268 context.getString(R.string.wifi_security_wpa_wpa2); 269 case PSK_UNKNOWN: 270 default: 271 return concise ? context.getString(R.string.wifi_security_short_psk_generic) 272 : context.getString(R.string.wifi_security_psk_generic); 273 } 274 case SECURITY_WEP: 275 return concise ? context.getString(R.string.wifi_security_short_wep) : 276 context.getString(R.string.wifi_security_wep); 277 case SECURITY_NONE: 278 default: 279 return concise ? "" : context.getString(R.string.wifi_security_none); 280 } 281 } 282 283 public String getSsid() { 284 return ssid; 285 } 286 287 public String getConfigName() { 288 if (mConfig != null && mConfig.isPasspoint()) { 289 return mConfig.providerFriendlyName; 290 } else { 291 return ssid; 292 } 293 } 294 295 public DetailedState getDetailedState() { 296 return mNetworkInfo != null ? mNetworkInfo.getDetailedState() : null; 297 } 298 299 public String getSavedNetworkSummary() { 300 if (mConfig != null) { 301 PackageManager pm = mContext.getPackageManager(); 302 String systemName = pm.getNameForUid(android.os.Process.SYSTEM_UID); 303 int userId = UserHandle.getUserId(mConfig.creatorUid); 304 ApplicationInfo appInfo = null; 305 if (mConfig.creatorName != null && mConfig.creatorName.equals(systemName)) { 306 appInfo = mContext.getApplicationInfo(); 307 } else { 308 try { 309 IPackageManager ipm = AppGlobals.getPackageManager(); 310 appInfo = ipm.getApplicationInfo(mConfig.creatorName, 0 /* flags */, userId); 311 } catch (RemoteException rex) { 312 } 313 } 314 if (appInfo != null && 315 !appInfo.packageName.equals(mContext.getString(R.string.settings_package)) && 316 !appInfo.packageName.equals( 317 mContext.getString(R.string.certinstaller_package))) { 318 return mContext.getString(R.string.saved_network, appInfo.loadLabel(pm)); 319 } 320 } 321 return ""; 322 } 323 324 public String getSummary() { 325 return getSettingsSummary(); 326 } 327 328 public String getSettingsSummary() { 329 // Update to new summary 330 StringBuilder summary = new StringBuilder(); 331 332 if (isActive() && mConfig != null && mConfig.isPasspoint()) { 333 // This is the active connection on passpoint 334 summary.append(getSummary(mContext, getDetailedState(), 335 false, mConfig.providerFriendlyName)); 336 } else if (isActive()) { 337 // This is the active connection on non-passpoint network 338 summary.append(getSummary(mContext, getDetailedState(), 339 networkId == WifiConfiguration.INVALID_NETWORK_ID)); 340 } else if (mConfig != null && mConfig.isPasspoint()) { 341 String format = mContext.getString(R.string.available_via_passpoint); 342 summary.append(String.format(format, mConfig.providerFriendlyName)); 343 } else if (mConfig != null && mConfig.hasNoInternetAccess()) { 344 summary.append(mContext.getString(R.string.wifi_no_internet)); 345 } else if (mConfig != null && ((mConfig.status == WifiConfiguration.Status.DISABLED && 346 mConfig.disableReason != WifiConfiguration.DISABLED_UNKNOWN_REASON) 347 || mConfig.autoJoinStatus 348 >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)) { 349 if (mConfig.autoJoinStatus 350 >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) { 351 if (mConfig.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE) { 352 summary.append(mContext.getString(R.string.wifi_disabled_network_failure)); 353 } else if (mConfig.disableReason == WifiConfiguration.DISABLED_AUTH_FAILURE) { 354 summary.append(mContext.getString(R.string.wifi_disabled_password_failure)); 355 } else { 356 summary.append(mContext.getString(R.string.wifi_disabled_wifi_failure)); 357 } 358 } else { 359 switch (mConfig.disableReason) { 360 case WifiConfiguration.DISABLED_AUTH_FAILURE: 361 summary.append(mContext.getString(R.string.wifi_disabled_password_failure)); 362 break; 363 case WifiConfiguration.DISABLED_DHCP_FAILURE: 364 case WifiConfiguration.DISABLED_DNS_FAILURE: 365 summary.append(mContext.getString(R.string.wifi_disabled_network_failure)); 366 break; 367 case WifiConfiguration.DISABLED_UNKNOWN_REASON: 368 case WifiConfiguration.DISABLED_ASSOCIATION_REJECT: 369 summary.append(mContext.getString(R.string.wifi_disabled_generic)); 370 break; 371 } 372 } 373 } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range 374 summary.append(mContext.getString(R.string.wifi_not_in_range)); 375 } else { // In range, not disabled. 376 if (mConfig != null) { // Is saved network 377 summary.append(mContext.getString(R.string.wifi_remembered)); 378 } 379 } 380 381 if (WifiTracker.sVerboseLogging > 0) { 382 // Add RSSI/band information for this config, what was seen up to 6 seconds ago 383 // verbose WiFi Logging is only turned on thru developers settings 384 if (mInfo != null && mNetworkInfo != null) { // This is the active connection 385 summary.append(" f=" + Integer.toString(mInfo.getFrequency())); 386 } 387 summary.append(" " + getVisibilityStatus()); 388 if (mConfig != null && mConfig.autoJoinStatus > 0) { 389 summary.append(" (" + mConfig.autoJoinStatus); 390 if (mConfig.blackListTimestamp > 0) { 391 long now = System.currentTimeMillis(); 392 long diff = (now - mConfig.blackListTimestamp)/1000; 393 long sec = diff%60; //seconds 394 long min = (diff/60)%60; //minutes 395 long hour = (min/60)%60; //hours 396 summary.append(", "); 397 if (hour > 0) summary.append(Long.toString(hour) + "h "); 398 summary.append( Long.toString(min) + "m "); 399 summary.append( Long.toString(sec) + "s "); 400 } 401 summary.append(")"); 402 } 403 if (mConfig != null && mConfig.numIpConfigFailures > 0) { 404 summary.append(" ipf=").append(mConfig.numIpConfigFailures); 405 } 406 if (mConfig != null && mConfig.numConnectionFailures > 0) { 407 summary.append(" cf=").append(mConfig.numConnectionFailures); 408 } 409 if (mConfig != null && mConfig.numAuthFailures > 0) { 410 summary.append(" authf=").append(mConfig.numAuthFailures); 411 } 412 if (mConfig != null && mConfig.numNoInternetAccessReports > 0) { 413 summary.append(" noInt=").append(mConfig.numNoInternetAccessReports); 414 } 415 } 416 return summary.toString(); 417 } 418 419 /** 420 * Returns the visibility status of the WifiConfiguration. 421 * 422 * @return autojoin debugging information 423 * TODO: use a string formatter 424 * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"] 425 * For instance [-40,5/-30,2] 426 */ 427 private String getVisibilityStatus() { 428 StringBuilder visibility = new StringBuilder(); 429 StringBuilder scans24GHz = null; 430 StringBuilder scans5GHz = null; 431 String bssid = null; 432 433 long now = System.currentTimeMillis(); 434 435 if (mInfo != null) { 436 bssid = mInfo.getBSSID(); 437 if (bssid != null) { 438 visibility.append(" ").append(bssid); 439 } 440 visibility.append(" rssi=").append(mInfo.getRssi()); 441 visibility.append(" "); 442 visibility.append(" score=").append(mInfo.score); 443 visibility.append(String.format(" tx=%.1f,", mInfo.txSuccessRate)); 444 visibility.append(String.format("%.1f,", mInfo.txRetriesRate)); 445 visibility.append(String.format("%.1f ", mInfo.txBadRate)); 446 visibility.append(String.format("rx=%.1f", mInfo.rxSuccessRate)); 447 } 448 449 if (mScanResultCache != null) { 450 int rssi5 = WifiConfiguration.INVALID_RSSI; 451 int rssi24 = WifiConfiguration.INVALID_RSSI; 452 int num5 = 0; 453 int num24 = 0; 454 int numBlackListed = 0; 455 int n24 = 0; // Number scan results we included in the string 456 int n5 = 0; // Number scan results we included in the string 457 Map<String, ScanResult> list = mScanResultCache.snapshot(); 458 // TODO: sort list by RSSI or age 459 for (ScanResult result : list.values()) { 460 if (result.seen == 0) 461 continue; 462 463 if (result.autoJoinStatus != ScanResult.ENABLED) numBlackListed++; 464 465 if (result.frequency >= LOWER_FREQ_5GHZ 466 && result.frequency <= HIGHER_FREQ_5GHZ) { 467 // Strictly speaking: [4915, 5825] 468 // number of known BSSID on 5GHz band 469 num5 = num5 + 1; 470 } else if (result.frequency >= LOWER_FREQ_24GHZ 471 && result.frequency <= HIGHER_FREQ_24GHZ) { 472 // Strictly speaking: [2412, 2482] 473 // number of known BSSID on 2.4Ghz band 474 num24 = num24 + 1; 475 } 476 477 // Ignore results seen, older than 20 seconds 478 if (now - result.seen > VISIBILITY_OUTDATED_AGE_IN_MILLI) continue; 479 480 if (result.frequency >= LOWER_FREQ_5GHZ 481 && result.frequency <= HIGHER_FREQ_5GHZ) { 482 if (result.level > rssi5) { 483 rssi5 = result.level; 484 } 485 if (n5 < 4) { 486 if (scans5GHz == null) scans5GHz = new StringBuilder(); 487 scans5GHz.append(" \n{").append(result.BSSID); 488 if (bssid != null && result.BSSID.equals(bssid)) scans5GHz.append("*"); 489 scans5GHz.append("=").append(result.frequency); 490 scans5GHz.append(",").append(result.level); 491 if (result.autoJoinStatus != 0) { 492 scans5GHz.append(",st=").append(result.autoJoinStatus); 493 } 494 if (result.numIpConfigFailures != 0) { 495 scans5GHz.append(",ipf=").append(result.numIpConfigFailures); 496 } 497 scans5GHz.append("}"); 498 n5++; 499 } 500 } else if (result.frequency >= LOWER_FREQ_24GHZ 501 && result.frequency <= HIGHER_FREQ_24GHZ) { 502 if (result.level > rssi24) { 503 rssi24 = result.level; 504 } 505 if (n24 < 4) { 506 if (scans24GHz == null) scans24GHz = new StringBuilder(); 507 scans24GHz.append(" \n{").append(result.BSSID); 508 if (bssid != null && result.BSSID.equals(bssid)) scans24GHz.append("*"); 509 scans24GHz.append("=").append(result.frequency); 510 scans24GHz.append(",").append(result.level); 511 if (result.autoJoinStatus != 0) { 512 scans24GHz.append(",st=").append(result.autoJoinStatus); 513 } 514 if (result.numIpConfigFailures != 0) { 515 scans24GHz.append(",ipf=").append(result.numIpConfigFailures); 516 } 517 scans24GHz.append("}"); 518 n24++; 519 } 520 } 521 } 522 visibility.append(" ["); 523 if (num24 > 0) { 524 visibility.append("(").append(num24).append(")"); 525 if (n24 <= 4) { 526 if (scans24GHz != null) { 527 visibility.append(scans24GHz.toString()); 528 } 529 } else { 530 visibility.append("max=").append(rssi24); 531 if (scans24GHz != null) { 532 visibility.append(",").append(scans24GHz.toString()); 533 } 534 } 535 } 536 visibility.append(";"); 537 if (num5 > 0) { 538 visibility.append("(").append(num5).append(")"); 539 if (n5 <= 4) { 540 if (scans5GHz != null) { 541 visibility.append(scans5GHz.toString()); 542 } 543 } else { 544 visibility.append("max=").append(rssi5); 545 if (scans5GHz != null) { 546 visibility.append(",").append(scans5GHz.toString()); 547 } 548 } 549 } 550 if (numBlackListed > 0) 551 visibility.append("!").append(numBlackListed); 552 visibility.append("]"); 553 } else { 554 if (mRssi != Integer.MAX_VALUE) { 555 visibility.append(" rssi="); 556 visibility.append(mRssi); 557 if (mScanResult != null) { 558 visibility.append(", f="); 559 visibility.append(mScanResult.frequency); 560 } 561 } 562 } 563 564 return visibility.toString(); 565 } 566 567 /** 568 * Return whether this is the active connection. 569 * For ephemeral connections (networkId is invalid), this returns false if the network is 570 * disconnected. 571 */ 572 public boolean isActive() { 573 return mNetworkInfo != null && 574 (networkId != WifiConfiguration.INVALID_NETWORK_ID || 575 mNetworkInfo.getState() != State.DISCONNECTED); 576 } 577 578 public boolean isConnectable() { 579 return getLevel() != -1 && getDetailedState() == null; 580 } 581 582 public boolean isEphemeral() { 583 return !isSaved() && mNetworkInfo != null && mNetworkInfo.getState() != State.DISCONNECTED; 584 } 585 586 /** Return whether the given {@link WifiInfo} is for this access point. */ 587 private boolean isInfoForThisAccessPoint(WifiInfo info) { 588 if (networkId != WifiConfiguration.INVALID_NETWORK_ID) { 589 return networkId == info.getNetworkId(); 590 } else { 591 // Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID. 592 // (Note that we only do this if the WifiConfiguration explicitly equals INVALID). 593 // TODO: Handle hex string SSIDs. 594 return ssid.equals(removeDoubleQuotes(info.getSSID())); 595 } 596 } 597 598 public boolean isSaved() { 599 return networkId != WifiConfiguration.INVALID_NETWORK_ID; 600 } 601 602 public Object getTag() { 603 return mTag; 604 } 605 606 public void setTag(Object tag) { 607 mTag = tag; 608 } 609 610 /** 611 * Generate and save a default wifiConfiguration with common values. 612 * Can only be called for unsecured networks. 613 */ 614 public void generateOpenNetworkConfig() { 615 if (security != SECURITY_NONE) 616 throw new IllegalStateException(); 617 if (mConfig != null) 618 return; 619 mConfig = new WifiConfiguration(); 620 mConfig.SSID = AccessPoint.convertToQuotedString(ssid); 621 mConfig.allowedKeyManagement.set(KeyMgmt.NONE); 622 } 623 624 void loadConfig(WifiConfiguration config) { 625 if (config.isPasspoint()) 626 ssid = config.providerFriendlyName; 627 else 628 ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); 629 630 security = getSecurity(config); 631 networkId = config.networkId; 632 mConfig = config; 633 } 634 635 private void loadResult(ScanResult result) { 636 ssid = result.SSID; 637 security = getSecurity(result); 638 if (security == SECURITY_PSK) 639 pskType = getPskType(result); 640 mRssi = result.level; 641 mScanResult = result; 642 if (result.seen > mSeen) { 643 mSeen = result.seen; 644 } 645 } 646 647 public void saveWifiState(Bundle savedState) { 648 savedState.putParcelable(KEY_CONFIG, mConfig); 649 savedState.putParcelable(KEY_SCANRESULT, mScanResult); 650 savedState.putParcelable(KEY_WIFIINFO, mInfo); 651 if (mNetworkInfo != null) { 652 savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo); 653 } 654 } 655 656 public void setListener(AccessPointListener listener) { 657 mAccessPointListener = listener; 658 } 659 660 boolean update(ScanResult result) { 661 if (result.seen > mSeen) { 662 mSeen = result.seen; 663 } 664 if (WifiTracker.sVerboseLogging > 0) { 665 if (mScanResultCache == null) { 666 mScanResultCache = new LruCache<String, ScanResult>(32); 667 } 668 mScanResultCache.put(result.BSSID, result); 669 } 670 671 if (ssid.equals(result.SSID) && security == getSecurity(result)) { 672 if (WifiManager.compareSignalLevel(result.level, mRssi) > 0) { 673 int oldLevel = getLevel(); 674 mRssi = result.level; 675 if (getLevel() != oldLevel && mAccessPointListener != null) { 676 mAccessPointListener.onLevelChanged(this); 677 } 678 } 679 // This flag only comes from scans, is not easily saved in config 680 if (security == SECURITY_PSK) { 681 pskType = getPskType(result); 682 } 683 mScanResult = result; 684 if (mAccessPointListener != null) { 685 mAccessPointListener.onAccessPointChanged(this); 686 } 687 return true; 688 } 689 return false; 690 } 691 692 boolean update(WifiInfo info, NetworkInfo networkInfo) { 693 boolean reorder = false; 694 if (info != null && isInfoForThisAccessPoint(info)) { 695 reorder = (mInfo == null); 696 mRssi = info.getRssi(); 697 mInfo = info; 698 mNetworkInfo = networkInfo; 699 if (mAccessPointListener != null) { 700 mAccessPointListener.onAccessPointChanged(this); 701 } 702 } else if (mInfo != null) { 703 reorder = true; 704 mInfo = null; 705 mNetworkInfo = null; 706 if (mAccessPointListener != null) { 707 mAccessPointListener.onAccessPointChanged(this); 708 } 709 } 710 return reorder; 711 } 712 713 void update(WifiConfiguration config) { 714 mConfig = config; 715 networkId = config.networkId; 716 if (mAccessPointListener != null) { 717 mAccessPointListener.onAccessPointChanged(this); 718 } 719 } 720 721 public static String getSummary(Context context, String ssid, DetailedState state, 722 boolean isEphemeral, String passpointProvider) { 723 if (state == DetailedState.CONNECTED && ssid == null) { 724 if (TextUtils.isEmpty(passpointProvider) == false) { 725 // Special case for connected + passpoint networks. 726 String format = context.getString(R.string.connected_via_passpoint); 727 return String.format(format, passpointProvider); 728 } else if (isEphemeral) { 729 // Special case for connected + ephemeral networks. 730 return context.getString(R.string.connected_via_wfa); 731 } 732 } 733 734 // Case when there is wifi connected without internet connectivity. 735 final ConnectivityManager cm = (ConnectivityManager) 736 context.getSystemService(Context.CONNECTIVITY_SERVICE); 737 if (state == DetailedState.CONNECTED) { 738 IWifiManager wifiManager = IWifiManager.Stub.asInterface( 739 ServiceManager.getService(Context.WIFI_SERVICE)); 740 Network nw; 741 742 try { 743 nw = wifiManager.getCurrentNetwork(); 744 } catch (RemoteException e) { 745 nw = null; 746 } 747 NetworkCapabilities nc = cm.getNetworkCapabilities(nw); 748 if (nc != null && !nc.hasCapability(nc.NET_CAPABILITY_VALIDATED)) { 749 return context.getString(R.string.wifi_connected_no_internet); 750 } 751 } 752 753 String[] formats = context.getResources().getStringArray((ssid == null) 754 ? R.array.wifi_status : R.array.wifi_status_with_ssid); 755 int index = state.ordinal(); 756 757 if (index >= formats.length || formats[index].length() == 0) { 758 return null; 759 } 760 return String.format(formats[index], ssid); 761 } 762 763 public static String getSummary(Context context, DetailedState state, boolean isEphemeral) { 764 return getSummary(context, null, state, isEphemeral, null); 765 } 766 767 public static String getSummary(Context context, DetailedState state, boolean isEphemeral, 768 String passpointProvider) { 769 return getSummary(context, null, state, isEphemeral, passpointProvider); 770 } 771 772 public static String convertToQuotedString(String string) { 773 return "\"" + string + "\""; 774 } 775 776 private static int getPskType(ScanResult result) { 777 boolean wpa = result.capabilities.contains("WPA-PSK"); 778 boolean wpa2 = result.capabilities.contains("WPA2-PSK"); 779 if (wpa2 && wpa) { 780 return PSK_WPA_WPA2; 781 } else if (wpa2) { 782 return PSK_WPA2; 783 } else if (wpa) { 784 return PSK_WPA; 785 } else { 786 Log.w(TAG, "Received abnormal flag string: " + result.capabilities); 787 return PSK_UNKNOWN; 788 } 789 } 790 791 private static int getSecurity(ScanResult result) { 792 if (result.capabilities.contains("WEP")) { 793 return SECURITY_WEP; 794 } else if (result.capabilities.contains("PSK")) { 795 return SECURITY_PSK; 796 } else if (result.capabilities.contains("EAP")) { 797 return SECURITY_EAP; 798 } 799 return SECURITY_NONE; 800 } 801 802 static int getSecurity(WifiConfiguration config) { 803 if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 804 return SECURITY_PSK; 805 } 806 if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) || 807 config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 808 return SECURITY_EAP; 809 } 810 return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE; 811 } 812 813 public static String securityToString(int security, int pskType) { 814 if (security == SECURITY_WEP) { 815 return "WEP"; 816 } else if (security == SECURITY_PSK) { 817 if (pskType == PSK_WPA) { 818 return "WPA"; 819 } else if (pskType == PSK_WPA2) { 820 return "WPA2"; 821 } else if (pskType == PSK_WPA_WPA2) { 822 return "WPA_WPA2"; 823 } 824 return "PSK"; 825 } else if (security == SECURITY_EAP) { 826 return "EAP"; 827 } 828 return "NONE"; 829 } 830 831 static String removeDoubleQuotes(String string) { 832 if (TextUtils.isEmpty(string)) { 833 return ""; 834 } 835 int length = string.length(); 836 if ((length > 1) && (string.charAt(0) == '"') 837 && (string.charAt(length - 1) == '"')) { 838 return string.substring(1, length - 1); 839 } 840 return string; 841 } 842 843 public interface AccessPointListener { 844 void onAccessPointChanged(AccessPoint accessPoint); 845 void onLevelChanged(AccessPoint accessPoint); 846 } 847} 848