1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.wifi; 18 19 import com.android.settings.R; 20 21 import android.content.Context; 22 import android.net.NetworkInfo; 23 import android.net.wifi.ScanResult; 24 import android.net.wifi.WifiConfiguration; 25 import android.net.wifi.WifiInfo; 26 import android.net.wifi.WifiConfiguration.AuthAlgorithm; 27 import android.net.wifi.WifiConfiguration.GroupCipher; 28 import android.net.wifi.WifiConfiguration.KeyMgmt; 29 import android.net.wifi.WifiConfiguration.PairwiseCipher; 30 import android.net.wifi.WifiConfiguration.Protocol; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 import android.text.TextUtils; 34 import android.util.Log; 35 36 public final class AccessPointState implements Comparable<AccessPointState>, Parcelable { 37 38 private static final String TAG = "AccessPointState"; 39 40 // Constants used for different security types 41 public static final String WPA2 = "WPA2"; 42 public static final String WPA = "WPA"; 43 public static final String WEP = "WEP"; 44 public static final String OPEN = "Open"; 45 46 /* For EAP Enterprise fields */ 47 public static final String WPA_EAP = "WPA-EAP"; 48 public static final String IEEE8021X = "IEEE8021X"; 49 50 public static final String[] EAP_METHOD = { "PEAP", "TLS", "TTLS" }; 51 52 /** String present in capabilities if the scan result is ad-hoc */ 53 private static final String ADHOC_CAPABILITY = "[IBSS]"; 54 /** String present in capabilities if the scan result is enterprise secured */ 55 private static final String ENTERPRISE_CAPABILITY = "-EAP-"; 56 57 public static final String BSSID_ANY = "any"; 58 public static final int NETWORK_ID_NOT_SET = -1; 59 /** This should be used with care! */ 60 static final int NETWORK_ID_ANY = -2; 61 62 public static final int MATCH_NONE = 0; 63 public static final int MATCH_WEAK = 1; 64 public static final int MATCH_STRONG = 2; 65 public static final int MATCH_EXACT = 3; 66 67 // Don't set these directly, use the setters. 68 public int networkId; 69 public int priority; 70 public boolean hiddenSsid; 71 public int linkSpeed; 72 public int ipAddress; 73 public String bssid; 74 public String ssid; 75 public int signal; 76 public boolean primary; 77 public boolean seen; 78 public boolean configured; 79 public NetworkInfo.DetailedState status; 80 public String security; 81 public boolean disabled; 82 83 /** 84 * Use this for sorting based on signal strength. It is a heavily-damped 85 * time-averaged weighted signal. 86 */ 87 private float signalForSorting = Float.MIN_VALUE; 88 89 private static final float DAMPING_FACTOR = 0.2f; 90 91 /** 92 * This will be a user entered password, and NOT taken from wpa_supplicant 93 * (since it would give us *) 94 */ 95 private String mPassword; 96 private boolean mConfigHadPassword; 97 98 public static final int WEP_PASSWORD_AUTO = 0; 99 public static final int WEP_PASSWORD_ASCII = 1; 100 public static final int WEP_PASSWORD_HEX = 2; 101 private int mWepPasswordType; 102 103 /* Enterprise Fields */ 104 public static final int IDENTITY = 0; 105 public static final int ANONYMOUS_IDENTITY = 1; 106 public static final int PRIVATE_KEY_PASSWD = 2; 107 public static final int CLIENT_CERT = 3; 108 public static final int CA_CERT = 4; 109 public static final int PRIVATE_KEY = 5; 110 public static final int MAX_ENTRPRISE_FIELD = 6; 111 private String mEnterpriseFields[] = new String[MAX_ENTRPRISE_FIELD]; 112 private String mEap; 113 private String mPhase2; 114 115 private Context mContext; 116 117 /** 118 * If > 0, don't refresh (changes are being batched), use 119 * {@link #blockRefresh()} and {@link #unblockRefresh()} only. 120 */ 121 private int mBlockRefresh; 122 /** 123 * This will be set by {@link #requestRefresh} and shouldn't be written to 124 * elsewhere. 125 */ 126 private boolean mNeedsRefresh; 127 128 private AccessPointStateCallback mCallback; 129 130 private StringBuilder mSummaryBuilder = new StringBuilder(); 131 132 interface AccessPointStateCallback { refreshAccessPointState()133 void refreshAccessPointState(); 134 } 135 AccessPointState(Context context)136 public AccessPointState(Context context) { 137 this(); 138 139 setContext(context); 140 } 141 AccessPointState()142 private AccessPointState() { 143 bssid = BSSID_ANY; 144 ssid = ""; 145 networkId = NETWORK_ID_NOT_SET; 146 hiddenSsid = false; 147 } 148 setContext(Context context)149 void setContext(Context context) { 150 mContext = context; 151 } 152 setNetworkId(int networkId)153 public void setNetworkId(int networkId) { 154 this.networkId = networkId; 155 } 156 setBssid(String bssid)157 public void setBssid(String bssid) { 158 if (bssid != null) { 159 // If the BSSID is a wildcard, do NOT let a specific BSSID replace it 160 if (!this.bssid.equals(BSSID_ANY)) { 161 this.bssid = bssid; 162 } 163 } 164 } 165 getWpaSupplicantBssid()166 private String getWpaSupplicantBssid() { 167 return bssid.equals(BSSID_ANY) ? null : bssid; 168 } 169 convertToQuotedString(String string)170 public static String convertToQuotedString(String string) { 171 if (TextUtils.isEmpty(string)) { 172 return ""; 173 } 174 175 final int lastPos = string.length() - 1; 176 if (lastPos < 0 || (string.charAt(0) == '"' && string.charAt(lastPos) == '"')) { 177 return string; 178 } 179 180 return "\"" + string + "\""; 181 } 182 setPrimary(boolean primary)183 public void setPrimary(boolean primary) { 184 if (this.primary != primary) { 185 this.primary = primary; 186 requestRefresh(); 187 } 188 } 189 setSeen(boolean seen)190 public void setSeen(boolean seen) { 191 if (this.seen != seen) { 192 this.seen = seen; 193 requestRefresh(); 194 } 195 } 196 setDisabled(boolean disabled)197 public void setDisabled(boolean disabled) { 198 if (this.disabled != disabled) { 199 this.disabled = disabled; 200 requestRefresh(); 201 } 202 } 203 setSignal(int signal)204 public void setSignal(int signal) { 205 206 if (signalForSorting == Float.MIN_VALUE) { 207 signalForSorting = signal; 208 } else { 209 signalForSorting = (DAMPING_FACTOR * signal) + ((1-DAMPING_FACTOR) * signalForSorting); 210 } 211 212 if (this.signal != signal) { 213 this.signal = signal; 214 requestRefresh(); 215 } 216 } 217 getHumanReadableSsid()218 public String getHumanReadableSsid() { 219 if (TextUtils.isEmpty(ssid)) { 220 return ""; 221 } 222 223 final int lastPos = ssid.length() - 1; 224 if (ssid.charAt(0) == '"' && ssid.charAt(lastPos) == '"') { 225 return ssid.substring(1, lastPos); 226 } 227 228 return ssid; 229 } 230 setSsid(String ssid)231 public void setSsid(String ssid) { 232 if (ssid != null) { 233 this.ssid = convertToQuotedString(ssid); 234 requestRefresh(); 235 } 236 } 237 setPriority(int priority)238 public void setPriority(int priority) { 239 if (this.priority != priority) { 240 this.priority = priority; 241 requestRefresh(); 242 } 243 } 244 setHiddenSsid(boolean hiddenSsid)245 public void setHiddenSsid(boolean hiddenSsid) { 246 if (this.hiddenSsid != hiddenSsid) { 247 this.hiddenSsid = hiddenSsid; 248 requestRefresh(); 249 } 250 } 251 setLinkSpeed(int linkSpeed)252 public void setLinkSpeed(int linkSpeed) { 253 if (this.linkSpeed != linkSpeed) { 254 this.linkSpeed = linkSpeed; 255 requestRefresh(); 256 } 257 } 258 setIpAddress(int address)259 public void setIpAddress(int address) { 260 if (ipAddress != address) { 261 ipAddress = address; 262 requestRefresh(); 263 } 264 } 265 setConfigured(boolean configured)266 public void setConfigured(boolean configured) { 267 if (this.configured != configured) { 268 this.configured = configured; 269 requestRefresh(); 270 } 271 } 272 setStatus(NetworkInfo.DetailedState status)273 public void setStatus(NetworkInfo.DetailedState status) { 274 if (this.status != status) { 275 this.status = status; 276 requestRefresh(); 277 } 278 } 279 isEnterprise()280 public boolean isEnterprise() { 281 return (WPA_EAP.equals(security) || 282 AccessPointState.IEEE8021X.equals(security)); 283 } 284 setSecurity(String security)285 public void setSecurity(String security) { 286 if (TextUtils.isEmpty(this.security) || !this.security.equals(security)) { 287 this.security = security; 288 requestRefresh(); 289 } 290 } 291 hasSecurity()292 public boolean hasSecurity() { 293 return security != null && !security.contains(OPEN); 294 } 295 getHumanReadableSecurity()296 public String getHumanReadableSecurity() { 297 if (security.equals(OPEN)) return mContext.getString(R.string.wifi_security_open); 298 else if (security.equals(WEP)) return mContext.getString(R.string.wifi_security_wep); 299 else if (security.equals(WPA)) return mContext.getString(R.string.wifi_security_wpa); 300 else if (security.equals(WPA2)) return mContext.getString(R.string.wifi_security_wpa2); 301 else if (security.equals(WPA_EAP)) return mContext.getString(R.string.wifi_security_wpa_eap); 302 else if (security.equals(IEEE8021X)) return mContext.getString(R.string.wifi_security_ieee8021x); 303 304 return mContext.getString(R.string.wifi_security_unknown); 305 } 306 updateFromScanResult(ScanResult scanResult)307 public void updateFromScanResult(ScanResult scanResult) { 308 blockRefresh(); 309 310 // We don't keep specific AP BSSIDs and instead leave that as wildcard 311 312 setSeen(true); 313 setSsid(scanResult.SSID); 314 if (networkId == NETWORK_ID_NOT_SET) { 315 // Since ScanResults don't cross-reference network ID, we set it as a wildcard 316 setNetworkId(NETWORK_ID_ANY); 317 } 318 setSignal(scanResult.level); 319 setSecurity(getScanResultSecurity(scanResult)); 320 unblockRefresh(); 321 } 322 323 /** 324 * @return The security of a given {@link ScanResult}. 325 */ getScanResultSecurity(ScanResult scanResult)326 public static String getScanResultSecurity(ScanResult scanResult) { 327 final String cap = scanResult.capabilities; 328 final String[] securityModes = { WEP, WPA, WPA2, WPA_EAP, IEEE8021X }; 329 for (int i = securityModes.length - 1; i >= 0; i--) { 330 if (cap.contains(securityModes[i])) { 331 return securityModes[i]; 332 } 333 } 334 335 return OPEN; 336 } 337 338 /** 339 * @return Whether the given ScanResult represents an adhoc network. 340 */ isAdhoc(ScanResult scanResult)341 public static boolean isAdhoc(ScanResult scanResult) { 342 return scanResult.capabilities.contains(ADHOC_CAPABILITY); 343 } 344 345 /** 346 * @return Whether the given ScanResult has enterprise security. 347 */ isEnterprise(ScanResult scanResult)348 public static boolean isEnterprise(ScanResult scanResult) { 349 return scanResult.capabilities.contains(ENTERPRISE_CAPABILITY); 350 } 351 updateFromWifiConfiguration(WifiConfiguration wifiConfig)352 public void updateFromWifiConfiguration(WifiConfiguration wifiConfig) { 353 if (wifiConfig != null) { 354 blockRefresh(); 355 setBssid(wifiConfig.BSSID); 356 setNetworkId(wifiConfig.networkId); 357 setPriority(wifiConfig.priority); 358 setHiddenSsid(wifiConfig.hiddenSSID); 359 setSsid(wifiConfig.SSID); 360 setConfigured(true); 361 setDisabled(wifiConfig.status == WifiConfiguration.Status.DISABLED); 362 parseWifiConfigurationSecurity(wifiConfig); 363 unblockRefresh(); 364 } 365 } 366 setPassword(String password)367 public void setPassword(String password) { 368 setPassword(password, WEP_PASSWORD_AUTO); 369 } 370 setPassword(String password, int wepPasswordType)371 public void setPassword(String password, int wepPasswordType) { 372 mPassword = password; 373 mWepPasswordType = wepPasswordType; 374 } 375 376 /* For Enterprise Fields */ setEnterpriseField(int field, String value)377 public void setEnterpriseField(int field, String value) { 378 if ((value != null) && (field >= 0) && (field < MAX_ENTRPRISE_FIELD)) { 379 this.mEnterpriseFields[field] = value; 380 requestRefresh(); 381 } 382 } 383 setPhase2(String phase2)384 public void setPhase2(String phase2) { 385 if (!TextUtils.isEmpty(phase2) && (!phase2.equals("None"))) { 386 mPhase2 = phase2; 387 } 388 } 389 getPhase2()390 public String getPhase2() { 391 return mPhase2; 392 } 393 setEap(int method)394 public void setEap(int method) { 395 mEap = EAP_METHOD[method]; 396 requestRefresh(); 397 } 398 getEap()399 public String getEap() { 400 return mEap; 401 } getEnterpriseField(int field)402 public String getEnterpriseField(int field) { 403 if(field >=0 && field < MAX_ENTRPRISE_FIELD) { 404 return mEnterpriseFields[field]; 405 } 406 return null; 407 } 408 hasPassword()409 public boolean hasPassword() { 410 return !TextUtils.isEmpty(mPassword) || mConfigHadPassword; 411 } 412 hasPassword(WifiConfiguration wifiConfig)413 private static boolean hasPassword(WifiConfiguration wifiConfig) { 414 return !TextUtils.isEmpty(wifiConfig.preSharedKey) 415 || !TextUtils.isEmpty(wifiConfig.wepKeys[0]) 416 || !TextUtils.isEmpty(wifiConfig.wepKeys[1]) 417 || !TextUtils.isEmpty(wifiConfig.wepKeys[2]) 418 || !TextUtils.isEmpty(wifiConfig.wepKeys[3]); 419 } 420 parseWifiConfigurationSecurity(WifiConfiguration wifiConfig)421 private void parseWifiConfigurationSecurity(WifiConfiguration wifiConfig) { 422 setSecurity(getWifiConfigurationSecurity(wifiConfig)); 423 mConfigHadPassword = hasPassword(wifiConfig); 424 } 425 426 /** 427 * @return The security of a given {@link WifiConfiguration}. 428 */ getWifiConfigurationSecurity(WifiConfiguration wifiConfig)429 public static String getWifiConfigurationSecurity(WifiConfiguration wifiConfig) { 430 431 if (wifiConfig.allowedKeyManagement.get(KeyMgmt.NONE)) { 432 // If we never set group ciphers, wpa_supplicant puts all of them. 433 // For open, we don't set group ciphers. 434 // For WEP, we specifically only set WEP40 and WEP104, so CCMP 435 // and TKIP should not be there. 436 if (!wifiConfig.allowedGroupCiphers.get(GroupCipher.CCMP) 437 && (wifiConfig.allowedGroupCiphers.get(GroupCipher.WEP40) 438 || wifiConfig.allowedGroupCiphers.get(GroupCipher.WEP104))) { 439 return WEP; 440 } else { 441 return OPEN; 442 } 443 } else if (wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_EAP)) { 444 return WPA_EAP; 445 } else if (wifiConfig.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 446 return IEEE8021X; 447 } else if (wifiConfig.allowedProtocols.get(Protocol.RSN)) { 448 return WPA2; 449 } else if (wifiConfig.allowedProtocols.get(Protocol.WPA)) { 450 return WPA; 451 } else { 452 Log.w(TAG, "Unknown security type from WifiConfiguration, falling back on open."); 453 return OPEN; 454 } 455 } 456 updateFromWifiInfo(WifiInfo wifiInfo, NetworkInfo.DetailedState state)457 public void updateFromWifiInfo(WifiInfo wifiInfo, NetworkInfo.DetailedState state) { 458 if (wifiInfo != null) { 459 blockRefresh(); 460 setBssid(wifiInfo.getBSSID()); 461 setLinkSpeed(wifiInfo.getLinkSpeed()); 462 setNetworkId(wifiInfo.getNetworkId()); 463 setIpAddress(wifiInfo.getIpAddress()); 464 setSsid(wifiInfo.getSSID()); 465 if (state != null) { 466 setStatus(state); 467 } 468 setHiddenSsid(wifiInfo.getHiddenSSID()); 469 unblockRefresh(); 470 } 471 } 472 473 /** 474 * @return Whether this AP can be connected to at the moment. 475 */ isConnectable()476 public boolean isConnectable() { 477 return !primary && seen; 478 } 479 480 /** 481 * @return Whether this AP can be forgotten at the moment. 482 */ isForgetable()483 public boolean isForgetable() { 484 return configured; 485 } 486 487 /** 488 * Updates the state as if it were never configured. 489 * <p> 490 * Note: This will not pass the forget call to the Wi-Fi API. 491 */ forget()492 public void forget() { 493 blockRefresh(); 494 setConfigured(false); 495 setNetworkId(NETWORK_ID_NOT_SET); 496 setPrimary(false); 497 setStatus(null); 498 setDisabled(false); 499 unblockRefresh(); 500 } 501 updateWifiConfiguration(WifiConfiguration config)502 public void updateWifiConfiguration(WifiConfiguration config) { 503 config.BSSID = getWpaSupplicantBssid(); 504 config.priority = priority; 505 config.hiddenSSID = hiddenSsid; 506 config.SSID = convertToQuotedString(ssid); 507 config.eap = mEap; 508 509 if (!TextUtils.isEmpty(mPhase2)) { 510 config.phase2 = convertToQuotedString("auth=" + mPhase2); 511 } else { 512 config.phase2 = null; 513 } 514 if (!TextUtils.isEmpty(mEnterpriseFields[IDENTITY])) { 515 config.identity = 516 convertToQuotedString(mEnterpriseFields[IDENTITY]); 517 } else { 518 config.identity = null; 519 } 520 if (!TextUtils.isEmpty(mEnterpriseFields[ANONYMOUS_IDENTITY])) { 521 config.anonymousIdentity = convertToQuotedString( 522 mEnterpriseFields[ANONYMOUS_IDENTITY]); 523 } else { 524 config.anonymousIdentity = null; 525 } 526 if (!TextUtils.isEmpty(mEnterpriseFields[CLIENT_CERT])) { 527 config.clientCert = convertToQuotedString( 528 mEnterpriseFields[CLIENT_CERT]); 529 } else { 530 config.clientCert = null; 531 } 532 if (!TextUtils.isEmpty(mEnterpriseFields[CA_CERT])) { 533 config.caCert = convertToQuotedString( 534 mEnterpriseFields[CA_CERT]); 535 } else { 536 config.caCert = null; 537 } 538 if (!TextUtils.isEmpty(mEnterpriseFields[PRIVATE_KEY])) { 539 config.privateKey = convertToQuotedString( 540 mEnterpriseFields[PRIVATE_KEY]); 541 } else { 542 config.privateKey = null; 543 } 544 if (!TextUtils.isEmpty(mEnterpriseFields[PRIVATE_KEY_PASSWD])) { 545 config.privateKeyPasswd = convertToQuotedString( 546 mEnterpriseFields[PRIVATE_KEY_PASSWD]); 547 } else { 548 config.privateKeyPasswd = null; 549 } 550 setupSecurity(config); 551 } 552 setupSecurity(WifiConfiguration config)553 private void setupSecurity(WifiConfiguration config) { 554 config.allowedAuthAlgorithms.clear(); 555 config.allowedGroupCiphers.clear(); 556 config.allowedKeyManagement.clear(); 557 config.allowedPairwiseCiphers.clear(); 558 config.allowedProtocols.clear(); 559 560 if (TextUtils.isEmpty(security)) { 561 security = OPEN; 562 Log.w(TAG, "Empty security, assuming open"); 563 } 564 565 if (security.equals(WEP)) { 566 567 // If password is empty, it should be left untouched 568 if (!TextUtils.isEmpty(mPassword)) { 569 if (mWepPasswordType == WEP_PASSWORD_AUTO) { 570 if (isHexWepKey(mPassword)) { 571 config.wepKeys[0] = mPassword; 572 } else { 573 config.wepKeys[0] = convertToQuotedString(mPassword); 574 } 575 } else { 576 config.wepKeys[0] = mWepPasswordType == WEP_PASSWORD_ASCII 577 ? convertToQuotedString(mPassword) 578 : mPassword; 579 } 580 } 581 582 config.wepTxKeyIndex = 0; 583 584 config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN); 585 config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED); 586 587 config.allowedKeyManagement.set(KeyMgmt.NONE); 588 589 config.allowedGroupCiphers.set(GroupCipher.WEP40); 590 config.allowedGroupCiphers.set(GroupCipher.WEP104); 591 592 } else if (security.equals(WPA) || security.equals(WPA2)){ 593 config.allowedGroupCiphers.set(GroupCipher.TKIP); 594 config.allowedGroupCiphers.set(GroupCipher.CCMP); 595 596 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); 597 598 config.allowedPairwiseCiphers.set(PairwiseCipher.CCMP); 599 config.allowedPairwiseCiphers.set(PairwiseCipher.TKIP); 600 601 config.allowedProtocols.set(security.equals(WPA2) ? Protocol.RSN : Protocol.WPA); 602 603 // If password is empty, it should be left untouched 604 if (!TextUtils.isEmpty(mPassword)) { 605 if (mPassword.length() == 64 && isHex(mPassword)) { 606 // Goes unquoted as hex 607 config.preSharedKey = mPassword; 608 } else { 609 // Goes quoted as ASCII 610 config.preSharedKey = convertToQuotedString(mPassword); 611 } 612 } 613 614 } else if (security.equals(OPEN)) { 615 config.allowedKeyManagement.set(KeyMgmt.NONE); 616 } else if (security.equals(WPA_EAP) || security.equals(IEEE8021X)) { 617 config.allowedGroupCiphers.set(GroupCipher.TKIP); 618 config.allowedGroupCiphers.set(GroupCipher.CCMP); 619 if (security.equals(WPA_EAP)) { 620 config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); 621 } else { 622 config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); 623 } 624 if (!TextUtils.isEmpty(mPassword)) { 625 config.password = convertToQuotedString(mPassword); 626 } 627 } 628 } 629 isHexWepKey(String wepKey)630 private static boolean isHexWepKey(String wepKey) { 631 final int len = wepKey.length(); 632 633 // WEP-40, WEP-104, and some vendors using 256-bit WEP (WEP-232?) 634 if (len != 10 && len != 26 && len != 58) { 635 return false; 636 } 637 638 return isHex(wepKey); 639 } 640 isHex(String key)641 private static boolean isHex(String key) { 642 for (int i = key.length() - 1; i >= 0; i--) { 643 final char c = key.charAt(i); 644 if (!(c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f')) { 645 return false; 646 } 647 } 648 649 return true; 650 } 651 setCallback(AccessPointStateCallback callback)652 public void setCallback(AccessPointStateCallback callback) { 653 mCallback = callback; 654 } 655 blockRefresh()656 void blockRefresh() { 657 mBlockRefresh++; 658 } 659 unblockRefresh()660 void unblockRefresh() { 661 if (--mBlockRefresh == 0 && mNeedsRefresh) { 662 requestRefresh(); 663 } 664 } 665 requestRefresh()666 private void requestRefresh() { 667 if (mBlockRefresh > 0) { 668 mNeedsRefresh = true; 669 return; 670 } 671 672 if (mCallback != null) { 673 mCallback.refreshAccessPointState(); 674 } 675 676 mNeedsRefresh = false; 677 } 678 679 /** 680 * {@inheritDoc} 681 * @see #hashCode() 682 * @see #equals(Object) 683 */ matches(int otherNetworkId, String otherBssid, String otherSsid, String otherSecurity)684 public int matches(int otherNetworkId, String otherBssid, String otherSsid, 685 String otherSecurity) { 686 687 // Whenever this method is touched, please ensure #equals and #hashCode 688 // still work with the changes here! 689 690 if (otherSsid == null) { 691 if (WifiLayer.LOGV) { 692 Log.w(TAG, "BSSID: " + otherBssid + ", SSID: " + otherSsid); 693 } 694 return MATCH_NONE; 695 } 696 697 /* 698 * If we both have 'security' set, it must match (an open network still 699 * has 'security' set to OPEN) 700 */ 701 if (security != null && otherSecurity != null) { 702 if (!security.equals(otherSecurity)) { 703 return MATCH_NONE; 704 } 705 } 706 707 // WifiConfiguration gives an empty bssid as a BSSID wildcard 708 if (TextUtils.isEmpty(otherBssid)) { 709 otherBssid = AccessPointState.BSSID_ANY; 710 } 711 712 final boolean networkIdMatches = networkId == otherNetworkId; 713 if (!networkIdMatches && networkId != NETWORK_ID_ANY && otherNetworkId != NETWORK_ID_ANY) { 714 // Network IDs don't match (e.g., 1 & 2 or unset & 1) and neither is a wildcard 715 return MATCH_NONE; 716 } 717 718 if (networkIdMatches && otherNetworkId != NETWORK_ID_NOT_SET 719 && otherNetworkId != NETWORK_ID_ANY) { 720 // Network ID matches (they're set to the same ID) 721 return MATCH_EXACT; 722 } 723 724 // So now, network IDs aren't set or at least one is a wildcard 725 726 final boolean bssidMatches = bssid.equals(otherBssid); 727 final boolean otherBssidIsWildcard = otherBssid.equals(BSSID_ANY); 728 if (bssidMatches && !otherBssidIsWildcard) { 729 // BSSID matches (and neither is a wildcard) 730 return MATCH_STRONG; 731 } 732 733 if (!bssidMatches && !bssid.equals(BSSID_ANY) && !otherBssidIsWildcard) { 734 // BSSIDs don't match (e.g., 00:24:21:21:42:12 & 42:12:44:21:22:52) 735 // and neither is a wildcard 736 return MATCH_NONE; 737 } 738 739 // So now, BSSIDs are both wildcards 740 741 final boolean ssidMatches = ssid.equals(otherSsid); 742 if (ssidMatches) { 743 // SSID matches 744 return MATCH_WEAK; 745 } 746 747 return MATCH_NONE; 748 } 749 750 /** 751 * {@inheritDoc} 752 * @see #matches(int, String, String) 753 * @see #equals(Object) 754 */ 755 @Override hashCode()756 public int hashCode() { 757 // Two equal() objects must have same hashCode. 758 // With Wi-Fi, the broadest match is if two SSIDs are the same. The finer-grained matches 759 // imply this (for example, the same network IDs means the same WifiConfiguration which 760 // means the same SSID). 761 // See #matches for the exact matching algorithm we use. 762 return ssid != null ? ssid.hashCode() : 0; 763 } 764 765 /** 766 * {@inheritDoc} 767 * @see #matches(int, String, String) 768 * @see #hashCode() 769 */ 770 @Override equals(Object o)771 public boolean equals(Object o) { 772 if (!o.getClass().equals(getClass())) { 773 return false; 774 } 775 776 final AccessPointState other = (AccessPointState) o; 777 778 // To see which conditions cause two AccessPointStates to be equal, see 779 // where #matches returns MATCH_WEAK or greater. 780 781 return matches(other.networkId, other.bssid, other.ssid, other.security) >= MATCH_WEAK; 782 } 783 matchesWifiConfiguration(WifiConfiguration wifiConfig)784 public int matchesWifiConfiguration(WifiConfiguration wifiConfig) { 785 String security = getWifiConfigurationSecurity(wifiConfig); 786 return matches(wifiConfig.networkId, wifiConfig.BSSID, wifiConfig.SSID, security); 787 } 788 getSummarizedStatus()789 String getSummarizedStatus() { 790 StringBuilder sb = mSummaryBuilder; 791 sb.delete(0, sb.length()); 792 793 if (primary && status != null) { 794 buildSummary(sb, WifiStatus.getPrintable(mContext, status), true); 795 796 } else if (!seen) { 797 buildSummary(sb, mContext.getString(R.string.summary_not_in_range), true); 798 799 // Remembered comes second in this case 800 if (!primary && configured) { 801 buildSummary(sb, mContext.getString(R.string.summary_remembered), true); 802 } 803 804 } else { 805 if (configured && disabled) { 806 // The connection failure overrides all in this case 807 return mContext.getString(R.string.summary_connection_failed); 808 } 809 810 // Remembered comes first in this case 811 if (!primary && configured) { 812 buildSummary(sb, mContext.getString(R.string.summary_remembered), true); 813 } 814 815 // If it is seen (and not the primary), show the security type 816 String verboseSecurity = getVerboseSecurity(); 817 if (verboseSecurity != null) { 818 buildSummary(sb, verboseSecurity, true); 819 } 820 } 821 822 return sb.toString(); 823 } 824 getVerboseSecurity()825 private String getVerboseSecurity() { 826 if (WEP.equals(security)) { 827 return mContext.getString(R.string.wifi_security_verbose_wep); 828 } else if (WPA.equals(security)) { 829 return mContext.getString(R.string.wifi_security_verbose_wpa); 830 } else if (WPA2.equals(security)) { 831 return mContext.getString(R.string.wifi_security_verbose_wpa2); 832 } else if (OPEN.equals(security)) { 833 return mContext.getString(R.string.wifi_security_verbose_open); 834 } else if (WPA_EAP.equals(security)) { 835 return mContext.getString(R.string.wifi_security_verbose_wpa_eap); 836 } else if (IEEE8021X.equals(security)) { 837 return mContext.getString(R.string.wifi_security_verbose_ieee8021x); 838 } else { 839 return null; 840 } 841 } 842 buildSummary(StringBuilder sb, String string, boolean autoUpperCaseFirstLetter)843 private void buildSummary(StringBuilder sb, String string, boolean autoUpperCaseFirstLetter) { 844 if (sb.length() == 0) { 845 if (autoUpperCaseFirstLetter && string.length() > 1 846 && Character.isLowerCase(string.charAt(0)) 847 && !Character.isUpperCase(string.charAt(1))) { 848 sb.append(Character.toUpperCase(string.charAt(0))).append(string, 1, 849 string.length()); 850 } else { 851 sb.append(string); 852 } 853 } else { 854 sb.append(", "); 855 sb.append(string); 856 } 857 } 858 compareTo(AccessPointState other)859 public int compareTo(AccessPointState other) { 860 // This ranks the states for displaying in the AP list, not for 861 // connecting to (wpa_supplicant does that using the WifiConfiguration's 862 // priority field). 863 864 // Clarity > efficiency, of this logic: 865 int comparison; 866 867 // Primary 868 comparison = (other.primary ? 1 : 0) - (primary ? 1 : 0); 869 if (comparison != 0) return comparison; 870 871 // Currently seen (similar to, but not always the same as within range) 872 comparison = (other.seen ? 1 : 0) - (seen ? 1 : 0); 873 if (comparison != 0) return comparison; 874 875 // Configured 876 comparison = (other.configured ? 1 : 0) - (configured ? 1 : 0); 877 if (comparison != 0) return comparison; 878 879 if (!configured) { 880 // Neither are configured 881 882 // Open network 883 comparison = (hasSecurity() ? 1 : 0) - (other.hasSecurity() ? 1 : 0); 884 if (comparison != 0) return comparison; 885 } 886 887 // Signal strength 888 comparison = (int) (other.signalForSorting - signalForSorting); 889 if (comparison != 0) return comparison; 890 891 // Alphabetical 892 return ssid.compareToIgnoreCase(other.ssid); 893 } 894 toString()895 public String toString() { 896 return ssid + " (" + bssid + ", " + networkId + ", " + super.toString() + ")"; 897 } 898 899 /** Implement the Parcelable interface */ writeToParcel(Parcel dest, int flags)900 public void writeToParcel(Parcel dest, int flags) { 901 dest.writeString(bssid); 902 dest.writeInt(configured ? 1 : 0); 903 dest.writeInt(ipAddress); 904 dest.writeInt(linkSpeed); 905 dest.writeInt(networkId); 906 dest.writeInt(primary ? 1 : 0); 907 dest.writeInt(priority); 908 dest.writeInt(hiddenSsid ? 1 : 0); 909 dest.writeString(security); 910 dest.writeInt(seen ? 1 : 0); 911 dest.writeInt(disabled ? 1 : 0); 912 dest.writeInt(signal); 913 dest.writeString(ssid); 914 dest.writeString(status != null ? status.toString() : null); 915 dest.writeString(mPassword); 916 dest.writeInt(mConfigHadPassword ? 1 : 0); 917 dest.writeInt(mWepPasswordType); 918 } 919 920 /** Implement the Parcelable interface */ describeContents()921 public int describeContents() { 922 return 0; 923 } 924 925 /** Implement the Parcelable interface */ 926 public static final Creator<AccessPointState> CREATOR = 927 new Creator<AccessPointState>() { 928 public AccessPointState createFromParcel(Parcel in) { 929 AccessPointState state = new AccessPointState(); 930 state.bssid = in.readString(); 931 state.configured = in.readInt() == 1; 932 state.ipAddress = in.readInt(); 933 state.linkSpeed = in.readInt(); 934 state.networkId = in.readInt(); 935 state.primary = in.readInt() == 1; 936 state.priority = in.readInt(); 937 state.hiddenSsid = in.readInt() == 1; 938 state.security = in.readString(); 939 state.seen = in.readInt() == 1; 940 state.disabled = in.readInt() == 1; 941 state.signal = in.readInt(); 942 state.ssid = in.readString(); 943 String statusStr = in.readString(); 944 if (statusStr != null) { 945 state.status = NetworkInfo.DetailedState.valueOf(statusStr); 946 } 947 state.mPassword = in.readString(); 948 state.mConfigHadPassword = in.readInt() == 1; 949 state.mWepPasswordType = in.readInt(); 950 return state; 951 } 952 953 public AccessPointState[] newArray(int size) { 954 return new AccessPointState[size]; 955 } 956 }; 957 958 959 } 960