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 17 package com.android.settingslib.wifi; 18 19 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED; 20 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED; 21 22 import android.annotation.IntDef; 23 import android.annotation.MainThread; 24 import android.app.ActivityManager; 25 import android.app.AppGlobals; 26 import android.content.Context; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.IPackageManager; 29 import android.content.pm.PackageManager; 30 import android.net.ConnectivityManager; 31 import android.net.NetworkCapabilities; 32 import android.net.NetworkInfo; 33 import android.net.NetworkInfo.DetailedState; 34 import android.net.NetworkInfo.State; 35 import android.net.NetworkKey; 36 import android.net.NetworkScoreManager; 37 import android.net.NetworkScorerAppData; 38 import android.net.ScoredNetwork; 39 import android.net.wifi.ScanResult; 40 import android.net.wifi.WifiConfiguration; 41 import android.net.wifi.WifiConfiguration.KeyMgmt; 42 import android.net.wifi.WifiInfo; 43 import android.net.wifi.WifiManager; 44 import android.net.wifi.WifiNetworkScoreCache; 45 import android.net.wifi.hotspot2.OsuProvider; 46 import android.net.wifi.hotspot2.PasspointConfiguration; 47 import android.net.wifi.hotspot2.ProvisioningCallback; 48 import android.os.Bundle; 49 import android.os.Parcelable; 50 import android.os.RemoteException; 51 import android.os.SystemClock; 52 import android.os.UserHandle; 53 import android.provider.Settings; 54 import android.text.TextUtils; 55 import android.util.ArraySet; 56 import android.util.Log; 57 import android.util.Pair; 58 59 import androidx.annotation.GuardedBy; 60 import androidx.annotation.NonNull; 61 import androidx.annotation.Nullable; 62 63 import com.android.internal.annotations.VisibleForTesting; 64 import com.android.internal.util.CollectionUtils; 65 import com.android.settingslib.R; 66 import com.android.settingslib.utils.ThreadUtils; 67 68 import java.lang.annotation.Retention; 69 import java.lang.annotation.RetentionPolicy; 70 import java.util.ArrayList; 71 import java.util.Collection; 72 import java.util.Collections; 73 import java.util.HashMap; 74 import java.util.Iterator; 75 import java.util.List; 76 import java.util.Map; 77 import java.util.Set; 78 import java.util.concurrent.atomic.AtomicInteger; 79 80 /** 81 * Represents a selectable Wifi Network for use in various wifi selection menus backed by 82 * {@link WifiTracker}. 83 * 84 * <p>An AccessPoint, which would be more fittingly named "WifiNetwork", is an aggregation of 85 * {@link ScanResult ScanResults} along with pertinent metadata (e.g. current connection info, 86 * network scores) required to successfully render the network to the user. 87 * 88 * @deprecated WifiTracker/AccessPoint is no longer supported, and will be removed in a future 89 * release. Clients that need a dynamic list of available wifi networks should migrate to one of the 90 * newer tracker classes, 91 * {@link com.android.wifitrackerlib.WifiPickerTracker}, 92 * {@link com.android.wifitrackerlib.SavedNetworkTracker}, 93 * {@link com.android.wifitrackerlib.NetworkDetailsTracker}, 94 * in conjunction with {@link com.android.wifitrackerlib.WifiEntry} to represent each wifi network. 95 */ 96 @Deprecated 97 public class AccessPoint implements Comparable<AccessPoint> { 98 static final String TAG = "SettingsLib.AccessPoint"; 99 100 /** 101 * Lower bound on the 2.4 GHz (802.11b/g/n) WLAN channels 102 */ 103 public static final int LOWER_FREQ_24GHZ = 2400; 104 105 /** 106 * Upper bound on the 2.4 GHz (802.11b/g/n) WLAN channels 107 */ 108 public static final int HIGHER_FREQ_24GHZ = 2500; 109 110 /** 111 * Lower bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels 112 */ 113 public static final int LOWER_FREQ_5GHZ = 4900; 114 115 /** 116 * Upper bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels 117 */ 118 public static final int HIGHER_FREQ_5GHZ = 5900; 119 120 /** 121 * Lower bound on the 60 GHz (802.11ad) WIGIG channels 122 */ 123 public static final int LOWER_FREQ_60GHZ = 58320; 124 125 /** 126 * Upper bound on the 60 GHz (802.11ad) WIGIG channels 127 */ 128 public static final int HIGHER_FREQ_60GHZ = 70200; 129 130 /** The key which identifies this AccessPoint grouping. */ 131 private String mKey; 132 133 /** 134 * Synchronization lock for managing concurrency between main and worker threads. 135 * 136 * <p>This lock should be held for all modifications to {@link #mScanResults} and 137 * {@link #mExtraScanResults}. 138 */ 139 private final Object mLock = new Object(); 140 141 @IntDef({Speed.NONE, Speed.SLOW, Speed.MODERATE, Speed.FAST, Speed.VERY_FAST}) 142 @Retention(RetentionPolicy.SOURCE) 143 public @interface Speed { 144 /** 145 * Constant value representing an unlabeled / unscored network. 146 */ 147 int NONE = 0; 148 /** 149 * Constant value representing a slow speed network connection. 150 */ 151 int SLOW = 5; 152 /** 153 * Constant value representing a medium speed network connection. 154 */ 155 int MODERATE = 10; 156 /** 157 * Constant value representing a fast speed network connection. 158 */ 159 int FAST = 20; 160 /** 161 * Constant value representing a very fast speed network connection. 162 */ 163 int VERY_FAST = 30; 164 } 165 166 @IntDef({PasspointConfigurationVersion.INVALID, 167 PasspointConfigurationVersion.NO_OSU_PROVISIONED, 168 PasspointConfigurationVersion.OSU_PROVISIONED}) 169 @Retention(RetentionPolicy.SOURCE) 170 public @interface PasspointConfigurationVersion { 171 int INVALID = 0; 172 int NO_OSU_PROVISIONED = 1; // R1. 173 int OSU_PROVISIONED = 2; // R2 or R3. 174 } 175 176 /** The underlying set of scan results comprising this AccessPoint. */ 177 @GuardedBy("mLock") 178 private final ArraySet<ScanResult> mScanResults = new ArraySet<>(); 179 180 /** 181 * Extra set of unused scan results corresponding to this AccessPoint for verbose logging 182 * purposes, such as a set of Passpoint roaming scan results when home scans are available. 183 */ 184 @GuardedBy("mLock") 185 private final ArraySet<ScanResult> mExtraScanResults = new ArraySet<>(); 186 187 /** 188 * Map of BSSIDs to scored networks for individual bssids. 189 * 190 * <p>This cache should not be evicted with scan results, as the values here are used to 191 * generate a fallback in the absence of scores for the visible APs. 192 */ 193 private final Map<String, TimestampedScoredNetwork> mScoredNetworkCache = new HashMap<>(); 194 195 static final String KEY_NETWORKINFO = "key_networkinfo"; 196 static final String KEY_WIFIINFO = "key_wifiinfo"; 197 static final String KEY_SSID = "key_ssid"; 198 static final String KEY_SECURITY = "key_security"; 199 static final String KEY_SPEED = "key_speed"; 200 static final String KEY_PSKTYPE = "key_psktype"; 201 static final String KEY_SCANRESULTS = "key_scanresults"; 202 static final String KEY_SCOREDNETWORKCACHE = "key_scorednetworkcache"; 203 static final String KEY_CONFIG = "key_config"; 204 static final String KEY_PASSPOINT_UNIQUE_ID = "key_passpoint_unique_id"; 205 static final String KEY_FQDN = "key_fqdn"; 206 static final String KEY_PROVIDER_FRIENDLY_NAME = "key_provider_friendly_name"; 207 static final String KEY_EAPTYPE = "eap_psktype"; 208 static final String KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS = 209 "key_subscription_expiration_time_in_millis"; 210 static final String KEY_PASSPOINT_CONFIGURATION_VERSION = "key_passpoint_configuration_version"; 211 static final String KEY_IS_PSK_SAE_TRANSITION_MODE = "key_is_psk_sae_transition_mode"; 212 static final String KEY_IS_OWE_TRANSITION_MODE = "key_is_owe_transition_mode"; 213 static final AtomicInteger sLastId = new AtomicInteger(0); 214 215 /* 216 * NOTE: These constants for security and PSK types are saved to the bundle in saveWifiState, 217 * and sent across IPC. The numeric values should remain stable, otherwise the changes will need 218 * to be synced with other unbundled users of this library. 219 */ 220 public static final int SECURITY_NONE = 0; 221 public static final int SECURITY_WEP = 1; 222 public static final int SECURITY_PSK = 2; 223 public static final int SECURITY_EAP = 3; 224 public static final int SECURITY_OWE = 4; 225 public static final int SECURITY_SAE = 5; 226 public static final int SECURITY_EAP_SUITE_B = 6; 227 public static final int SECURITY_EAP_WPA3_ENTERPRISE = 7; 228 public static final int SECURITY_MAX_VAL = 8; // Has to be the last 229 230 private static final int PSK_UNKNOWN = 0; 231 private static final int PSK_WPA = 1; 232 private static final int PSK_WPA2 = 2; 233 private static final int PSK_WPA_WPA2 = 3; 234 235 private static final int EAP_UNKNOWN = 0; 236 private static final int EAP_WPA = 1; // WPA-EAP 237 private static final int EAP_WPA2_WPA3 = 2; // RSN-EAP 238 239 public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE; 240 241 public static final String KEY_PREFIX_AP = "AP:"; 242 public static final String KEY_PREFIX_PASSPOINT_UNIQUE_ID = "PASSPOINT:"; 243 public static final String KEY_PREFIX_OSU = "OSU:"; 244 245 private final Context mContext; 246 247 private WifiManager mWifiManager; 248 private WifiManager.ActionListener mConnectListener; 249 250 private String ssid; 251 private String bssid; 252 private int security; 253 private int networkId = WifiConfiguration.INVALID_NETWORK_ID; 254 255 private int pskType = PSK_UNKNOWN; 256 private int mEapType = EAP_UNKNOWN; 257 258 private WifiConfiguration mConfig; 259 260 private int mRssi = UNREACHABLE_RSSI; 261 262 private WifiInfo mInfo; 263 private NetworkInfo mNetworkInfo; 264 AccessPointListener mAccessPointListener; 265 266 private Object mTag; 267 268 @Speed private int mSpeed = Speed.NONE; 269 private boolean mIsScoredNetworkMetered = false; 270 271 /** 272 * Information associated with the {@link PasspointConfiguration}. Only maintaining 273 * the relevant info to preserve spaces. 274 */ 275 private String mPasspointUniqueId; 276 private String mFqdn; 277 private String mProviderFriendlyName; 278 private boolean mIsRoaming = false; 279 private long mSubscriptionExpirationTimeInMillis; 280 @PasspointConfigurationVersion private int mPasspointConfigurationVersion = 281 PasspointConfigurationVersion.INVALID; 282 283 private OsuProvider mOsuProvider; 284 285 private String mOsuStatus; 286 private String mOsuFailure; 287 private boolean mOsuProvisioningComplete = false; 288 289 private boolean mIsPskSaeTransitionMode = false; 290 private boolean mIsOweTransitionMode = false; 291 AccessPoint(Context context, Bundle savedState)292 public AccessPoint(Context context, Bundle savedState) { 293 mContext = context; 294 295 if (savedState.containsKey(KEY_CONFIG)) { 296 mConfig = savedState.getParcelable(KEY_CONFIG); 297 } 298 if (mConfig != null) { 299 loadConfig(mConfig); 300 } 301 if (savedState.containsKey(KEY_SSID)) { 302 ssid = savedState.getString(KEY_SSID); 303 } 304 if (savedState.containsKey(KEY_SECURITY)) { 305 security = savedState.getInt(KEY_SECURITY); 306 } 307 if (savedState.containsKey(KEY_SPEED)) { 308 mSpeed = savedState.getInt(KEY_SPEED); 309 } 310 if (savedState.containsKey(KEY_PSKTYPE)) { 311 pskType = savedState.getInt(KEY_PSKTYPE); 312 } 313 if (savedState.containsKey(KEY_EAPTYPE)) { 314 mEapType = savedState.getInt(KEY_EAPTYPE); 315 } 316 mInfo = savedState.getParcelable(KEY_WIFIINFO); 317 if (savedState.containsKey(KEY_NETWORKINFO)) { 318 mNetworkInfo = savedState.getParcelable(KEY_NETWORKINFO); 319 } 320 if (savedState.containsKey(KEY_SCANRESULTS)) { 321 Parcelable[] scanResults = savedState.getParcelableArray(KEY_SCANRESULTS); 322 mScanResults.clear(); 323 for (Parcelable result : scanResults) { 324 mScanResults.add((ScanResult) result); 325 } 326 } 327 if (savedState.containsKey(KEY_SCOREDNETWORKCACHE)) { 328 ArrayList<TimestampedScoredNetwork> scoredNetworkArrayList = 329 savedState.getParcelableArrayList(KEY_SCOREDNETWORKCACHE); 330 for (TimestampedScoredNetwork timedScore : scoredNetworkArrayList) { 331 mScoredNetworkCache.put(timedScore.getScore().networkKey.wifiKey.bssid, timedScore); 332 } 333 } 334 if (savedState.containsKey(KEY_PASSPOINT_UNIQUE_ID)) { 335 mPasspointUniqueId = savedState.getString(KEY_PASSPOINT_UNIQUE_ID); 336 } 337 if (savedState.containsKey(KEY_FQDN)) { 338 mFqdn = savedState.getString(KEY_FQDN); 339 } 340 if (savedState.containsKey(KEY_PROVIDER_FRIENDLY_NAME)) { 341 mProviderFriendlyName = savedState.getString(KEY_PROVIDER_FRIENDLY_NAME); 342 } 343 if (savedState.containsKey(KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS)) { 344 mSubscriptionExpirationTimeInMillis = 345 savedState.getLong(KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS); 346 } 347 if (savedState.containsKey(KEY_PASSPOINT_CONFIGURATION_VERSION)) { 348 mPasspointConfigurationVersion = savedState.getInt(KEY_PASSPOINT_CONFIGURATION_VERSION); 349 } 350 if (savedState.containsKey(KEY_IS_PSK_SAE_TRANSITION_MODE)) { 351 mIsPskSaeTransitionMode = savedState.getBoolean(KEY_IS_PSK_SAE_TRANSITION_MODE); 352 } 353 if (savedState.containsKey(KEY_IS_OWE_TRANSITION_MODE)) { 354 mIsOweTransitionMode = savedState.getBoolean(KEY_IS_OWE_TRANSITION_MODE); 355 } 356 357 update(mConfig, mInfo, mNetworkInfo); 358 359 // Calculate required fields 360 updateKey(); 361 updateBestRssiInfo(); 362 } 363 364 /** 365 * Creates an AccessPoint with only a WifiConfiguration. This is used for the saved networks 366 * page. 367 */ AccessPoint(Context context, WifiConfiguration config)368 public AccessPoint(Context context, WifiConfiguration config) { 369 mContext = context; 370 loadConfig(config); 371 updateKey(); 372 } 373 374 /** 375 * Initialize an AccessPoint object for a {@link PasspointConfiguration}. This is mainly 376 * used by "Saved Networks" page for managing the saved {@link PasspointConfiguration}. 377 */ AccessPoint(Context context, PasspointConfiguration config)378 public AccessPoint(Context context, PasspointConfiguration config) { 379 mContext = context; 380 mPasspointUniqueId = config.getUniqueId(); 381 mFqdn = config.getHomeSp().getFqdn(); 382 mProviderFriendlyName = config.getHomeSp().getFriendlyName(); 383 mSubscriptionExpirationTimeInMillis = config.getSubscriptionExpirationTimeMillis(); 384 if (config.isOsuProvisioned()) { 385 mPasspointConfigurationVersion = PasspointConfigurationVersion.OSU_PROVISIONED; 386 } else { 387 mPasspointConfigurationVersion = PasspointConfigurationVersion.NO_OSU_PROVISIONED; 388 } 389 updateKey(); 390 } 391 392 /** 393 * Initialize an AccessPoint object for a Passpoint network. 394 */ AccessPoint(@onNull Context context, @NonNull WifiConfiguration config, @Nullable Collection<ScanResult> homeScans, @Nullable Collection<ScanResult> roamingScans)395 public AccessPoint(@NonNull Context context, @NonNull WifiConfiguration config, 396 @Nullable Collection<ScanResult> homeScans, 397 @Nullable Collection<ScanResult> roamingScans) { 398 mContext = context; 399 networkId = config.networkId; 400 mConfig = config; 401 mPasspointUniqueId = config.getKey(); 402 mFqdn = config.FQDN; 403 setScanResultsPasspoint(homeScans, roamingScans); 404 updateKey(); 405 } 406 407 /** 408 * Initialize an AccessPoint object for a Passpoint OSU Provider. 409 */ AccessPoint(@onNull Context context, @NonNull OsuProvider provider, @NonNull Collection<ScanResult> results)410 public AccessPoint(@NonNull Context context, @NonNull OsuProvider provider, 411 @NonNull Collection<ScanResult> results) { 412 mContext = context; 413 mOsuProvider = provider; 414 setScanResults(results); 415 updateKey(); 416 } 417 AccessPoint(Context context, Collection<ScanResult> results)418 AccessPoint(Context context, Collection<ScanResult> results) { 419 mContext = context; 420 setScanResults(results); 421 updateKey(); 422 } 423 loadConfig(WifiConfiguration config)424 @VisibleForTesting void loadConfig(WifiConfiguration config) { 425 ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); 426 bssid = config.BSSID; 427 security = getSecurity(config); 428 networkId = config.networkId; 429 mConfig = config; 430 } 431 432 /** Updates {@link #mKey} and should only called upon object creation/initialization. */ updateKey()433 private void updateKey() { 434 // TODO(sghuman): Consolidate Key logic on ScanResultMatchInfo 435 if (isPasspoint()) { 436 mKey = getKey(mConfig); 437 } else if (isPasspointConfig()) { 438 mKey = getKey(mPasspointUniqueId); 439 } else if (isOsuProvider()) { 440 mKey = getKey(mOsuProvider); 441 } else { // Non-Passpoint AP 442 mKey = getKey(getSsidStr(), getBssid(), getSecurity()); 443 } 444 } 445 446 /** 447 * Returns a negative integer, zero, or a positive integer if this AccessPoint is less than, 448 * equal to, or greater than the other AccessPoint. 449 * 450 * Sort order rules for AccessPoints: 451 * 1. Active before inactive 452 * 2. Reachable before unreachable 453 * 3. Saved before unsaved 454 * 4. Network speed value 455 * 5. Stronger signal before weaker signal 456 * 6. SSID alphabetically 457 * 458 * Note that AccessPoints with a signal are usually also Reachable, 459 * and will thus appear before unreachable saved AccessPoints. 460 */ 461 @Override compareTo(@onNull AccessPoint other)462 public int compareTo(@NonNull AccessPoint other) { 463 // Active one goes first. 464 if (isActive() && !other.isActive()) return -1; 465 if (!isActive() && other.isActive()) return 1; 466 467 // Reachable one goes before unreachable one. 468 if (isReachable() && !other.isReachable()) return -1; 469 if (!isReachable() && other.isReachable()) return 1; 470 471 // Configured (saved) one goes before unconfigured one. 472 if (isSaved() && !other.isSaved()) return -1; 473 if (!isSaved() && other.isSaved()) return 1; 474 475 // Faster speeds go before slower speeds - but only if visible change in speed label 476 if (getSpeed() != other.getSpeed()) { 477 return other.getSpeed() - getSpeed(); 478 } 479 480 WifiManager wifiManager = getWifiManager(); 481 // Sort by signal strength, bucketed by level 482 int difference = wifiManager.calculateSignalLevel(other.mRssi) 483 - wifiManager.calculateSignalLevel(mRssi); 484 if (difference != 0) { 485 return difference; 486 } 487 488 // Sort by title. 489 difference = getTitle().compareToIgnoreCase(other.getTitle()); 490 if (difference != 0) { 491 return difference; 492 } 493 494 // Do a case sensitive comparison to distinguish SSIDs that differ in case only 495 return getSsidStr().compareTo(other.getSsidStr()); 496 } 497 498 @Override equals(Object other)499 public boolean equals(Object other) { 500 if (!(other instanceof AccessPoint)) return false; 501 return (this.compareTo((AccessPoint) other) == 0); 502 } 503 504 @Override hashCode()505 public int hashCode() { 506 int result = 0; 507 if (mInfo != null) result += 13 * mInfo.hashCode(); 508 result += 19 * mRssi; 509 result += 23 * networkId; 510 result += 29 * ssid.hashCode(); 511 return result; 512 } 513 514 @Override toString()515 public String toString() { 516 StringBuilder builder = new StringBuilder().append("AccessPoint(") 517 .append(ssid); 518 if (bssid != null) { 519 builder.append(":").append(bssid); 520 } 521 if (isSaved()) { 522 builder.append(',').append("saved"); 523 } 524 if (isActive()) { 525 builder.append(',').append("active"); 526 } 527 if (isEphemeral()) { 528 builder.append(',').append("ephemeral"); 529 } 530 if (isConnectable()) { 531 builder.append(',').append("connectable"); 532 } 533 if ((security != SECURITY_NONE) && (security != SECURITY_OWE)) { 534 builder.append(',').append(securityToString(security, pskType)); 535 } 536 builder.append(",level=").append(getLevel()); 537 if (mSpeed != Speed.NONE) { 538 builder.append(",speed=").append(mSpeed); 539 } 540 builder.append(",metered=").append(isMetered()); 541 542 if (isVerboseLoggingEnabled()) { 543 builder.append(",rssi=").append(mRssi); 544 synchronized (mLock) { 545 builder.append(",scan cache size=").append(mScanResults.size() 546 + mExtraScanResults.size()); 547 } 548 } 549 550 return builder.append(')').toString(); 551 } 552 553 /** 554 * Updates the AccessPoint rankingScore, metering, and speed, returning true if the data has 555 * changed. 556 * 557 * @param scoreCache The score cache to use to retrieve scores 558 * @param scoringUiEnabled Whether to show scoring and badging UI 559 * @param maxScoreCacheAgeMillis the maximum age in milliseconds of scores to consider when 560 * generating speed labels 561 */ update( WifiNetworkScoreCache scoreCache, boolean scoringUiEnabled, long maxScoreCacheAgeMillis)562 boolean update( 563 WifiNetworkScoreCache scoreCache, 564 boolean scoringUiEnabled, 565 long maxScoreCacheAgeMillis) { 566 boolean scoreChanged = false; 567 if (scoringUiEnabled) { 568 scoreChanged = updateScores(scoreCache, maxScoreCacheAgeMillis); 569 } 570 return updateMetered(scoreCache) || scoreChanged; 571 } 572 573 /** 574 * Updates the AccessPoint rankingScore and speed, returning true if the data has changed. 575 * 576 * <p>Any cached {@link TimestampedScoredNetwork} objects older than the given max age in millis 577 * will be removed when this method is invoked. 578 * 579 * <p>Precondition: {@link #mRssi} is up to date before invoking this method. 580 * 581 * @param scoreCache The score cache to use to retrieve scores 582 * @param maxScoreCacheAgeMillis the maximum age in milliseconds of scores to consider when 583 * generating speed labels 584 * 585 * @return true if the set speed has changed 586 */ updateScores(WifiNetworkScoreCache scoreCache, long maxScoreCacheAgeMillis)587 private boolean updateScores(WifiNetworkScoreCache scoreCache, long maxScoreCacheAgeMillis) { 588 long nowMillis = SystemClock.elapsedRealtime(); 589 synchronized (mLock) { 590 for (ScanResult result : mScanResults) { 591 ScoredNetwork score = scoreCache.getScoredNetwork(result); 592 if (score == null) { 593 continue; 594 } 595 TimestampedScoredNetwork timedScore = mScoredNetworkCache.get(result.BSSID); 596 if (timedScore == null) { 597 mScoredNetworkCache.put( 598 result.BSSID, new TimestampedScoredNetwork(score, nowMillis)); 599 } else { 600 // Update data since the has been seen in the score cache 601 timedScore.update(score, nowMillis); 602 } 603 } 604 } 605 606 // Remove old cached networks 607 long evictionCutoff = nowMillis - maxScoreCacheAgeMillis; 608 Iterator<TimestampedScoredNetwork> iterator = mScoredNetworkCache.values().iterator(); 609 iterator.forEachRemaining(timestampedScoredNetwork -> { 610 if (timestampedScoredNetwork.getUpdatedTimestampMillis() < evictionCutoff) { 611 iterator.remove(); 612 } 613 }); 614 615 return updateSpeed(); 616 } 617 618 /** 619 * Updates the internal speed, returning true if the update resulted in a speed label change. 620 */ updateSpeed()621 private boolean updateSpeed() { 622 int oldSpeed = mSpeed; 623 mSpeed = generateAverageSpeedForSsid(); 624 625 boolean changed = oldSpeed != mSpeed; 626 if(isVerboseLoggingEnabled() && changed) { 627 Log.i(TAG, String.format("%s: Set speed to %d", ssid, mSpeed)); 628 } 629 return changed; 630 } 631 632 /** Creates a speed value for the current {@link #mRssi} by averaging all non zero badges. */ generateAverageSpeedForSsid()633 @Speed private int generateAverageSpeedForSsid() { 634 if (mScoredNetworkCache.isEmpty()) { 635 return Speed.NONE; 636 } 637 638 if (Log.isLoggable(TAG, Log.DEBUG)) { 639 Log.d(TAG, String.format("Generating fallbackspeed for %s using cache: %s", 640 getSsidStr(), mScoredNetworkCache)); 641 } 642 643 // TODO(b/63073866): If flickering issues persist, consider mapping using getLevel rather 644 // than specific rssi value so score doesn't change without a visible wifi bar change. This 645 // issue is likely to be more evident for the active AP whose RSSI value is not half-lifed. 646 647 int count = 0; 648 int totalSpeed = 0; 649 for (TimestampedScoredNetwork timedScore : mScoredNetworkCache.values()) { 650 int speed = timedScore.getScore().calculateBadge(mRssi); 651 if (speed != Speed.NONE) { 652 count++; 653 totalSpeed += speed; 654 } 655 } 656 int speed = count == 0 ? Speed.NONE : totalSpeed / count; 657 if (isVerboseLoggingEnabled()) { 658 Log.i(TAG, String.format("%s generated fallback speed is: %d", getSsidStr(), speed)); 659 } 660 return roundToClosestSpeedEnum(speed); 661 } 662 663 /** 664 * Updates the AccessPoint's metering based on {@link ScoredNetwork#meteredHint}, returning 665 * true if the metering changed. 666 */ updateMetered(WifiNetworkScoreCache scoreCache)667 private boolean updateMetered(WifiNetworkScoreCache scoreCache) { 668 boolean oldMetering = mIsScoredNetworkMetered; 669 mIsScoredNetworkMetered = false; 670 671 if (isActive() && mInfo != null) { 672 NetworkKey key = NetworkKey.createFromWifiInfo(mInfo); 673 ScoredNetwork score = scoreCache.getScoredNetwork(key); 674 if (score != null) { 675 mIsScoredNetworkMetered |= score.meteredHint; 676 } 677 } else { 678 synchronized (mLock) { 679 for (ScanResult result : mScanResults) { 680 ScoredNetwork score = scoreCache.getScoredNetwork(result); 681 if (score == null) { 682 continue; 683 } 684 mIsScoredNetworkMetered |= score.meteredHint; 685 } 686 } 687 } 688 return oldMetering != mIsScoredNetworkMetered; 689 } 690 691 /** 692 * Generates an AccessPoint key for a given scan result 693 * 694 * @param context 695 * @param result Scan result 696 * @return AccessPoint key 697 */ getKey(Context context, ScanResult result)698 public static String getKey(Context context, ScanResult result) { 699 return getKey(result.SSID, result.BSSID, getSecurity(context, result)); 700 } 701 702 /** 703 * Returns the AccessPoint key for a WifiConfiguration. 704 * This will return a special Passpoint key if the config is for Passpoint. 705 */ getKey(WifiConfiguration config)706 public static String getKey(WifiConfiguration config) { 707 if (config.isPasspoint()) { 708 return getKey(config.getKey()); 709 } else { 710 return getKey(removeDoubleQuotes(config.SSID), config.BSSID, getSecurity(config)); 711 } 712 } 713 714 /** 715 * Returns the AccessPoint key corresponding to a Passpoint network by its unique identifier. 716 */ getKey(String passpointUniqueId)717 public static String getKey(String passpointUniqueId) { 718 return new StringBuilder() 719 .append(KEY_PREFIX_PASSPOINT_UNIQUE_ID) 720 .append(passpointUniqueId).toString(); 721 } 722 723 /** 724 * Returns the AccessPoint key corresponding to the OsuProvider. 725 */ getKey(OsuProvider provider)726 public static String getKey(OsuProvider provider) { 727 return new StringBuilder() 728 .append(KEY_PREFIX_OSU) 729 .append(provider.getFriendlyName()) 730 .append(',') 731 .append(provider.getServerUri()).toString(); 732 } 733 734 /** 735 * Returns the AccessPoint key for a normal non-Passpoint network by ssid/bssid and security. 736 */ getKey(String ssid, String bssid, int security)737 private static String getKey(String ssid, String bssid, int security) { 738 StringBuilder builder = new StringBuilder(); 739 builder.append(KEY_PREFIX_AP); 740 if (TextUtils.isEmpty(ssid)) { 741 builder.append(bssid); 742 } else { 743 builder.append(ssid); 744 } 745 builder.append(',').append(security); 746 return builder.toString(); 747 } 748 getKey()749 public String getKey() { 750 return mKey; 751 } 752 753 /** 754 * Determines if the other AccessPoint represents the same network as this AccessPoint 755 */ matches(AccessPoint other)756 public boolean matches(AccessPoint other) { 757 if (isPasspoint() || isPasspointConfig() || isOsuProvider()) { 758 return getKey().equals(other.getKey()); 759 } 760 761 if (!isSameSsidOrBssid(other)) { 762 return false; 763 } 764 765 final int otherApSecurity = other.getSecurity(); 766 if (mIsPskSaeTransitionMode) { 767 if (otherApSecurity == SECURITY_SAE && getWifiManager().isWpa3SaeSupported()) { 768 return true; 769 } else if (otherApSecurity == SECURITY_PSK) { 770 return true; 771 } 772 } else { 773 if ((security == SECURITY_SAE || security == SECURITY_PSK) 774 && other.isPskSaeTransitionMode()) { 775 return true; 776 } 777 } 778 779 if (mIsOweTransitionMode) { 780 if (otherApSecurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) { 781 return true; 782 } else if (otherApSecurity == SECURITY_NONE) { 783 return true; 784 } 785 } else { 786 if ((security == SECURITY_OWE || security == SECURITY_NONE) 787 && other.isOweTransitionMode()) { 788 return true; 789 } 790 } 791 792 return security == other.getSecurity(); 793 } 794 matches(WifiConfiguration config)795 public boolean matches(WifiConfiguration config) { 796 if (config.isPasspoint()) { 797 return (isPasspoint() && config.getKey().equals(mConfig.getKey())); 798 } 799 800 if (!ssid.equals(removeDoubleQuotes(config.SSID)) 801 || (mConfig != null && mConfig.shared != config.shared)) { 802 return false; 803 } 804 805 final int configSecurity = getSecurity(config); 806 if (mIsPskSaeTransitionMode) { 807 if (configSecurity == SECURITY_SAE && getWifiManager().isWpa3SaeSupported()) { 808 return true; 809 } else if (configSecurity == SECURITY_PSK) { 810 return true; 811 } 812 } 813 814 if (mIsOweTransitionMode) { 815 if (configSecurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) { 816 return true; 817 } else if (configSecurity == SECURITY_NONE) { 818 return true; 819 } 820 } 821 822 return security == getSecurity(config); 823 } 824 matches(WifiConfiguration config, WifiInfo wifiInfo)825 private boolean matches(WifiConfiguration config, WifiInfo wifiInfo) { 826 if (config == null || wifiInfo == null) { 827 return false; 828 } 829 if (!config.isPasspoint() && !isSameSsidOrBssid(wifiInfo)) { 830 return false; 831 } 832 return matches(config); 833 } 834 835 @VisibleForTesting matches(ScanResult scanResult)836 boolean matches(ScanResult scanResult) { 837 if (scanResult == null) { 838 return false; 839 } 840 if (isPasspoint() || isOsuProvider()) { 841 throw new IllegalStateException("Should not matches a Passpoint by ScanResult"); 842 } 843 844 if (!isSameSsidOrBssid(scanResult)) { 845 return false; 846 } 847 848 if (mIsPskSaeTransitionMode) { 849 if (scanResult.capabilities.contains("SAE") 850 && getWifiManager().isWpa3SaeSupported()) { 851 return true; 852 } else if (scanResult.capabilities.contains("PSK")) { 853 return true; 854 } 855 } else { 856 if ((security == SECURITY_SAE || security == SECURITY_PSK) 857 && AccessPoint.isPskSaeTransitionMode(scanResult)) { 858 return true; 859 } 860 } 861 862 if (mIsOweTransitionMode) { 863 final int scanResultSccurity = getSecurity(mContext, scanResult); 864 if (scanResultSccurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) { 865 return true; 866 } else if (scanResultSccurity == SECURITY_NONE) { 867 return true; 868 } 869 } else { 870 if ((security == SECURITY_OWE || security == SECURITY_NONE) 871 && AccessPoint.isOweTransitionMode(scanResult)) { 872 return true; 873 } 874 } 875 876 return security == getSecurity(mContext, scanResult); 877 } 878 getConfig()879 public WifiConfiguration getConfig() { 880 return mConfig; 881 } 882 getPasspointFqdn()883 public String getPasspointFqdn() { 884 return mFqdn; 885 } 886 clearConfig()887 public void clearConfig() { 888 mConfig = null; 889 networkId = WifiConfiguration.INVALID_NETWORK_ID; 890 } 891 getInfo()892 public WifiInfo getInfo() { 893 return mInfo; 894 } 895 896 /** 897 * Returns the number of levels to show for a Wifi icon, from 0 to 898 * {@link WifiManager#getMaxSignalLevel()}. 899 * 900 * <p>Use {@link #isReachable()} to determine if an AccessPoint is in range, as this method will 901 * always return at least 0. 902 */ getLevel()903 public int getLevel() { 904 return getWifiManager().calculateSignalLevel(mRssi); 905 } 906 getRssi()907 public int getRssi() { 908 return mRssi; 909 } 910 911 /** 912 * Returns the underlying scan result set. 913 * 914 * <p>Callers should not modify this set. 915 */ getScanResults()916 public Set<ScanResult> getScanResults() { 917 Set<ScanResult> allScans = new ArraySet<>(); 918 synchronized (mLock) { 919 allScans.addAll(mScanResults); 920 allScans.addAll(mExtraScanResults); 921 } 922 return allScans; 923 } 924 getScoredNetworkCache()925 public Map<String, TimestampedScoredNetwork> getScoredNetworkCache() { 926 return mScoredNetworkCache; 927 } 928 929 /** 930 * Updates {@link #mRssi} and sets scan result information to that of the best RSSI scan result. 931 * 932 * <p>If the given connection is active, the existing value of {@link #mRssi} will be returned. 933 * If the given AccessPoint is not active, a value will be calculated from previous scan 934 * results, returning the best RSSI for all matching AccessPoints averaged with the previous 935 * value. If the access point is not connected and there are no scan results, the rssi will be 936 * set to {@link #UNREACHABLE_RSSI}. 937 */ updateBestRssiInfo()938 private void updateBestRssiInfo() { 939 if (this.isActive()) { 940 return; 941 } 942 943 ScanResult bestResult = null; 944 int bestRssi = UNREACHABLE_RSSI; 945 synchronized (mLock) { 946 for (ScanResult result : mScanResults) { 947 if (result.level > bestRssi) { 948 bestRssi = result.level; 949 bestResult = result; 950 } 951 } 952 } 953 954 // Set the rssi to the average of the current rssi and the previous rssi. 955 if (bestRssi != UNREACHABLE_RSSI && mRssi != UNREACHABLE_RSSI) { 956 mRssi = (mRssi + bestRssi) / 2; 957 } else { 958 mRssi = bestRssi; 959 } 960 961 if (bestResult != null) { 962 ssid = bestResult.SSID; 963 bssid = bestResult.BSSID; 964 security = getSecurity(mContext, bestResult); 965 if (security == SECURITY_PSK || security == SECURITY_SAE) { 966 pskType = getPskType(bestResult); 967 } 968 if (security == SECURITY_EAP) { 969 mEapType = getEapType(bestResult); 970 } 971 972 mIsPskSaeTransitionMode = AccessPoint.isPskSaeTransitionMode(bestResult); 973 mIsOweTransitionMode = AccessPoint.isOweTransitionMode(bestResult); 974 } 975 // Update the config SSID of a Passpoint network to that of the best RSSI 976 if (isPasspoint()) { 977 mConfig.SSID = convertToQuotedString(ssid); 978 } 979 } 980 981 /** 982 * Returns if the network should be considered metered. 983 */ isMetered()984 public boolean isMetered() { 985 return mIsScoredNetworkMetered 986 || WifiConfiguration.isMetered(mConfig, mInfo); 987 } 988 getNetworkInfo()989 public NetworkInfo getNetworkInfo() { 990 return mNetworkInfo; 991 } 992 getSecurity()993 public int getSecurity() { 994 return security; 995 } 996 getSecurityString(boolean concise)997 public String getSecurityString(boolean concise) { 998 Context context = mContext; 999 if (isPasspoint() || isPasspointConfig()) { 1000 return concise ? context.getString(R.string.wifi_security_short_eap) : 1001 context.getString(R.string.wifi_security_eap); 1002 } 1003 1004 if (mIsPskSaeTransitionMode) { 1005 return concise ? context.getString(R.string.wifi_security_short_psk_sae) : 1006 context.getString(R.string.wifi_security_psk_sae); 1007 } 1008 if (mIsOweTransitionMode) { 1009 return concise ? context.getString(R.string.wifi_security_short_none_owe) : 1010 context.getString(R.string.wifi_security_none_owe); 1011 } 1012 1013 switch(security) { 1014 case SECURITY_EAP: 1015 switch (mEapType) { 1016 case EAP_WPA: 1017 return concise ? context.getString(R.string.wifi_security_short_eap_wpa) : 1018 context.getString(R.string.wifi_security_eap_wpa); 1019 case EAP_WPA2_WPA3: 1020 return concise 1021 ? context.getString(R.string.wifi_security_short_eap_wpa2_wpa3) : 1022 context.getString(R.string.wifi_security_eap_wpa2_wpa3); 1023 case EAP_UNKNOWN: 1024 default: 1025 return concise 1026 ? context.getString(R.string.wifi_security_short_eap) : 1027 context.getString(R.string.wifi_security_eap); 1028 } 1029 case SECURITY_EAP_SUITE_B: 1030 return concise ? context.getString(R.string.wifi_security_short_eap_suiteb) : 1031 context.getString(R.string.wifi_security_eap_suiteb); 1032 case SECURITY_PSK: 1033 switch (pskType) { 1034 case PSK_WPA: 1035 return concise ? context.getString(R.string.wifi_security_short_wpa) : 1036 context.getString(R.string.wifi_security_wpa); 1037 case PSK_WPA2: 1038 return concise ? context.getString(R.string.wifi_security_short_wpa2) : 1039 context.getString(R.string.wifi_security_wpa2); 1040 case PSK_WPA_WPA2: 1041 return concise ? context.getString(R.string.wifi_security_short_wpa_wpa2) : 1042 context.getString(R.string.wifi_security_wpa_wpa2); 1043 case PSK_UNKNOWN: 1044 default: 1045 return concise ? context.getString(R.string.wifi_security_short_psk_generic) 1046 : context.getString(R.string.wifi_security_psk_generic); 1047 } 1048 case SECURITY_WEP: 1049 return concise ? context.getString(R.string.wifi_security_short_wep) : 1050 context.getString(R.string.wifi_security_wep); 1051 case SECURITY_SAE: 1052 return concise ? context.getString(R.string.wifi_security_short_sae) : 1053 context.getString(R.string.wifi_security_sae); 1054 case SECURITY_OWE: 1055 return concise ? context.getString(R.string.wifi_security_short_owe) : 1056 context.getString(R.string.wifi_security_owe); 1057 case SECURITY_NONE: 1058 default: 1059 return concise ? "" : context.getString(R.string.wifi_security_none); 1060 } 1061 } 1062 getSsidStr()1063 public String getSsidStr() { 1064 return ssid; 1065 } 1066 getBssid()1067 public String getBssid() { 1068 return bssid; 1069 } 1070 getSsid()1071 public CharSequence getSsid() { 1072 return ssid; 1073 } 1074 1075 /** 1076 * Returns the name associated with the stored config. 1077 * @deprecated Please use {@link #getTitle()} instead to get the display name of an AccessPoint. 1078 */ 1079 @Deprecated getConfigName()1080 public String getConfigName() { 1081 if (mConfig != null && mConfig.isPasspoint()) { 1082 return mConfig.providerFriendlyName; 1083 } else if (mPasspointUniqueId != null) { 1084 return mProviderFriendlyName; 1085 } else { 1086 return ssid; 1087 } 1088 } 1089 getDetailedState()1090 public DetailedState getDetailedState() { 1091 if (mNetworkInfo != null) { 1092 return mNetworkInfo.getDetailedState(); 1093 } 1094 Log.w(TAG, "NetworkInfo is null, cannot return detailed state"); 1095 return null; 1096 } 1097 getSavedNetworkSummary()1098 public String getSavedNetworkSummary() { 1099 WifiConfiguration config = mConfig; 1100 if (config != null) { 1101 PackageManager pm = mContext.getPackageManager(); 1102 String systemName = pm.getNameForUid(android.os.Process.SYSTEM_UID); 1103 int userId = UserHandle.getUserId(config.creatorUid); 1104 ApplicationInfo appInfo = null; 1105 if (config.creatorName != null && config.creatorName.equals(systemName)) { 1106 appInfo = mContext.getApplicationInfo(); 1107 } else { 1108 try { 1109 IPackageManager ipm = AppGlobals.getPackageManager(); 1110 appInfo = ipm.getApplicationInfo(config.creatorName, 0 /* flags */, userId); 1111 } catch (RemoteException rex) { 1112 } 1113 } 1114 if (appInfo != null && 1115 !appInfo.packageName.equals(mContext.getString(R.string.settings_package)) && 1116 !appInfo.packageName.equals( 1117 mContext.getString(R.string.certinstaller_package))) { 1118 return mContext.getString(R.string.saved_network, appInfo.loadLabel(pm)); 1119 } 1120 } 1121 1122 if (isPasspointConfigurationR1() && isExpired()) { 1123 return mContext.getString(R.string.wifi_passpoint_expired); 1124 } 1125 return ""; 1126 } 1127 1128 /** 1129 * Returns the display title for the AccessPoint, such as for an AccessPointPreference's title. 1130 */ getTitle()1131 public String getTitle() { 1132 if (isPasspoint() && !TextUtils.isEmpty(mConfig.providerFriendlyName)) { 1133 return mConfig.providerFriendlyName; 1134 } else if (isPasspointConfig() && !TextUtils.isEmpty(mProviderFriendlyName)) { 1135 return mProviderFriendlyName; 1136 } else if (isOsuProvider() && !TextUtils.isEmpty(mOsuProvider.getFriendlyName())) { 1137 return mOsuProvider.getFriendlyName(); 1138 } else if (!TextUtils.isEmpty(getSsidStr())) { 1139 return getSsidStr(); 1140 } else { 1141 return ""; 1142 } 1143 } 1144 getSummary()1145 public String getSummary() { 1146 return getSettingsSummary(); 1147 } 1148 getSettingsSummary()1149 public String getSettingsSummary() { 1150 return getSettingsSummary(false /*convertSavedAsDisconnected*/); 1151 } 1152 1153 /** 1154 * Returns the summary for the AccessPoint. 1155 */ getSettingsSummary(boolean convertSavedAsDisconnected)1156 public String getSettingsSummary(boolean convertSavedAsDisconnected) { 1157 if (isPasspointConfigurationR1() && isExpired()) { 1158 return mContext.getString(R.string.wifi_passpoint_expired); 1159 } 1160 1161 // Update to new summary 1162 StringBuilder summary = new StringBuilder(); 1163 1164 if (isOsuProvider()) { 1165 if (mOsuProvisioningComplete) { 1166 summary.append(mContext.getString(R.string.osu_sign_up_complete)); 1167 } else if (mOsuFailure != null) { 1168 summary.append(mOsuFailure); 1169 } else if (mOsuStatus != null) { 1170 summary.append(mOsuStatus); 1171 } else { 1172 summary.append(mContext.getString(R.string.tap_to_sign_up)); 1173 } 1174 } else if (isActive()) { 1175 summary.append(getSummary(mContext, /* ssid */ null, getDetailedState(), 1176 mInfo != null && mInfo.isEphemeral(), 1177 mInfo != null ? mInfo.getRequestingPackageName() : null)); 1178 } else { // not active 1179 if (mConfig != null && mConfig.hasNoInternetAccess()) { 1180 int messageID = 1181 mConfig.getNetworkSelectionStatus().getNetworkSelectionStatus() 1182 == NETWORK_SELECTION_PERMANENTLY_DISABLED 1183 ? R.string.wifi_no_internet_no_reconnect 1184 : R.string.wifi_no_internet; 1185 summary.append(mContext.getString(messageID)); 1186 } else if (mConfig != null 1187 && (mConfig.getNetworkSelectionStatus().getNetworkSelectionStatus() 1188 != NETWORK_SELECTION_ENABLED)) { 1189 WifiConfiguration.NetworkSelectionStatus networkStatus = 1190 mConfig.getNetworkSelectionStatus(); 1191 switch (networkStatus.getNetworkSelectionDisableReason()) { 1192 case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE: 1193 summary.append(mContext.getString(R.string.wifi_disabled_password_failure)); 1194 break; 1195 case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD: 1196 summary.append(mContext.getString(R.string.wifi_check_password_try_again)); 1197 break; 1198 case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE: 1199 summary.append(mContext.getString(R.string.wifi_disabled_network_failure)); 1200 break; 1201 case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION: 1202 summary.append(mContext.getString(R.string.wifi_disabled_generic)); 1203 break; 1204 } 1205 } else if (!isReachable()) { // Wifi out of range 1206 summary.append(mContext.getString(R.string.wifi_not_in_range)); 1207 } else { // In range, not disabled. 1208 if (mConfig != null) { // Is saved network 1209 // Last attempt to connect to this failed. Show reason why 1210 switch (mConfig.getRecentFailureReason()) { 1211 case WifiConfiguration.RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA: 1212 summary.append(mContext.getString( 1213 R.string.wifi_ap_unable_to_handle_new_sta)); 1214 break; 1215 default: 1216 if (convertSavedAsDisconnected) { 1217 // Disconnected 1218 summary.append(mContext.getString(R.string.wifi_disconnected)); 1219 } else { 1220 // "Saved" 1221 summary.append(mContext.getString(R.string.wifi_remembered)); 1222 } 1223 break; 1224 } 1225 } 1226 } 1227 } 1228 1229 1230 1231 if (isVerboseLoggingEnabled()) { 1232 summary.append(WifiUtils.buildLoggingSummary(this, mConfig)); 1233 } 1234 1235 if (mConfig != null && (WifiUtils.isMeteredOverridden(mConfig) || mConfig.meteredHint)) { 1236 return mContext.getResources().getString( 1237 R.string.preference_summary_default_combination, 1238 WifiUtils.getMeteredLabel(mContext, mConfig), 1239 summary.toString()); 1240 } 1241 1242 // If Speed label and summary are both present, use the preference combination to combine 1243 // the two, else return the non-null one. 1244 if (getSpeedLabel() != null && summary.length() != 0) { 1245 return mContext.getResources().getString( 1246 R.string.preference_summary_default_combination, 1247 getSpeedLabel(), 1248 summary.toString()); 1249 } else if (getSpeedLabel() != null) { 1250 return getSpeedLabel(); 1251 } else { 1252 return summary.toString(); 1253 } 1254 } 1255 1256 /** 1257 * Return whether this is the active connection. 1258 * For ephemeral connections (networkId is invalid), this returns false if the network is 1259 * disconnected. 1260 */ isActive()1261 public boolean isActive() { 1262 return mNetworkInfo != null && 1263 (networkId != WifiConfiguration.INVALID_NETWORK_ID || 1264 mNetworkInfo.getState() != State.DISCONNECTED); 1265 } 1266 isConnectable()1267 public boolean isConnectable() { 1268 return getLevel() != -1 && getDetailedState() == null; 1269 } 1270 isEphemeral()1271 public boolean isEphemeral() { 1272 return mInfo != null && mInfo.isEphemeral() && 1273 mNetworkInfo != null && mNetworkInfo.getState() != State.DISCONNECTED; 1274 } 1275 1276 /** 1277 * Return true if this AccessPoint represents a Passpoint AP. 1278 */ isPasspoint()1279 public boolean isPasspoint() { 1280 return mConfig != null && mConfig.isPasspoint(); 1281 } 1282 1283 /** 1284 * Return true if this AccessPoint represents a Passpoint provider configuration. 1285 */ isPasspointConfig()1286 public boolean isPasspointConfig() { 1287 return mPasspointUniqueId != null && mConfig == null; 1288 } 1289 1290 /** 1291 * Return true if this AccessPoint represents an OSU Provider. 1292 */ isOsuProvider()1293 public boolean isOsuProvider() { 1294 return mOsuProvider != null; 1295 } 1296 1297 /** 1298 * Return true if this AccessPoint is expired. 1299 */ isExpired()1300 public boolean isExpired() { 1301 if (mSubscriptionExpirationTimeInMillis <= 0) { 1302 // Expiration time not specified. 1303 return false; 1304 } else { 1305 return System.currentTimeMillis() >= mSubscriptionExpirationTimeInMillis; 1306 } 1307 } 1308 isPasspointConfigurationR1()1309 public boolean isPasspointConfigurationR1() { 1310 return mPasspointConfigurationVersion == PasspointConfigurationVersion.NO_OSU_PROVISIONED; 1311 } 1312 1313 /** 1314 * Return true if {@link PasspointConfiguration#isOsuProvisioned} is true, this may refer to R2 1315 * or R3. 1316 */ isPasspointConfigurationOsuProvisioned()1317 public boolean isPasspointConfigurationOsuProvisioned() { 1318 return mPasspointConfigurationVersion == PasspointConfigurationVersion.OSU_PROVISIONED; 1319 } 1320 1321 /** 1322 * Starts the OSU Provisioning flow. 1323 */ startOsuProvisioning(@ullable WifiManager.ActionListener connectListener)1324 public void startOsuProvisioning(@Nullable WifiManager.ActionListener connectListener) { 1325 mConnectListener = connectListener; 1326 1327 getWifiManager().startSubscriptionProvisioning( 1328 mOsuProvider, 1329 mContext.getMainExecutor(), 1330 new AccessPointProvisioningCallback() 1331 ); 1332 } 1333 1334 /** 1335 * Return whether the given {@link WifiInfo} is for this access point. 1336 * If the current AP does not have a network Id then the config is used to 1337 * match based on SSID and security. 1338 */ isInfoForThisAccessPoint(WifiConfiguration config, WifiInfo info)1339 private boolean isInfoForThisAccessPoint(WifiConfiguration config, WifiInfo info) { 1340 if (info.isOsuAp() || mOsuStatus != null) { 1341 return (info.isOsuAp() && mOsuStatus != null); 1342 } else if (info.isPasspointAp() || isPasspoint()) { 1343 // TODO: Use TextUtils.equals(info.getPasspointUniqueId(), mConfig.getKey()) when API 1344 // is available 1345 return (info.isPasspointAp() && isPasspoint() 1346 && TextUtils.equals(info.getPasspointFqdn(), mConfig.FQDN) 1347 && TextUtils.equals(info.getPasspointProviderFriendlyName(), 1348 mConfig.providerFriendlyName)); 1349 } 1350 1351 if (networkId != WifiConfiguration.INVALID_NETWORK_ID) { 1352 return networkId == info.getNetworkId(); 1353 } else if (config != null) { 1354 return matches(config, info); 1355 } else { 1356 // Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID. 1357 // (Note that we only do this if the WifiConfiguration explicitly equals INVALID). 1358 // TODO: Handle hex string SSIDs. 1359 return TextUtils.equals(removeDoubleQuotes(info.getSSID()), ssid); 1360 } 1361 } 1362 isSaved()1363 public boolean isSaved() { 1364 return mConfig != null; 1365 } 1366 getTag()1367 public Object getTag() { 1368 return mTag; 1369 } 1370 setTag(Object tag)1371 public void setTag(Object tag) { 1372 mTag = tag; 1373 } 1374 1375 /** 1376 * Generate and save a default wifiConfiguration with common values. 1377 * Can only be called for unsecured networks. 1378 */ generateOpenNetworkConfig()1379 public void generateOpenNetworkConfig() { 1380 if (!isOpenNetwork()) { 1381 throw new IllegalStateException(); 1382 } 1383 if (mConfig != null) 1384 return; 1385 mConfig = new WifiConfiguration(); 1386 mConfig.SSID = AccessPoint.convertToQuotedString(ssid); 1387 1388 if (security == SECURITY_NONE) { 1389 mConfig.allowedKeyManagement.set(KeyMgmt.NONE); 1390 } else { 1391 mConfig.allowedKeyManagement.set(KeyMgmt.OWE); 1392 mConfig.requirePmf = true; 1393 } 1394 } 1395 saveWifiState(Bundle savedState)1396 public void saveWifiState(Bundle savedState) { 1397 if (ssid != null) savedState.putString(KEY_SSID, getSsidStr()); 1398 savedState.putInt(KEY_SECURITY, security); 1399 savedState.putInt(KEY_SPEED, mSpeed); 1400 savedState.putInt(KEY_PSKTYPE, pskType); 1401 savedState.putInt(KEY_EAPTYPE, mEapType); 1402 if (mConfig != null) savedState.putParcelable(KEY_CONFIG, mConfig); 1403 savedState.putParcelable(KEY_WIFIINFO, mInfo); 1404 synchronized (mLock) { 1405 savedState.putParcelableArray(KEY_SCANRESULTS, 1406 mScanResults.toArray(new Parcelable[mScanResults.size() 1407 + mExtraScanResults.size()])); 1408 } 1409 savedState.putParcelableArrayList(KEY_SCOREDNETWORKCACHE, 1410 new ArrayList<>(mScoredNetworkCache.values())); 1411 if (mNetworkInfo != null) { 1412 savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo); 1413 } 1414 if (mPasspointUniqueId != null) { 1415 savedState.putString(KEY_PASSPOINT_UNIQUE_ID, mPasspointUniqueId); 1416 } 1417 if (mFqdn != null) { 1418 savedState.putString(KEY_FQDN, mFqdn); 1419 } 1420 if (mProviderFriendlyName != null) { 1421 savedState.putString(KEY_PROVIDER_FRIENDLY_NAME, mProviderFriendlyName); 1422 } 1423 savedState.putLong(KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS, 1424 mSubscriptionExpirationTimeInMillis); 1425 savedState.putInt(KEY_PASSPOINT_CONFIGURATION_VERSION, mPasspointConfigurationVersion); 1426 savedState.putBoolean(KEY_IS_PSK_SAE_TRANSITION_MODE, mIsPskSaeTransitionMode); 1427 savedState.putBoolean(KEY_IS_OWE_TRANSITION_MODE, mIsOweTransitionMode); 1428 } 1429 setListener(AccessPointListener listener)1430 public void setListener(AccessPointListener listener) { 1431 mAccessPointListener = listener; 1432 } 1433 1434 /** 1435 * Sets {@link #mScanResults} to the given collection and updates info based on the best RSSI 1436 * scan result. 1437 * 1438 * @param scanResults a collection of scan results to add to the internal set 1439 */ setScanResults(Collection<ScanResult> scanResults)1440 void setScanResults(Collection<ScanResult> scanResults) { 1441 if (CollectionUtils.isEmpty(scanResults)) { 1442 Log.d(TAG, "Cannot set scan results to empty list"); 1443 return; 1444 } 1445 1446 // Validate scan results are for current AP only by matching SSID/BSSID 1447 // Passpoint networks are not bound to a specific SSID/BSSID, so skip this for passpoint. 1448 if (mKey != null && !isPasspoint() && !isOsuProvider()) { 1449 for (ScanResult result : scanResults) { 1450 if (!matches(result)) { 1451 Log.d(TAG, String.format( 1452 "ScanResult %s\nkey of %s did not match current AP key %s", 1453 result, getKey(mContext, result), mKey)); 1454 return; 1455 } 1456 } 1457 } 1458 1459 int oldLevel = getLevel(); 1460 synchronized (mLock) { 1461 mScanResults.clear(); 1462 mScanResults.addAll(scanResults); 1463 } 1464 updateBestRssiInfo(); 1465 int newLevel = getLevel(); 1466 1467 // If newLevel is 0, there will be no displayed Preference since the AP is unreachable 1468 if (newLevel > 0 && newLevel != oldLevel) { 1469 // Only update labels on visible rssi changes 1470 updateSpeed(); 1471 ThreadUtils.postOnMainThread(() -> { 1472 if (mAccessPointListener != null) { 1473 mAccessPointListener.onLevelChanged(this); 1474 } 1475 }); 1476 1477 } 1478 1479 ThreadUtils.postOnMainThread(() -> { 1480 if (mAccessPointListener != null) { 1481 mAccessPointListener.onAccessPointChanged(this); 1482 } 1483 }); 1484 } 1485 1486 /** 1487 * Sets the internal scan result cache to the list of home scans. 1488 * If there are no home scans, then the roaming scan list is used, and the AccessPoint is 1489 * marked as roaming. 1490 */ setScanResultsPasspoint( @ullable Collection<ScanResult> homeScans, @Nullable Collection<ScanResult> roamingScans)1491 void setScanResultsPasspoint( 1492 @Nullable Collection<ScanResult> homeScans, 1493 @Nullable Collection<ScanResult> roamingScans) { 1494 synchronized (mLock) { 1495 mExtraScanResults.clear(); 1496 if (!CollectionUtils.isEmpty(homeScans)) { 1497 mIsRoaming = false; 1498 if (!CollectionUtils.isEmpty(roamingScans)) { 1499 mExtraScanResults.addAll(roamingScans); 1500 } 1501 setScanResults(homeScans); 1502 } else if (!CollectionUtils.isEmpty(roamingScans)) { 1503 mIsRoaming = true; 1504 setScanResults(roamingScans); 1505 } 1506 } 1507 } 1508 1509 /** 1510 * Attempt to update the AccessPoint with the current connection info. 1511 * This is used to set an AccessPoint to the active one if the connection info matches, or 1512 * conversely to set an AccessPoint to inactive if the connection info does not match. The RSSI 1513 * is also updated upon a match. Listeners will be notified if an update occurred. 1514 * 1515 * This is called in {@link WifiTracker#updateAccessPoints} as well as in callbacks for handling 1516 * NETWORK_STATE_CHANGED_ACTION, RSSI_CHANGED_ACTION, and onCapabilitiesChanged in WifiTracker. 1517 * 1518 * Returns true if an update occurred. 1519 */ update( @ullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo)1520 public boolean update( 1521 @Nullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) { 1522 boolean updated = false; 1523 final int oldLevel = getLevel(); 1524 if (info != null && isInfoForThisAccessPoint(config, info)) { 1525 updated = (mInfo == null); 1526 if (!isPasspoint() && mConfig != config) { 1527 // We do not set updated = true as we do not want to increase the amount of sorting 1528 // and copying performed in WifiTracker at this time. If issues involving refresh 1529 // are still seen, we will investigate further. 1530 update(config); // Notifies the AccessPointListener of the change 1531 } 1532 if (mRssi != info.getRssi() && info.getRssi() != WifiInfo.INVALID_RSSI) { 1533 mRssi = info.getRssi(); 1534 updated = true; 1535 } else if (mNetworkInfo != null && networkInfo != null 1536 && mNetworkInfo.getDetailedState() != networkInfo.getDetailedState()) { 1537 updated = true; 1538 } 1539 mInfo = info; 1540 mNetworkInfo = networkInfo; 1541 } else if (mInfo != null) { 1542 updated = true; 1543 mInfo = null; 1544 mNetworkInfo = null; 1545 } 1546 if (updated && mAccessPointListener != null) { 1547 ThreadUtils.postOnMainThread(() -> { 1548 if (mAccessPointListener != null) { 1549 mAccessPointListener.onAccessPointChanged(this); 1550 } 1551 }); 1552 1553 if (oldLevel != getLevel() /* current level */) { 1554 ThreadUtils.postOnMainThread(() -> { 1555 if (mAccessPointListener != null) { 1556 mAccessPointListener.onLevelChanged(this); 1557 } 1558 }); 1559 } 1560 } 1561 1562 return updated; 1563 } 1564 update(@ullable WifiConfiguration config)1565 void update(@Nullable WifiConfiguration config) { 1566 mConfig = config; 1567 if (mConfig != null && !isPasspoint()) { 1568 ssid = removeDoubleQuotes(mConfig.SSID); 1569 } 1570 networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID; 1571 ThreadUtils.postOnMainThread(() -> { 1572 if (mAccessPointListener != null) { 1573 mAccessPointListener.onAccessPointChanged(this); 1574 } 1575 }); 1576 } 1577 1578 @VisibleForTesting setRssi(int rssi)1579 void setRssi(int rssi) { 1580 mRssi = rssi; 1581 } 1582 1583 /** Sets the rssi to {@link #UNREACHABLE_RSSI}. */ setUnreachable()1584 void setUnreachable() { 1585 setRssi(AccessPoint.UNREACHABLE_RSSI); 1586 } 1587 getSpeed()1588 int getSpeed() { return mSpeed;} 1589 1590 @Nullable getSpeedLabel()1591 String getSpeedLabel() { 1592 return getSpeedLabel(mSpeed); 1593 } 1594 1595 @Nullable 1596 @Speed roundToClosestSpeedEnum(int speed)1597 private static int roundToClosestSpeedEnum(int speed) { 1598 if (speed < Speed.SLOW) { 1599 return Speed.NONE; 1600 } else if (speed < (Speed.SLOW + Speed.MODERATE) / 2) { 1601 return Speed.SLOW; 1602 } else if (speed < (Speed.MODERATE + Speed.FAST) / 2) { 1603 return Speed.MODERATE; 1604 } else if (speed < (Speed.FAST + Speed.VERY_FAST) / 2) { 1605 return Speed.FAST; 1606 } else { 1607 return Speed.VERY_FAST; 1608 } 1609 } 1610 1611 @Nullable getSpeedLabel(@peed int speed)1612 String getSpeedLabel(@Speed int speed) { 1613 return getSpeedLabel(mContext, speed); 1614 } 1615 getSpeedLabel(Context context, int speed)1616 private static String getSpeedLabel(Context context, int speed) { 1617 switch (speed) { 1618 case Speed.VERY_FAST: 1619 return context.getString(R.string.speed_label_very_fast); 1620 case Speed.FAST: 1621 return context.getString(R.string.speed_label_fast); 1622 case Speed.MODERATE: 1623 return context.getString(R.string.speed_label_okay); 1624 case Speed.SLOW: 1625 return context.getString(R.string.speed_label_slow); 1626 case Speed.NONE: 1627 default: 1628 return null; 1629 } 1630 } 1631 1632 /** Return the speed label for a {@link ScoredNetwork} at the specified {@code rssi} level. */ 1633 @Nullable getSpeedLabel(Context context, ScoredNetwork scoredNetwork, int rssi)1634 public static String getSpeedLabel(Context context, ScoredNetwork scoredNetwork, int rssi) { 1635 return getSpeedLabel(context, roundToClosestSpeedEnum(scoredNetwork.calculateBadge(rssi))); 1636 } 1637 1638 /** Return true if the current RSSI is reachable, and false otherwise. */ isReachable()1639 public boolean isReachable() { 1640 return mRssi != UNREACHABLE_RSSI; 1641 } 1642 getAppLabel(String packageName, PackageManager packageManager)1643 private static CharSequence getAppLabel(String packageName, PackageManager packageManager) { 1644 CharSequence appLabel = ""; 1645 ApplicationInfo appInfo = null; 1646 try { 1647 int userId = ActivityManager.getCurrentUser(); 1648 appInfo = packageManager.getApplicationInfoAsUser(packageName, 0 /* flags */, userId); 1649 } catch (PackageManager.NameNotFoundException e) { 1650 Log.e(TAG, "Failed to get app info", e); 1651 return appLabel; 1652 } 1653 if (appInfo != null) { 1654 appLabel = appInfo.loadLabel(packageManager); 1655 } 1656 return appLabel; 1657 } 1658 getSummary(Context context, String ssid, DetailedState state, boolean isEphemeral, String suggestionOrSpecifierPackageName)1659 public static String getSummary(Context context, String ssid, DetailedState state, 1660 boolean isEphemeral, String suggestionOrSpecifierPackageName) { 1661 if (state == DetailedState.CONNECTED) { 1662 if (isEphemeral && !TextUtils.isEmpty(suggestionOrSpecifierPackageName)) { 1663 CharSequence appLabel = 1664 getAppLabel(suggestionOrSpecifierPackageName, context.getPackageManager()); 1665 return context.getString(R.string.connected_via_app, appLabel); 1666 } else if (isEphemeral) { 1667 // Special case for connected + ephemeral networks. 1668 final NetworkScoreManager networkScoreManager = context.getSystemService( 1669 NetworkScoreManager.class); 1670 NetworkScorerAppData scorer = networkScoreManager.getActiveScorer(); 1671 if (scorer != null && scorer.getRecommendationServiceLabel() != null) { 1672 String format = context.getString(R.string.connected_via_network_scorer); 1673 return String.format(format, scorer.getRecommendationServiceLabel()); 1674 } else { 1675 return context.getString(R.string.connected_via_network_scorer_default); 1676 } 1677 } 1678 } 1679 1680 // Case when there is wifi connected without internet connectivity. 1681 final ConnectivityManager cm = (ConnectivityManager) 1682 context.getSystemService(Context.CONNECTIVITY_SERVICE); 1683 if (state == DetailedState.CONNECTED) { 1684 WifiManager wifiManager = context.getSystemService(WifiManager.class); 1685 NetworkCapabilities nc = cm.getNetworkCapabilities(wifiManager.getCurrentNetwork()); 1686 1687 if (nc != null) { 1688 if (nc.hasCapability(nc.NET_CAPABILITY_CAPTIVE_PORTAL)) { 1689 int id = context.getResources() 1690 .getIdentifier("network_available_sign_in", "string", "android"); 1691 return context.getString(id); 1692 } else if (nc.hasCapability( 1693 NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) { 1694 return context.getString(R.string.wifi_limited_connection); 1695 } else if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { 1696 final String mode = Settings.Global.getString(context.getContentResolver(), 1697 Settings.Global.PRIVATE_DNS_MODE); 1698 if (nc.isPrivateDnsBroken()) { 1699 return context.getString(R.string.private_dns_broken); 1700 } else { 1701 return context.getString(R.string.wifi_connected_no_internet); 1702 } 1703 } 1704 } 1705 } 1706 if (state == null) { 1707 Log.w(TAG, "state is null, returning empty summary"); 1708 return ""; 1709 } 1710 String[] formats = context.getResources().getStringArray((ssid == null) 1711 ? R.array.wifi_status : R.array.wifi_status_with_ssid); 1712 int index = state.ordinal(); 1713 1714 if (index >= formats.length || formats[index].length() == 0) { 1715 return ""; 1716 } 1717 return String.format(formats[index], ssid); 1718 } 1719 convertToQuotedString(String string)1720 public static String convertToQuotedString(String string) { 1721 return "\"" + string + "\""; 1722 } 1723 getPskType(ScanResult result)1724 private static int getPskType(ScanResult result) { 1725 boolean wpa = result.capabilities.contains("WPA-PSK"); 1726 boolean wpa2 = result.capabilities.contains("RSN-PSK"); 1727 boolean wpa3 = result.capabilities.contains("RSN-SAE"); 1728 if (wpa2 && wpa) { 1729 return PSK_WPA_WPA2; 1730 } else if (wpa2) { 1731 return PSK_WPA2; 1732 } else if (wpa) { 1733 return PSK_WPA; 1734 } else { 1735 if (!wpa3) { 1736 // Suppress warning for WPA3 only networks 1737 Log.w(TAG, "Received abnormal flag string: " + result.capabilities); 1738 } 1739 return PSK_UNKNOWN; 1740 } 1741 } 1742 getEapType(ScanResult result)1743 private static int getEapType(ScanResult result) { 1744 // WPA2-Enterprise and WPA3-Enterprise (non 192-bit) advertise RSN-EAP-CCMP 1745 if (result.capabilities.contains("RSN-EAP")) { 1746 return EAP_WPA2_WPA3; 1747 } 1748 // WPA-Enterprise advertises WPA-EAP-TKIP 1749 if (result.capabilities.contains("WPA-EAP")) { 1750 return EAP_WPA; 1751 } 1752 return EAP_UNKNOWN; 1753 } 1754 getSecurity(Context context, ScanResult result)1755 private static int getSecurity(Context context, ScanResult result) { 1756 final boolean isWep = result.capabilities.contains("WEP"); 1757 final boolean isSae = result.capabilities.contains("SAE"); 1758 final boolean isPsk = result.capabilities.contains("PSK"); 1759 final boolean isEapSuiteB192 = result.capabilities.contains("EAP_SUITE_B_192"); 1760 final boolean isEap = result.capabilities.contains("EAP"); 1761 final boolean isOwe = result.capabilities.contains("OWE"); 1762 final boolean isOweTransition = result.capabilities.contains("OWE_TRANSITION"); 1763 1764 if (isSae && isPsk) { 1765 final WifiManager wifiManager = (WifiManager) 1766 context.getSystemService(Context.WIFI_SERVICE); 1767 return wifiManager.isWpa3SaeSupported() ? SECURITY_SAE : SECURITY_PSK; 1768 } 1769 if (isOweTransition) { 1770 final WifiManager wifiManager = (WifiManager) 1771 context.getSystemService(Context.WIFI_SERVICE); 1772 return wifiManager.isEnhancedOpenSupported() ? SECURITY_OWE : SECURITY_NONE; 1773 } 1774 1775 if (isWep) { 1776 return SECURITY_WEP; 1777 } else if (isSae) { 1778 return SECURITY_SAE; 1779 } else if (isPsk) { 1780 return SECURITY_PSK; 1781 } else if (isEapSuiteB192) { 1782 return SECURITY_EAP_SUITE_B; 1783 } else if (isEap) { 1784 return SECURITY_EAP; 1785 } else if (isOwe) { 1786 return SECURITY_OWE; 1787 } 1788 return SECURITY_NONE; 1789 } 1790 getSecurity(WifiConfiguration config)1791 static int getSecurity(WifiConfiguration config) { 1792 if (config.allowedKeyManagement.get(KeyMgmt.SAE)) { 1793 return SECURITY_SAE; 1794 } 1795 if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 1796 return SECURITY_PSK; 1797 } 1798 if (config.allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) { 1799 return SECURITY_EAP_SUITE_B; 1800 } 1801 if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) || 1802 config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 1803 return SECURITY_EAP; 1804 } 1805 if (config.allowedKeyManagement.get(KeyMgmt.OWE)) { 1806 return SECURITY_OWE; 1807 } 1808 return (config.wepTxKeyIndex >= 0 1809 && config.wepTxKeyIndex < config.wepKeys.length 1810 && config.wepKeys[config.wepTxKeyIndex] != null) 1811 ? SECURITY_WEP : SECURITY_NONE; 1812 } 1813 securityToString(int security, int pskType)1814 public static String securityToString(int security, int pskType) { 1815 if (security == SECURITY_WEP) { 1816 return "WEP"; 1817 } else if (security == SECURITY_PSK) { 1818 if (pskType == PSK_WPA) { 1819 return "WPA"; 1820 } else if (pskType == PSK_WPA2) { 1821 return "WPA2"; 1822 } else if (pskType == PSK_WPA_WPA2) { 1823 return "WPA_WPA2"; 1824 } 1825 return "PSK"; 1826 } else if (security == SECURITY_EAP) { 1827 return "EAP"; 1828 } else if (security == SECURITY_SAE) { 1829 return "SAE"; 1830 } else if (security == SECURITY_EAP_SUITE_B) { 1831 return "SUITE_B"; 1832 } else if (security == SECURITY_OWE) { 1833 return "OWE"; 1834 } 1835 return "NONE"; 1836 } 1837 removeDoubleQuotes(String string)1838 static String removeDoubleQuotes(String string) { 1839 if (TextUtils.isEmpty(string)) { 1840 return ""; 1841 } 1842 int length = string.length(); 1843 if ((length > 1) && (string.charAt(0) == '"') 1844 && (string.charAt(length - 1) == '"')) { 1845 return string.substring(1, length - 1); 1846 } 1847 return string; 1848 } 1849 getWifiManager()1850 private WifiManager getWifiManager() { 1851 if (mWifiManager == null) { 1852 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 1853 } 1854 return mWifiManager; 1855 } 1856 1857 /** 1858 * Return true if this is an open network AccessPoint. 1859 */ isOpenNetwork()1860 public boolean isOpenNetwork() { 1861 return security == SECURITY_NONE || security == SECURITY_OWE; 1862 } 1863 1864 /** 1865 * Callbacks relaying changes to the AccessPoint representation. 1866 * 1867 * <p>All methods are invoked on the Main Thread. 1868 */ 1869 public interface AccessPointListener { 1870 1871 /** 1872 * Indicates a change to the externally visible state of the AccessPoint trigger by an 1873 * update of ScanResults, saved configuration state, connection state, or score 1874 * (labels/metered) state. 1875 * 1876 * <p>Clients should refresh their view of the AccessPoint to match the updated state when 1877 * this is invoked. Overall this method is extraneous if clients are listening to 1878 * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks. 1879 * 1880 * <p>Examples of changes include signal strength, connection state, speed label, and 1881 * generally anything that would impact the summary string. 1882 * 1883 * @param accessPoint The accessPoint object the listener was registered on which has 1884 * changed 1885 */ onAccessPointChanged(AccessPoint accessPoint)1886 @MainThread void onAccessPointChanged(AccessPoint accessPoint); 1887 /** 1888 * Indicates the "wifi pie signal level" has changed, retrieved via calls to 1889 * {@link AccessPoint#getLevel()}. 1890 * 1891 * <p>This call is a subset of {@link #onAccessPointChanged(AccessPoint)} , hence is also 1892 * extraneous if the client is already reacting to that or the 1893 * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks. 1894 * 1895 * @param accessPoint The accessPoint object the listener was registered on whose level has 1896 * changed 1897 */ onLevelChanged(AccessPoint accessPoint)1898 @MainThread void onLevelChanged(AccessPoint accessPoint); 1899 } 1900 isVerboseLoggingEnabled()1901 private static boolean isVerboseLoggingEnabled() { 1902 return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE); 1903 } 1904 1905 /** 1906 * Callbacks relaying changes to the OSU provisioning status started in startOsuProvisioning(). 1907 * 1908 * All methods are invoked on the Main Thread 1909 */ 1910 @VisibleForTesting 1911 class AccessPointProvisioningCallback extends ProvisioningCallback { 1912 @Override onProvisioningFailure(int status)1913 @MainThread public void onProvisioningFailure(int status) { 1914 if (TextUtils.equals(mOsuStatus, mContext.getString(R.string.osu_completing_sign_up))) { 1915 mOsuFailure = mContext.getString(R.string.osu_sign_up_failed); 1916 } else { 1917 mOsuFailure = mContext.getString(R.string.osu_connect_failed); 1918 } 1919 mOsuStatus = null; 1920 mOsuProvisioningComplete = false; 1921 ThreadUtils.postOnMainThread(() -> { 1922 if (mAccessPointListener != null) { 1923 mAccessPointListener.onAccessPointChanged(AccessPoint.this); 1924 } 1925 }); 1926 } 1927 1928 @Override onProvisioningStatus(int status)1929 @MainThread public void onProvisioningStatus(int status) { 1930 String newStatus = null; 1931 switch (status) { 1932 case OSU_STATUS_AP_CONNECTING: 1933 case OSU_STATUS_AP_CONNECTED: 1934 case OSU_STATUS_SERVER_CONNECTING: 1935 case OSU_STATUS_SERVER_VALIDATED: 1936 case OSU_STATUS_SERVER_CONNECTED: 1937 case OSU_STATUS_INIT_SOAP_EXCHANGE: 1938 case OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE: 1939 newStatus = String.format(mContext.getString(R.string.osu_opening_provider), 1940 mOsuProvider.getFriendlyName()); 1941 break; 1942 case OSU_STATUS_REDIRECT_RESPONSE_RECEIVED: 1943 case OSU_STATUS_SECOND_SOAP_EXCHANGE: 1944 case OSU_STATUS_THIRD_SOAP_EXCHANGE: 1945 case OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS: 1946 newStatus = mContext.getString( 1947 R.string.osu_completing_sign_up); 1948 break; 1949 } 1950 boolean updated = !TextUtils.equals(mOsuStatus, newStatus); 1951 mOsuStatus = newStatus; 1952 mOsuFailure = null; 1953 mOsuProvisioningComplete = false; 1954 if (updated) { 1955 ThreadUtils.postOnMainThread(() -> { 1956 if (mAccessPointListener != null) { 1957 mAccessPointListener.onAccessPointChanged(AccessPoint.this); 1958 } 1959 }); 1960 } 1961 } 1962 1963 @Override onProvisioningComplete()1964 @MainThread public void onProvisioningComplete() { 1965 mOsuProvisioningComplete = true; 1966 mOsuFailure = null; 1967 mOsuStatus = null; 1968 1969 ThreadUtils.postOnMainThread(() -> { 1970 if (mAccessPointListener != null) { 1971 mAccessPointListener.onAccessPointChanged(AccessPoint.this); 1972 } 1973 }); 1974 1975 // Connect to the freshly provisioned network. 1976 WifiManager wifiManager = getWifiManager(); 1977 1978 PasspointConfiguration passpointConfig = wifiManager 1979 .getMatchingPasspointConfigsForOsuProviders(Collections.singleton(mOsuProvider)) 1980 .get(mOsuProvider); 1981 if (passpointConfig == null) { 1982 Log.e(TAG, "Missing PasspointConfiguration for newly provisioned network!"); 1983 if (mConnectListener != null) { 1984 mConnectListener.onFailure(0); 1985 } 1986 return; 1987 } 1988 1989 String uniqueId = passpointConfig.getUniqueId(); 1990 for (Pair<WifiConfiguration, Map<Integer, List<ScanResult>>> pairing : 1991 wifiManager.getAllMatchingWifiConfigs(wifiManager.getScanResults())) { 1992 WifiConfiguration config = pairing.first; 1993 if (TextUtils.equals(config.getKey(), uniqueId)) { 1994 List<ScanResult> homeScans = 1995 pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK); 1996 List<ScanResult> roamingScans = 1997 pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK); 1998 1999 AccessPoint connectionAp = 2000 new AccessPoint(mContext, config, homeScans, roamingScans); 2001 wifiManager.connect(connectionAp.getConfig(), mConnectListener); 2002 return; 2003 } 2004 } 2005 if (mConnectListener != null) { 2006 mConnectListener.onFailure(0); 2007 } 2008 } 2009 } 2010 isPskSaeTransitionMode()2011 public boolean isPskSaeTransitionMode() { 2012 return mIsPskSaeTransitionMode; 2013 } 2014 isOweTransitionMode()2015 public boolean isOweTransitionMode() { 2016 return mIsOweTransitionMode; 2017 } 2018 isPskSaeTransitionMode(ScanResult scanResult)2019 private static boolean isPskSaeTransitionMode(ScanResult scanResult) { 2020 return scanResult.capabilities.contains("PSK") 2021 && scanResult.capabilities.contains("SAE"); 2022 } 2023 isOweTransitionMode(ScanResult scanResult)2024 private static boolean isOweTransitionMode(ScanResult scanResult) { 2025 return scanResult.capabilities.contains("OWE_TRANSITION"); 2026 } 2027 isSameSsidOrBssid(ScanResult scanResult)2028 private boolean isSameSsidOrBssid(ScanResult scanResult) { 2029 if (scanResult == null) { 2030 return false; 2031 } 2032 2033 if (TextUtils.equals(ssid, scanResult.SSID)) { 2034 return true; 2035 } else if (scanResult.BSSID != null && TextUtils.equals(bssid, scanResult.BSSID)) { 2036 return true; 2037 } 2038 return false; 2039 } 2040 isSameSsidOrBssid(WifiInfo wifiInfo)2041 private boolean isSameSsidOrBssid(WifiInfo wifiInfo) { 2042 if (wifiInfo == null) { 2043 return false; 2044 } 2045 2046 if (TextUtils.equals(ssid, removeDoubleQuotes(wifiInfo.getSSID()))) { 2047 return true; 2048 } else if (wifiInfo.getBSSID() != null && TextUtils.equals(bssid, wifiInfo.getBSSID())) { 2049 return true; 2050 } 2051 return false; 2052 } 2053 isSameSsidOrBssid(AccessPoint accessPoint)2054 private boolean isSameSsidOrBssid(AccessPoint accessPoint) { 2055 if (accessPoint == null) { 2056 return false; 2057 } 2058 2059 if (TextUtils.equals(ssid, accessPoint.getSsid())) { 2060 return true; 2061 } else if (accessPoint.getBssid() != null 2062 && TextUtils.equals(bssid, accessPoint.getBssid())) { 2063 return true; 2064 } 2065 return false; 2066 } 2067 } 2068