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