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