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