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