1 /* 2 * Copyright (C) 2019 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.wifitrackerlib; 18 19 import static android.net.wifi.WifiInfo.INVALID_RSSI; 20 21 import static androidx.core.util.Preconditions.checkNotNull; 22 23 import static com.android.wifitrackerlib.Utils.getNetworkPart; 24 import static com.android.wifitrackerlib.Utils.getSingleSecurityTypeFromMultipleSecurityTypes; 25 26 import android.content.Context; 27 import android.net.ConnectivityDiagnosticsManager; 28 import android.net.ConnectivityManager; 29 import android.net.LinkAddress; 30 import android.net.LinkProperties; 31 import android.net.Network; 32 import android.net.NetworkCapabilities; 33 import android.net.NetworkInfo; 34 import android.net.RouteInfo; 35 import android.net.wifi.ScanResult; 36 import android.net.wifi.WifiConfiguration; 37 import android.net.wifi.WifiInfo; 38 import android.net.wifi.WifiManager; 39 import android.os.Handler; 40 import android.text.TextUtils; 41 import android.util.Log; 42 43 import androidx.annotation.AnyThread; 44 import androidx.annotation.IntDef; 45 import androidx.annotation.MainThread; 46 import androidx.annotation.NonNull; 47 import androidx.annotation.Nullable; 48 import androidx.annotation.WorkerThread; 49 import androidx.core.os.BuildCompat; 50 51 import java.lang.annotation.Retention; 52 import java.lang.annotation.RetentionPolicy; 53 import java.net.Inet4Address; 54 import java.net.Inet6Address; 55 import java.net.InetAddress; 56 import java.net.UnknownHostException; 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.Collections; 60 import java.util.Comparator; 61 import java.util.List; 62 import java.util.Optional; 63 import java.util.StringJoiner; 64 import java.util.stream.Collectors; 65 66 /** 67 * Base class for an entry representing a Wi-Fi network in a Wi-Fi picker/settings. 68 * Subclasses should override the default methods for their own needs. 69 * 70 * Clients implementing a Wi-Fi picker/settings should receive WifiEntry objects from classes 71 * implementing BaseWifiTracker, and rely on the given API for all user-displayable information and 72 * actions on the represented network. 73 */ 74 public class WifiEntry { 75 public static final String TAG = "WifiEntry"; 76 77 private static final int MAX_UNDERLYING_NETWORK_DEPTH = 5; 78 79 /** 80 * Security type based on WifiConfiguration.KeyMgmt 81 */ 82 @Retention(RetentionPolicy.SOURCE) 83 @IntDef(value = { 84 SECURITY_NONE, 85 SECURITY_OWE, 86 SECURITY_WEP, 87 SECURITY_PSK, 88 SECURITY_SAE, 89 SECURITY_EAP, 90 SECURITY_EAP_SUITE_B, 91 SECURITY_EAP_WPA3_ENTERPRISE, 92 }) 93 94 public @interface Security {} 95 96 public static final int SECURITY_NONE = 0; 97 public static final int SECURITY_WEP = 1; 98 public static final int SECURITY_PSK = 2; 99 public static final int SECURITY_EAP = 3; 100 public static final int SECURITY_OWE = 4; 101 public static final int SECURITY_SAE = 5; 102 public static final int SECURITY_EAP_SUITE_B = 6; 103 public static final int SECURITY_EAP_WPA3_ENTERPRISE = 7; 104 105 public static final int NUM_SECURITY_TYPES = 8; 106 107 @Retention(RetentionPolicy.SOURCE) 108 @IntDef(value = { 109 CONNECTED_STATE_DISCONNECTED, 110 CONNECTED_STATE_CONNECTED, 111 CONNECTED_STATE_CONNECTING 112 }) 113 114 public @interface ConnectedState {} 115 116 public static final int CONNECTED_STATE_DISCONNECTED = 0; 117 public static final int CONNECTED_STATE_CONNECTING = 1; 118 public static final int CONNECTED_STATE_CONNECTED = 2; 119 120 // Wi-Fi signal levels for displaying signal strength. 121 public static final int WIFI_LEVEL_MIN = 0; 122 public static final int WIFI_LEVEL_MAX = 4; 123 public static final int WIFI_LEVEL_UNREACHABLE = -1; 124 125 @Retention(RetentionPolicy.SOURCE) 126 @IntDef(value = { 127 METERED_CHOICE_AUTO, 128 METERED_CHOICE_METERED, 129 METERED_CHOICE_UNMETERED, 130 }) 131 132 public @interface MeteredChoice {} 133 134 // User's choice whether to treat a network as metered. 135 public static final int METERED_CHOICE_AUTO = 0; 136 public static final int METERED_CHOICE_METERED = 1; 137 public static final int METERED_CHOICE_UNMETERED = 2; 138 139 @Retention(RetentionPolicy.SOURCE) 140 @IntDef(value = { 141 PRIVACY_DEVICE_MAC, 142 PRIVACY_RANDOMIZED_MAC, 143 PRIVACY_UNKNOWN 144 }) 145 146 public @interface Privacy {} 147 148 public static final int PRIVACY_DEVICE_MAC = 0; 149 public static final int PRIVACY_RANDOMIZED_MAC = 1; 150 public static final int PRIVACY_UNKNOWN = 2; 151 152 @Retention(RetentionPolicy.SOURCE) 153 @IntDef(value = { 154 FREQUENCY_2_4_GHZ, 155 FREQUENCY_5_GHZ, 156 FREQUENCY_6_GHZ, 157 FREQUENCY_60_GHZ, 158 FREQUENCY_UNKNOWN 159 }) 160 161 public @interface Frequency {} 162 163 public static final int FREQUENCY_2_4_GHZ = 2_400; 164 public static final int FREQUENCY_5_GHZ = 5_000; 165 public static final int FREQUENCY_6_GHZ = 6_000; 166 public static final int FREQUENCY_60_GHZ = 60_000; 167 public static final int FREQUENCY_UNKNOWN = -1; 168 169 /** 170 * Min bound on the 2.4 GHz (802.11b/g/n) WLAN channels. 171 */ 172 public static final int MIN_FREQ_24GHZ = 2400; 173 174 /** 175 * Max bound on the 2.4 GHz (802.11b/g/n) WLAN channels. 176 */ 177 public static final int MAX_FREQ_24GHZ = 2500; 178 179 /** 180 * Min bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels. 181 */ 182 public static final int MIN_FREQ_5GHZ = 4900; 183 184 /** 185 * Max bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels. 186 */ 187 public static final int MAX_FREQ_5GHZ = 5900; 188 189 /** 190 * Min bound on the 6.0 GHz (802.11ax) WLAN channels. 191 */ 192 public static final int MIN_FREQ_6GHZ = 5925; 193 194 /** 195 * Max bound on the 6.0 GHz (802.11ax) WLAN channels. 196 */ 197 public static final int MAX_FREQ_6GHZ = 7125; 198 199 /** 200 * Min bound on the 60 GHz (802.11ad) WLAN channels. 201 */ 202 public static final int MIN_FREQ_60GHZ = 58320; 203 204 /** 205 * Max bound on the 60 GHz (802.11ad) WLAN channels. 206 */ 207 public static final int MAX_FREQ_60GHZ = 70200; 208 209 /** 210 * Max ScanResult information displayed of Wi-Fi Verbose Logging. 211 */ 212 protected static final int MAX_VERBOSE_LOG_DISPLAY_SCANRESULT_COUNT = 4; 213 214 /** 215 * Default comparator for sorting WifiEntries on a Wi-Fi picker list. 216 */ 217 public static Comparator<WifiEntry> WIFI_PICKER_COMPARATOR = 218 Comparator.comparing((WifiEntry entry) -> !entry.isPrimaryNetwork()) 219 .thenComparing((WifiEntry entry) -> 220 entry.getConnectedState() != CONNECTED_STATE_CONNECTED) 221 .thenComparing((WifiEntry entry) -> !(entry instanceof KnownNetworkEntry)) 222 .thenComparing((WifiEntry entry) -> !(entry instanceof HotspotNetworkEntry)) 223 .thenComparing((WifiEntry entry) -> (entry instanceof HotspotNetworkEntry) 224 ? -((HotspotNetworkEntry) entry).getUpstreamConnectionStrength() : 0) 225 .thenComparing((WifiEntry entry) -> !entry.canConnect()) 226 .thenComparing((WifiEntry entry) -> !entry.isSubscription()) 227 .thenComparing((WifiEntry entry) -> !entry.isSaved()) 228 .thenComparing((WifiEntry entry) -> !entry.isSuggestion()) 229 .thenComparing((WifiEntry entry) -> -entry.getLevel()) 230 .thenComparing((WifiEntry entry) -> entry.getTitle()); 231 232 /** 233 * Default comparator for sorting WifiEntries by title. 234 */ 235 public static Comparator<WifiEntry> TITLE_COMPARATOR = 236 Comparator.comparing((WifiEntry entry) -> entry.getTitle()); 237 238 protected final boolean mForSavedNetworksPage; 239 240 @NonNull protected final WifiTrackerInjector mInjector; 241 @NonNull protected final Context mContext; 242 protected final WifiManager mWifiManager; 243 244 // Callback associated with this WifiEntry. Subclasses should call its methods appropriately. 245 private WifiEntryCallback mListener; 246 protected final Handler mCallbackHandler; 247 protected int mWifiInfoLevel = WIFI_LEVEL_UNREACHABLE; 248 protected int mScanResultLevel = WIFI_LEVEL_UNREACHABLE; 249 protected WifiInfo mWifiInfo; 250 protected NetworkInfo mNetworkInfo; 251 protected Network mNetwork; 252 protected Network mLastNetwork; 253 protected NetworkCapabilities mNetworkCapabilities; 254 protected Network mDefaultNetwork; 255 protected NetworkCapabilities mDefaultNetworkCapabilities; 256 protected ConnectivityDiagnosticsManager.ConnectivityReport mConnectivityReport; 257 protected ConnectedInfo mConnectedInfo; 258 259 protected ConnectCallback mConnectCallback; 260 protected DisconnectCallback mDisconnectCallback; 261 protected ForgetCallback mForgetCallback; 262 263 protected boolean mCalledConnect = false; 264 protected boolean mCalledDisconnect = false; 265 266 267 private Optional<ManageSubscriptionAction> mManageSubscriptionAction = Optional.empty(); 268 WifiEntry(@onNull WifiTrackerInjector injector, @NonNull Handler callbackHandler, @NonNull WifiManager wifiManager, boolean forSavedNetworksPage)269 public WifiEntry(@NonNull WifiTrackerInjector injector, @NonNull Handler callbackHandler, 270 @NonNull WifiManager wifiManager, boolean forSavedNetworksPage) 271 throws IllegalArgumentException { 272 checkNotNull(injector, "Cannot construct with null injector!"); 273 checkNotNull(callbackHandler, "Cannot construct with null handler!"); 274 checkNotNull(wifiManager, "Cannot construct with null WifiManager!"); 275 mInjector = injector; 276 mContext = mInjector.getContext(); 277 mCallbackHandler = callbackHandler; 278 mForSavedNetworksPage = forSavedNetworksPage; 279 mWifiManager = wifiManager; 280 } 281 282 // Info available for all WifiEntries // 283 284 /** The unique key defining a WifiEntry */ 285 @NonNull getKey()286 public String getKey() { 287 return ""; 288 }; 289 290 /** Returns connection state of the network defined by the CONNECTED_STATE constants */ 291 @ConnectedState getConnectedState()292 public synchronized int getConnectedState() { 293 // If we have NetworkCapabilities, then we're L3 connected. 294 if (mNetworkCapabilities != null) { 295 return CONNECTED_STATE_CONNECTED; 296 } 297 298 // Use NetworkInfo to provide the connecting state before we're L3 connected. 299 if (mNetworkInfo != null) { 300 switch (mNetworkInfo.getDetailedState()) { 301 case SCANNING: 302 case CONNECTING: 303 case AUTHENTICATING: 304 case OBTAINING_IPADDR: 305 case VERIFYING_POOR_LINK: 306 case CAPTIVE_PORTAL_CHECK: 307 case CONNECTED: 308 return CONNECTED_STATE_CONNECTING; 309 default: 310 return CONNECTED_STATE_DISCONNECTED; 311 } 312 } 313 314 return CONNECTED_STATE_DISCONNECTED; 315 } 316 317 /** Returns the display title. This is most commonly the SSID of a network. */ 318 @NonNull getTitle()319 public String getTitle() { 320 return ""; 321 } 322 323 /** Returns the display summary, it's a concise summary. */ 324 @NonNull getSummary()325 public String getSummary() { 326 return getSummary(true /* concise */); 327 } 328 329 /** Returns the second summary, it's for additional information of the WifiEntry */ 330 @NonNull getSecondSummary()331 public CharSequence getSecondSummary() { 332 return ""; 333 } 334 335 /** 336 * Returns the display summary. 337 * @param concise Whether to show more information. e.g., verbose logging. 338 */ 339 @NonNull getSummary(boolean concise)340 public String getSummary(boolean concise) { 341 return ""; 342 }; 343 344 /** 345 * Returns the signal strength level within [WIFI_LEVEL_MIN, WIFI_LEVEL_MAX]. 346 * A value of WIFI_LEVEL_UNREACHABLE indicates an out of range network. 347 */ getLevel()348 public int getLevel() { 349 if (mWifiInfoLevel != WIFI_LEVEL_UNREACHABLE) { 350 return mWifiInfoLevel; 351 } 352 return mScanResultLevel; 353 }; 354 355 /** 356 * Returns whether the level icon for this network should show an X or not. 357 * By default, this means any connected network that has no/low-quality internet access. 358 */ shouldShowXLevelIcon()359 public boolean shouldShowXLevelIcon() { 360 return getConnectedState() != CONNECTED_STATE_DISCONNECTED 361 && mConnectivityReport != null 362 && (!hasInternetAccess() || isLowQuality()) 363 && !canSignIn() 364 && isPrimaryNetwork(); 365 } 366 367 /** 368 * Returns whether this network has validated internet access or not. 369 * Note: This does not necessarily mean the network is the default route. 370 */ hasInternetAccess()371 public synchronized boolean hasInternetAccess() { 372 return mNetworkCapabilities != null 373 && mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); 374 } 375 376 /** 377 * Returns whether this network is the default network or not (i.e. this network is the one 378 * currently being used to provide internet connection). 379 */ isDefaultNetwork()380 public synchronized boolean isDefaultNetwork() { 381 if (mNetwork != null && mNetwork.equals(mDefaultNetwork)) { 382 return true; 383 } 384 385 if (mLastNetwork != null && mLastNetwork.equals(mDefaultNetwork)) { 386 // Last network may still be default if we've roamed and haven't gotten 387 // onNetworkCapabilitiesChanged for the default network yet, so consider it default for 388 // now. 389 return true; 390 } 391 392 // Match based on the underlying networks if there are any (e.g. VPN). 393 return doesUnderlyingNetworkMatch(mDefaultNetworkCapabilities, 0); 394 } 395 doesUnderlyingNetworkMatch(@ullable NetworkCapabilities caps, int depth)396 private boolean doesUnderlyingNetworkMatch(@Nullable NetworkCapabilities caps, int depth) { 397 if (depth > MAX_UNDERLYING_NETWORK_DEPTH) { 398 Log.e(TAG, "Underlying network depth greater than max depth of " 399 + MAX_UNDERLYING_NETWORK_DEPTH); 400 return false; 401 } 402 403 if (caps == null) { 404 return false; 405 } 406 407 List<Network> underlyingNetworks = BuildCompat.isAtLeastT() 408 ? caps.getUnderlyingNetworks() : null; 409 if (underlyingNetworks == null) { 410 return false; 411 } 412 if (underlyingNetworks.contains(mNetwork)) { 413 return true; 414 } 415 416 // Check the underlying networks of the underlying networks. 417 ConnectivityManager connectivityManager = mInjector.getConnectivityManager(); 418 if (connectivityManager == null) { 419 Log.wtf(TAG, "ConnectivityManager is null!"); 420 return false; 421 } 422 for (Network underlying : underlyingNetworks) { 423 if (doesUnderlyingNetworkMatch( 424 connectivityManager.getNetworkCapabilities(underlying), depth + 1)) { 425 return true; 426 } 427 } 428 return false; 429 } 430 431 /** 432 * Returns whether this network is the primary Wi-Fi network or not. 433 */ isPrimaryNetwork()434 public synchronized boolean isPrimaryNetwork() { 435 if (getConnectedState() == CONNECTED_STATE_DISCONNECTED) { 436 // In case we have mNetworkInfo but the state is disconnected. 437 return false; 438 } 439 return mNetworkInfo != null 440 || (mWifiInfo != null && NonSdkApiWrapper.isPrimary(mWifiInfo)); 441 } 442 443 /** 444 * Returns whether this network is considered low quality. 445 */ isLowQuality()446 public synchronized boolean isLowQuality() { 447 return isPrimaryNetwork() && hasInternetAccess() && !isDefaultNetwork() 448 && mNetworkCapabilities != null 449 && mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 450 && mDefaultNetworkCapabilities != null 451 && mDefaultNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) 452 && !mDefaultNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) 453 && mDefaultNetworkCapabilities.hasCapability( 454 NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 455 } 456 457 /** 458 * Returns whether this network should display its SSID separately from the title 459 * (e.g. the Network Details page), for networks whose display titles differ from the SSID. 460 */ shouldShowSsid()461 public boolean shouldShowSsid() { 462 return false; 463 } 464 465 /** 466 * Returns the SSID of the entry, if applicable. Null otherwise. 467 */ 468 @Nullable getSsid()469 public String getSsid() { 470 return null; 471 } 472 473 /** 474 * Returns the security type defined by the SECURITY constants 475 * @deprecated Use getSecurityTypes() which can return multiple security types. 476 */ 477 // TODO(b/187554920): Remove this and move all clients to getSecurityTypes() 478 @Deprecated 479 @Security getSecurity()480 public int getSecurity() { 481 switch (getSingleSecurityTypeFromMultipleSecurityTypes(getSecurityTypes())) { 482 case WifiInfo.SECURITY_TYPE_OPEN: 483 return SECURITY_NONE; 484 case WifiInfo.SECURITY_TYPE_OWE: 485 return SECURITY_OWE; 486 case WifiInfo.SECURITY_TYPE_WEP: 487 return SECURITY_WEP; 488 case WifiInfo.SECURITY_TYPE_PSK: 489 return SECURITY_PSK; 490 case WifiInfo.SECURITY_TYPE_SAE: 491 return SECURITY_SAE; 492 case WifiInfo.SECURITY_TYPE_EAP: 493 return SECURITY_EAP; 494 case WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE: 495 return SECURITY_EAP_WPA3_ENTERPRISE; 496 case WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT: 497 return SECURITY_EAP_SUITE_B; 498 case WifiInfo.SECURITY_TYPE_PASSPOINT_R1_R2: 499 case WifiInfo.SECURITY_TYPE_PASSPOINT_R3: 500 return SECURITY_EAP; 501 default: 502 return SECURITY_NONE; 503 } 504 } 505 506 /** 507 * Returns security type of the current connection, or the available types for connection 508 * in the form of the SECURITY_TYPE_* values in {@link WifiInfo} 509 */ 510 @NonNull getSecurityTypes()511 public List<Integer> getSecurityTypes() { 512 return Collections.emptyList(); 513 } 514 515 /** Returns the MAC address of the connection */ 516 @Nullable getMacAddress()517 public String getMacAddress() { 518 return null; 519 } 520 521 /** 522 * Indicates when a network is metered or the user marked the network as metered. 523 */ isMetered()524 public boolean isMetered() { 525 return false; 526 } 527 528 /** 529 * Indicates whether or not an entry is for a saved configuration. 530 */ isSaved()531 public boolean isSaved() { 532 return false; 533 } 534 535 /** 536 * Indicates whether or not an entry is for a saved configuration. 537 */ isSuggestion()538 public boolean isSuggestion() { 539 return false; 540 } 541 542 /** 543 * Indicates whether or not an entry is for a subscription. 544 */ isSubscription()545 public boolean isSubscription() { 546 return false; 547 } 548 549 /** 550 * Returns whether this entry needs to be configured with a new WifiConfiguration before 551 * connection. 552 */ needsWifiConfiguration()553 public boolean needsWifiConfiguration() { 554 return false; 555 } 556 557 /** 558 * Returns the WifiConfiguration of an entry or null if unavailable. This should be used when 559 * information on the WifiConfiguration needs to be modified and saved via 560 * {@link WifiManager#save(WifiConfiguration, WifiManager.ActionListener)}. 561 */ 562 @Nullable getWifiConfiguration()563 public WifiConfiguration getWifiConfiguration() { 564 return null; 565 } 566 567 /** 568 * Returns the ConnectedInfo object pertaining to an active connection. 569 * 570 * Returns null if getConnectedState() != CONNECTED_STATE_CONNECTED. 571 */ 572 @Nullable getConnectedInfo()573 public synchronized ConnectedInfo getConnectedInfo() { 574 if (getConnectedState() != CONNECTED_STATE_CONNECTED) { 575 return null; 576 } 577 578 return new ConnectedInfo(mConnectedInfo); 579 } 580 581 /** 582 * Info associated with the active connection. 583 */ 584 public static class ConnectedInfo { 585 @Frequency 586 public int frequencyMhz; 587 public List<String> dnsServers = new ArrayList<>(); 588 public int linkSpeedMbps; 589 public String ipAddress; 590 public List<String> ipv6Addresses = new ArrayList<>(); 591 public String gateway; 592 public String subnetMask; 593 public int wifiStandard = ScanResult.WIFI_STANDARD_UNKNOWN; 594 public NetworkCapabilities networkCapabilities; 595 596 /** 597 * Creates an empty ConnectedInfo 598 */ ConnectedInfo()599 public ConnectedInfo() { 600 } 601 602 /** 603 * Creates a ConnectedInfo with all fields copied from an input ConnectedInfo 604 */ ConnectedInfo(@onNull ConnectedInfo other)605 public ConnectedInfo(@NonNull ConnectedInfo other) { 606 frequencyMhz = other.frequencyMhz; 607 dnsServers = new ArrayList<>(dnsServers); 608 linkSpeedMbps = other.linkSpeedMbps; 609 ipAddress = other.ipAddress; 610 ipv6Addresses = new ArrayList<>(other.ipv6Addresses); 611 gateway = other.gateway; 612 subnetMask = other.subnetMask; 613 wifiStandard = other.wifiStandard; 614 networkCapabilities = other.networkCapabilities; 615 } 616 } 617 618 // User actions on a network 619 620 /** Returns whether the entry should show a connect option */ canConnect()621 public boolean canConnect() { 622 return false; 623 } 624 625 /** Connects to the network */ connect(@ullable ConnectCallback callback)626 public void connect(@Nullable ConnectCallback callback) { 627 // Do nothing. 628 } 629 630 /** Returns whether the entry should show a disconnect option */ canDisconnect()631 public boolean canDisconnect() { 632 return false; 633 } 634 635 /** Disconnects from the network */ disconnect(@ullable DisconnectCallback callback)636 public void disconnect(@Nullable DisconnectCallback callback) { 637 // Do nothing. 638 } 639 640 /** Returns whether the entry should show a forget option */ canForget()641 public boolean canForget() { 642 return false; 643 } 644 645 /** Forgets the network */ forget(@ullable ForgetCallback callback)646 public void forget(@Nullable ForgetCallback callback) { 647 // Do nothing. 648 } 649 650 /** Returns whether the network can be signed-in to */ canSignIn()651 public boolean canSignIn() { 652 return false; 653 } 654 655 /** Sign-in to the network. For captive portals. */ signIn(@ullable SignInCallback callback)656 public void signIn(@Nullable SignInCallback callback) { 657 // Do nothing. 658 } 659 660 /** Returns whether the network can be shared via QR code */ canShare()661 public boolean canShare() { 662 return false; 663 } 664 665 /** Returns whether the user can use Easy Connect to onboard a device to the network */ canEasyConnect()666 public boolean canEasyConnect() { 667 return false; 668 } 669 670 // Modifiable settings 671 672 /** 673 * Returns the user's choice whether to treat a network as metered, 674 * defined by the METERED_CHOICE constants 675 */ 676 @MeteredChoice getMeteredChoice()677 public int getMeteredChoice() { 678 return METERED_CHOICE_AUTO; 679 } 680 681 /** Returns whether the entry should let the user choose the metered treatment of a network */ canSetMeteredChoice()682 public boolean canSetMeteredChoice() { 683 return false; 684 } 685 686 /** 687 * Sets the user's choice for treating a network as metered, 688 * defined by the METERED_CHOICE constants 689 */ setMeteredChoice(@eteredChoice int meteredChoice)690 public void setMeteredChoice(@MeteredChoice int meteredChoice) { 691 // Do nothing. 692 } 693 694 /** Returns whether the entry should let the user choose the MAC randomization setting */ canSetPrivacy()695 public boolean canSetPrivacy() { 696 return false; 697 } 698 699 /** Returns the MAC randomization setting defined by the PRIVACY constants */ 700 @Privacy getPrivacy()701 public int getPrivacy() { 702 return PRIVACY_UNKNOWN; 703 } 704 705 /** Sets the user's choice for MAC randomization defined by the PRIVACY constants */ setPrivacy(@rivacy int privacy)706 public void setPrivacy(@Privacy int privacy) { 707 // Do nothing. 708 } 709 710 /** Returns whether the network has auto-join enabled */ isAutoJoinEnabled()711 public boolean isAutoJoinEnabled() { 712 return false; 713 } 714 715 /** Returns whether the user can enable/disable auto-join */ canSetAutoJoinEnabled()716 public boolean canSetAutoJoinEnabled() { 717 return false; 718 } 719 720 /** Sets whether a network will be auto-joined or not */ setAutoJoinEnabled(boolean enabled)721 public void setAutoJoinEnabled(boolean enabled) { 722 // Do nothing. 723 } 724 725 /** Returns the string displayed for @Security */ getSecurityString(boolean concise)726 public String getSecurityString(boolean concise) { 727 return ""; 728 } 729 730 /** Returns the string displayed for the Wi-Fi standard */ getStandardString()731 public String getStandardString() { 732 return ""; 733 } 734 735 /** 736 * Info associated with the certificate based enterprise connection 737 */ 738 public static class CertificateInfo { 739 /** 740 * Server certificate validation method. Used to show the security certificate strings in 741 * the Network Details page. 742 */ 743 @Retention(RetentionPolicy.SOURCE) 744 @IntDef(value = { 745 CERTIFICATE_VALIDATION_METHOD_USING_NONE, 746 CERTIFICATE_VALIDATION_METHOD_USING_INSTALLED_ROOTCA, 747 CERTIFICATE_VALIDATION_METHOD_USING_SYSTEM_CERTIFICATE, 748 CERTIFICATE_VALIDATION_METHOD_USING_CERTIFICATE_PINNING, 749 }) 750 751 public @interface CertificateValidationMethod {} 752 public static final int CERTIFICATE_VALIDATION_METHOD_USING_NONE = 0; 753 public static final int CERTIFICATE_VALIDATION_METHOD_USING_INSTALLED_ROOTCA = 1; 754 public static final int CERTIFICATE_VALIDATION_METHOD_USING_SYSTEM_CERTIFICATE = 2; 755 public static final int CERTIFICATE_VALIDATION_METHOD_USING_CERTIFICATE_PINNING = 3; 756 757 public @CertificateValidationMethod int validationMethod; 758 759 /** Non null only for CERTIFICATE_VALIDATION_METHOD_USING_INSTALLED_ROOTCA */ 760 @Nullable public String[] caCertificateAliases; 761 762 /** Domain name / server name */ 763 @Nullable public String domain; 764 } 765 766 /** 767 * Returns the CertificateInfo to display, or null if it is not a certificate based connection. 768 */ 769 @Nullable getCertificateInfo()770 public CertificateInfo getCertificateInfo() { 771 return null; 772 } 773 774 /** Returns the string displayed for the Wi-Fi band */ getBandString()775 public String getBandString() { 776 return ""; 777 } 778 779 /** 780 * Returns the string displayed for Tx link speed. 781 */ getTxSpeedString()782 public String getTxSpeedString() { 783 return Utils.getSpeedString(mContext, mWifiInfo, /* isTx */ true); 784 } 785 786 /** 787 * Returns the string displayed for Rx link speed. 788 */ getRxSpeedString()789 public String getRxSpeedString() { 790 return Utils.getSpeedString(mContext, mWifiInfo, /* isTx */ false); 791 } 792 793 /** Returns whether subscription of the entry is expired */ isExpired()794 public boolean isExpired() { 795 return false; 796 } 797 798 799 /** Returns whether a user can manage their subscription through this WifiEntry */ canManageSubscription()800 public boolean canManageSubscription() { 801 return mManageSubscriptionAction.isPresent(); 802 }; 803 804 /** 805 * Return the URI string value of help, if it is not null, WifiPicker may show 806 * help icon and route the user to help page specified by the URI string. 807 * see {@link Intent#parseUri} 808 */ 809 @Nullable getHelpUriString()810 public String getHelpUriString() { 811 return null; 812 } 813 814 /** Allows the user to manage their subscription via an external flow */ manageSubscription()815 public void manageSubscription() { 816 mManageSubscriptionAction.ifPresent(ManageSubscriptionAction::onExecute); 817 }; 818 819 /** Set the action to be called on calling WifiEntry#manageSubscription. */ setManageSubscriptionAction( @onNull ManageSubscriptionAction manageSubscriptionAction)820 public void setManageSubscriptionAction( 821 @NonNull ManageSubscriptionAction manageSubscriptionAction) { 822 // only notify update on 1st time 823 boolean notify = !mManageSubscriptionAction.isPresent(); 824 825 mManageSubscriptionAction = Optional.of(manageSubscriptionAction); 826 if (notify) { 827 notifyOnUpdated(); 828 } 829 } 830 831 /** Returns the ScanResult information of a WifiEntry */ 832 @NonNull getScanResultDescription()833 protected String getScanResultDescription() { 834 return ""; 835 } 836 837 /** Returns the network selection information of a WifiEntry */ 838 @NonNull getNetworkSelectionDescription()839 String getNetworkSelectionDescription() { 840 return ""; 841 } 842 843 /** Returns the network capability information of a WifiEntry */ 844 @NonNull getNetworkCapabilityDescription()845 String getNetworkCapabilityDescription() { 846 final StringBuilder sb = new StringBuilder(); 847 if (getConnectedState() == CONNECTED_STATE_CONNECTED) { 848 sb.append("hasInternet:") 849 .append(hasInternetAccess()) 850 .append(", isDefaultNetwork:") 851 .append(isDefaultNetwork()) 852 .append(", isLowQuality:") 853 .append(isLowQuality()); 854 } 855 return sb.toString(); 856 } 857 858 /** 859 * In Wi-Fi picker, when users click a saved network, it will connect to the Wi-Fi network. 860 * However, for some special cases, Wi-Fi picker should show Wi-Fi editor UI for users to edit 861 * security or password before connecting. Or users will always get connection fail results. 862 */ shouldEditBeforeConnect()863 public boolean shouldEditBeforeConnect() { 864 return false; 865 } 866 867 /** 868 * Whether there are admin restrictions preventing connection to this network. 869 */ hasAdminRestrictions()870 public boolean hasAdminRestrictions() { 871 return false; 872 } 873 874 /** 875 * Sets the callback listener for WifiEntryCallback methods. 876 * Subsequent calls will overwrite the previous listener. 877 */ setListener(WifiEntryCallback listener)878 public synchronized void setListener(WifiEntryCallback listener) { 879 mListener = listener; 880 } 881 882 /** 883 * Listener for changes to the state of the WifiEntry. 884 * This callback will be invoked on the main thread. 885 */ 886 public interface WifiEntryCallback { 887 /** 888 * Indicates the state of the WifiEntry has changed and clients may retrieve updates through 889 * the WifiEntry getter methods. 890 */ 891 @MainThread onUpdated()892 void onUpdated(); 893 } 894 895 @AnyThread notifyOnUpdated()896 protected void notifyOnUpdated() { 897 if (mListener != null) { 898 mCallbackHandler.post(() -> { 899 final WifiEntryCallback listener = mListener; 900 if (listener != null) { 901 listener.onUpdated(); 902 } 903 }); 904 } 905 } 906 907 /** 908 * Listener for changes to the state of the WifiEntry. 909 * This callback will be invoked on the main thread. 910 */ 911 public interface ConnectCallback { 912 @Retention(RetentionPolicy.SOURCE) 913 @IntDef(value = { 914 CONNECT_STATUS_SUCCESS, 915 CONNECT_STATUS_FAILURE_NO_CONFIG, 916 CONNECT_STATUS_FAILURE_UNKNOWN, 917 CONNECT_STATUS_FAILURE_SIM_ABSENT 918 }) 919 920 public @interface ConnectStatus {} 921 922 int CONNECT_STATUS_SUCCESS = 0; 923 int CONNECT_STATUS_FAILURE_NO_CONFIG = 1; 924 int CONNECT_STATUS_FAILURE_UNKNOWN = 2; 925 int CONNECT_STATUS_FAILURE_SIM_ABSENT = 3; 926 927 /** 928 * Result of the connect request indicated by the CONNECT_STATUS constants. 929 */ 930 @MainThread onConnectResult(@onnectStatus int status)931 void onConnectResult(@ConnectStatus int status); 932 } 933 934 /** 935 * Listener for changes to the state of the WifiEntry. 936 * This callback will be invoked on the main thread. 937 */ 938 public interface DisconnectCallback { 939 @Retention(RetentionPolicy.SOURCE) 940 @IntDef(value = { 941 DISCONNECT_STATUS_SUCCESS, 942 DISCONNECT_STATUS_FAILURE_UNKNOWN 943 }) 944 945 public @interface DisconnectStatus {} 946 947 int DISCONNECT_STATUS_SUCCESS = 0; 948 int DISCONNECT_STATUS_FAILURE_UNKNOWN = 1; 949 /** 950 * Result of the disconnect request indicated by the DISCONNECT_STATUS constants. 951 */ 952 @MainThread onDisconnectResult(@isconnectStatus int status)953 void onDisconnectResult(@DisconnectStatus int status); 954 } 955 956 /** 957 * Listener for changes to the state of the WifiEntry. 958 * This callback will be invoked on the main thread. 959 */ 960 public interface ForgetCallback { 961 @Retention(RetentionPolicy.SOURCE) 962 @IntDef(value = { 963 FORGET_STATUS_SUCCESS, 964 FORGET_STATUS_FAILURE_UNKNOWN 965 }) 966 967 public @interface ForgetStatus {} 968 969 int FORGET_STATUS_SUCCESS = 0; 970 int FORGET_STATUS_FAILURE_UNKNOWN = 1; 971 972 /** 973 * Result of the forget request indicated by the FORGET_STATUS constants. 974 */ 975 @MainThread onForgetResult(@orgetStatus int status)976 void onForgetResult(@ForgetStatus int status); 977 } 978 979 /** 980 * Listener for changes to the state of the WifiEntry. 981 * This callback will be invoked on the main thread. 982 */ 983 public interface SignInCallback { 984 @Retention(RetentionPolicy.SOURCE) 985 @IntDef(value = { 986 SIGNIN_STATUS_SUCCESS, 987 SIGNIN_STATUS_FAILURE_UNKNOWN 988 }) 989 990 public @interface SignInStatus {} 991 992 int SIGNIN_STATUS_SUCCESS = 0; 993 int SIGNIN_STATUS_FAILURE_UNKNOWN = 1; 994 995 /** 996 * Result of the sign-in request indicated by the SIGNIN_STATUS constants. 997 */ 998 @MainThread onSignInResult(@ignInStatus int status)999 void onSignInResult(@SignInStatus int status); 1000 } 1001 1002 /** 1003 * Returns whether the supplied WifiInfo represents this WifiEntry 1004 */ connectionInfoMatches(@onNull WifiInfo wifiInfo)1005 boolean connectionInfoMatches(@NonNull WifiInfo wifiInfo) { 1006 return false; 1007 } 1008 1009 /** 1010 * Updates this WifiEntry with the given primary WifiInfo/NetworkInfo if they match. 1011 * @param primaryWifiInfo Primary WifiInfo that has changed 1012 * @param networkInfo NetworkInfo of the primary network if available 1013 */ onPrimaryWifiInfoChanged( @ullable WifiInfo primaryWifiInfo, @Nullable NetworkInfo networkInfo)1014 synchronized void onPrimaryWifiInfoChanged( 1015 @Nullable WifiInfo primaryWifiInfo, @Nullable NetworkInfo networkInfo) { 1016 if (primaryWifiInfo == null || !connectionInfoMatches(primaryWifiInfo)) { 1017 if (mNetworkInfo != null) { 1018 mNetworkInfo = null; 1019 notifyOnUpdated(); 1020 } 1021 return; 1022 } 1023 if (networkInfo != null) { 1024 mNetworkInfo = networkInfo; 1025 } 1026 updateWifiInfo(primaryWifiInfo); 1027 notifyOnUpdated(); 1028 } 1029 1030 /** 1031 * Updates this WifiEntry with the given NetworkCapabilities if it matches. 1032 */ 1033 @WorkerThread onNetworkCapabilitiesChanged( @onNull Network network, @NonNull NetworkCapabilities capabilities)1034 synchronized void onNetworkCapabilitiesChanged( 1035 @NonNull Network network, 1036 @NonNull NetworkCapabilities capabilities) { 1037 WifiInfo wifiInfo = Utils.getWifiInfo(capabilities); 1038 if (wifiInfo == null) { 1039 return; 1040 } 1041 1042 if (!connectionInfoMatches(wifiInfo)) { 1043 // WifiInfo doesn't match, so this network isn't for this WifiEntry. 1044 onNetworkLost(network); 1045 return; 1046 } 1047 1048 // Treat non-primary, non-OEM connections as disconnected. 1049 if (!NonSdkApiWrapper.isPrimary(wifiInfo) 1050 && !NonSdkApiWrapper.isOemCapabilities(capabilities)) { 1051 onNetworkLost(network); 1052 return; 1053 } 1054 1055 // Connection info matches, so the Network/NetworkCapabilities represent this network 1056 // and the network is currently connecting or connected. 1057 mLastNetwork = mNetwork; 1058 mNetwork = network; 1059 mNetworkCapabilities = capabilities; 1060 updateWifiInfo(wifiInfo); 1061 notifyOnUpdated(); 1062 } 1063 updateWifiInfo(WifiInfo wifiInfo)1064 protected synchronized void updateWifiInfo(WifiInfo wifiInfo) { 1065 if (wifiInfo == null) { 1066 mWifiInfo = null; 1067 mConnectedInfo = null; 1068 mWifiInfoLevel = WIFI_LEVEL_UNREACHABLE; 1069 updateSecurityTypes(); 1070 return; 1071 } 1072 mWifiInfo = wifiInfo; 1073 final int wifiInfoRssi = mWifiInfo.getRssi(); 1074 if (wifiInfoRssi != INVALID_RSSI) { 1075 mWifiInfoLevel = mWifiManager.calculateSignalLevel(wifiInfoRssi); 1076 } 1077 if (getConnectedState() == CONNECTED_STATE_CONNECTED) { 1078 if (mCalledConnect) { 1079 mCalledConnect = false; 1080 mCallbackHandler.post(() -> { 1081 final ConnectCallback connectCallback = mConnectCallback; 1082 if (connectCallback != null) { 1083 connectCallback.onConnectResult( 1084 ConnectCallback.CONNECT_STATUS_SUCCESS); 1085 } 1086 }); 1087 } 1088 1089 if (mConnectedInfo == null) { 1090 mConnectedInfo = new ConnectedInfo(); 1091 } 1092 mConnectedInfo.frequencyMhz = mWifiInfo.getFrequency(); 1093 mConnectedInfo.linkSpeedMbps = mWifiInfo.getLinkSpeed(); 1094 mConnectedInfo.wifiStandard = mWifiInfo.getWifiStandard(); 1095 } 1096 updateSecurityTypes(); 1097 } 1098 1099 /** 1100 * Updates this WifiEntry as disconnected if the network matches. 1101 * @param network Network that was lost 1102 */ onNetworkLost(@onNull Network network)1103 synchronized void onNetworkLost(@NonNull Network network) { 1104 if (network.equals(mNetwork)) { 1105 clearConnectionInfo(true); 1106 } else if (network.equals(mLastNetwork)) { 1107 mLastNetwork = null; 1108 notifyOnUpdated(); 1109 } 1110 } 1111 1112 /** 1113 * Clears any connection info from this entry. 1114 */ clearConnectionInfo(boolean notify)1115 synchronized void clearConnectionInfo(boolean notify) { 1116 updateWifiInfo(null); 1117 mNetwork = null; 1118 mLastNetwork = null; 1119 mNetworkInfo = null; 1120 mNetworkCapabilities = null; 1121 mConnectivityReport = null; 1122 if (mCalledDisconnect) { 1123 mCalledDisconnect = false; 1124 mCallbackHandler.post(() -> { 1125 final DisconnectCallback disconnectCallback = mDisconnectCallback; 1126 if (disconnectCallback != null) { 1127 disconnectCallback.onDisconnectResult( 1128 DisconnectCallback.DISCONNECT_STATUS_SUCCESS); 1129 } 1130 }); 1131 } 1132 if (notify) notifyOnUpdated(); 1133 } 1134 1135 /** 1136 * Updates this WifiEntry as the default network if it matches. 1137 */ 1138 @WorkerThread onDefaultNetworkCapabilitiesChanged( @onNull Network network, @NonNull NetworkCapabilities capabilities)1139 synchronized void onDefaultNetworkCapabilitiesChanged( 1140 @NonNull Network network, 1141 @NonNull NetworkCapabilities capabilities) { 1142 mDefaultNetwork = network; 1143 mDefaultNetworkCapabilities = capabilities; 1144 notifyOnUpdated(); 1145 } 1146 1147 /** 1148 * Notifies this WifiEntry that the default network was lost. 1149 */ onDefaultNetworkLost()1150 synchronized void onDefaultNetworkLost() { 1151 mDefaultNetwork = null; 1152 mDefaultNetworkCapabilities = null; 1153 notifyOnUpdated(); 1154 } 1155 1156 // Called to indicate the security types should be updated to match new information about the 1157 // network. updateSecurityTypes()1158 protected void updateSecurityTypes() { 1159 // Do nothing; 1160 } 1161 1162 // Updates this WifiEntry's link properties if the network matches. 1163 @WorkerThread updateLinkProperties( @onNull Network network, @NonNull LinkProperties linkProperties)1164 synchronized void updateLinkProperties( 1165 @NonNull Network network, @NonNull LinkProperties linkProperties) { 1166 if (!network.equals(mNetwork)) { 1167 return; 1168 } 1169 1170 if (mConnectedInfo == null) { 1171 mConnectedInfo = new ConnectedInfo(); 1172 } 1173 // Find IPv4 and IPv6 addresses, and subnet mask 1174 List<String> ipv6Addresses = new ArrayList<>(); 1175 for (LinkAddress addr : linkProperties.getLinkAddresses()) { 1176 if (addr.getAddress() instanceof Inet4Address) { 1177 mConnectedInfo.ipAddress = addr.getAddress().getHostAddress(); 1178 try { 1179 InetAddress all = InetAddress.getByAddress( 1180 new byte[]{(byte) 255, (byte) 255, (byte) 255, (byte) 255}); 1181 mConnectedInfo.subnetMask = getNetworkPart( 1182 all, addr.getPrefixLength()).getHostAddress(); 1183 } catch (UnknownHostException | IllegalArgumentException e) { 1184 // Leave subnet null; 1185 } 1186 } else if (addr.getAddress() instanceof Inet6Address) { 1187 ipv6Addresses.add(addr.getAddress().getHostAddress()); 1188 } 1189 } 1190 mConnectedInfo.ipv6Addresses = ipv6Addresses; 1191 1192 // Find IPv4 default gateway. 1193 for (RouteInfo routeInfo : linkProperties.getRoutes()) { 1194 if (routeInfo.isDefaultRoute() && routeInfo.getDestination().getAddress() 1195 instanceof Inet4Address && routeInfo.hasGateway()) { 1196 mConnectedInfo.gateway = routeInfo.getGateway().getHostAddress(); 1197 break; 1198 } 1199 } 1200 1201 // Find DNS servers 1202 mConnectedInfo.dnsServers = linkProperties.getDnsServers().stream() 1203 .map(InetAddress::getHostAddress).collect(Collectors.toList()); 1204 1205 notifyOnUpdated(); 1206 } 1207 1208 // Method for WifiTracker to update a connected WifiEntry's validation status. 1209 @WorkerThread updateConnectivityReport( @onNull ConnectivityDiagnosticsManager.ConnectivityReport connectivityReport)1210 synchronized void updateConnectivityReport( 1211 @NonNull ConnectivityDiagnosticsManager.ConnectivityReport connectivityReport) { 1212 if (connectivityReport.getNetwork().equals(mNetwork)) { 1213 mConnectivityReport = connectivityReport; 1214 notifyOnUpdated(); 1215 } 1216 } 1217 getWifiInfoDescription()1218 synchronized String getWifiInfoDescription() { 1219 final StringJoiner sj = new StringJoiner(" "); 1220 if (getConnectedState() == CONNECTED_STATE_CONNECTED && mWifiInfo != null) { 1221 sj.add("f = " + mWifiInfo.getFrequency()); 1222 final String bssid = mWifiInfo.getBSSID(); 1223 if (bssid != null) { 1224 sj.add(bssid); 1225 } 1226 sj.add("standard = " + getStandardString()); 1227 sj.add("rssi = " + mWifiInfo.getRssi()); 1228 sj.add("score = " + mWifiInfo.getScore()); 1229 sj.add(String.format(" tx=%.1f,", mWifiInfo.getSuccessfulTxPacketsPerSecond())); 1230 sj.add(String.format("%.1f,", mWifiInfo.getRetriedTxPacketsPerSecond())); 1231 sj.add(String.format("%.1f ", mWifiInfo.getLostTxPacketsPerSecond())); 1232 sj.add(String.format("rx=%.1f", mWifiInfo.getSuccessfulRxPacketsPerSecond())); 1233 if (BuildCompat.isAtLeastT() && mWifiInfo.getApMldMacAddress() != null) { 1234 sj.add("mldMac = " + mWifiInfo.getApMldMacAddress()); 1235 sj.add("linkId = " + mWifiInfo.getApMloLinkId()); 1236 sj.add("affLinks = " + Arrays.toString( 1237 mWifiInfo.getAffiliatedMloLinks().toArray())); 1238 } 1239 } 1240 return sj.toString(); 1241 } 1242 1243 protected class ConnectActionListener implements WifiManager.ActionListener { 1244 @Override onSuccess()1245 public void onSuccess() { 1246 synchronized (WifiEntry.this) { 1247 // Wait for L3 connection before returning the success result. 1248 mCalledConnect = true; 1249 } 1250 } 1251 1252 @Override onFailure(int i)1253 public void onFailure(int i) { 1254 mCallbackHandler.post(() -> { 1255 final ConnectCallback connectCallback = mConnectCallback; 1256 if (connectCallback != null) { 1257 connectCallback.onConnectResult(ConnectCallback.CONNECT_STATUS_FAILURE_UNKNOWN); 1258 } 1259 }); 1260 } 1261 } 1262 1263 protected class ForgetActionListener implements WifiManager.ActionListener { 1264 @Override onSuccess()1265 public void onSuccess() { 1266 mCallbackHandler.post(() -> { 1267 final ForgetCallback forgetCallback = mForgetCallback; 1268 if (forgetCallback != null) { 1269 forgetCallback.onForgetResult(ForgetCallback.FORGET_STATUS_SUCCESS); 1270 } 1271 }); 1272 } 1273 1274 @Override onFailure(int i)1275 public void onFailure(int i) { 1276 mCallbackHandler.post(() -> { 1277 final ForgetCallback forgetCallback = mForgetCallback; 1278 if (forgetCallback != null) { 1279 forgetCallback.onForgetResult(ForgetCallback.FORGET_STATUS_FAILURE_UNKNOWN); 1280 } 1281 }); 1282 } 1283 } 1284 1285 @Override equals(Object other)1286 public boolean equals(Object other) { 1287 if (!(other instanceof WifiEntry)) return false; 1288 return getKey().equals(((WifiEntry) other).getKey()); 1289 } 1290 1291 @Override hashCode()1292 public int hashCode() { 1293 return getKey().hashCode(); 1294 } 1295 1296 @Override toString()1297 public String toString() { 1298 StringJoiner sj = new StringJoiner("][", "[", "]"); 1299 sj.add(this.getClass().getSimpleName()); 1300 sj.add(getTitle()); 1301 sj.add(getSummary()); 1302 sj.add("Level:" + getLevel() + (shouldShowXLevelIcon() ? "!" : "")); 1303 String security = getSecurityString(true); 1304 if (!TextUtils.isEmpty(security)) { 1305 sj.add(security); 1306 } 1307 int connectedState = getConnectedState(); 1308 if (connectedState == CONNECTED_STATE_CONNECTED) { 1309 sj.add("Connected"); 1310 } else if (connectedState == CONNECTED_STATE_CONNECTING) { 1311 sj.add("Connecting..."); 1312 } 1313 if (hasInternetAccess()) { 1314 sj.add("Internet"); 1315 } 1316 if (isDefaultNetwork()) { 1317 sj.add("Default"); 1318 } 1319 if (isPrimaryNetwork()) { 1320 sj.add("Primary"); 1321 } 1322 if (isLowQuality()) { 1323 sj.add("LowQuality"); 1324 } 1325 if (isSaved()) { 1326 sj.add("Saved"); 1327 } 1328 if (isSubscription()) { 1329 sj.add("Subscription"); 1330 } 1331 if (isSuggestion()) { 1332 sj.add("Suggestion"); 1333 } 1334 if (isMetered()) { 1335 sj.add("Metered"); 1336 } 1337 if ((isSaved() || isSuggestion() || isSubscription()) && !isAutoJoinEnabled()) { 1338 sj.add("AutoJoinDisabled"); 1339 } 1340 if (isExpired()) { 1341 sj.add("Expired"); 1342 } 1343 if (canSignIn()) { 1344 sj.add("SignIn"); 1345 } 1346 if (shouldEditBeforeConnect()) { 1347 sj.add("EditBeforeConnect"); 1348 } 1349 if (hasAdminRestrictions()) { 1350 sj.add("AdminRestricted"); 1351 } 1352 return sj.toString(); 1353 } 1354 1355 /** 1356 * The action used to execute the calling of WifiEntry#manageSubscription. 1357 */ 1358 public interface ManageSubscriptionAction { 1359 /** 1360 * Execute the action of managing subscription. 1361 */ onExecute()1362 void onExecute(); 1363 } 1364 1365 /** 1366 * Whether this WifiEntry is using a verbose summary. 1367 */ isVerboseSummaryEnabled()1368 public boolean isVerboseSummaryEnabled() { 1369 return mInjector.isVerboseSummaryEnabled(); 1370 } 1371 } 1372