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 480 /** 481 * Creates an empty ConnectedInfo 482 */ ConnectedInfo()483 public ConnectedInfo() { 484 } 485 486 /** 487 * Creates a ConnectedInfo with all fields copied from an input ConnectedInfo 488 */ ConnectedInfo(@onNull ConnectedInfo other)489 public ConnectedInfo(@NonNull ConnectedInfo other) { 490 frequencyMhz = other.frequencyMhz; 491 dnsServers = new ArrayList<>(dnsServers); 492 linkSpeedMbps = other.linkSpeedMbps; 493 ipAddress = other.ipAddress; 494 ipv6Addresses = new ArrayList<>(other.ipv6Addresses); 495 gateway = other.gateway; 496 subnetMask = other.subnetMask; 497 wifiStandard = other.wifiStandard; 498 } 499 } 500 501 // User actions on a network 502 503 /** Returns whether the entry should show a connect option */ canConnect()504 public boolean canConnect() { 505 return false; 506 } 507 508 /** Connects to the network */ connect(@ullable ConnectCallback callback)509 public void connect(@Nullable ConnectCallback callback) { 510 // Do nothing. 511 } 512 513 /** Returns whether the entry should show a disconnect option */ canDisconnect()514 public boolean canDisconnect() { 515 return false; 516 } 517 518 /** Disconnects from the network */ disconnect(@ullable DisconnectCallback callback)519 public void disconnect(@Nullable DisconnectCallback callback) { 520 // Do nothing. 521 } 522 523 /** Returns whether the entry should show a forget option */ canForget()524 public boolean canForget() { 525 return false; 526 } 527 528 /** Forgets the network */ forget(@ullable ForgetCallback callback)529 public void forget(@Nullable ForgetCallback callback) { 530 // Do nothing. 531 } 532 533 /** Returns whether the network can be signed-in to */ canSignIn()534 public boolean canSignIn() { 535 return false; 536 } 537 538 /** Sign-in to the network. For captive portals. */ signIn(@ullable SignInCallback callback)539 public void signIn(@Nullable SignInCallback callback) { 540 // Do nothing. 541 } 542 543 /** Returns whether the network can be shared via QR code */ canShare()544 public boolean canShare() { 545 return false; 546 } 547 548 /** Returns whether the user can use Easy Connect to onboard a device to the network */ canEasyConnect()549 public boolean canEasyConnect() { 550 return false; 551 } 552 553 // Modifiable settings 554 555 /** 556 * Returns the user's choice whether to treat a network as metered, 557 * defined by the METERED_CHOICE constants 558 */ 559 @MeteredChoice getMeteredChoice()560 public int getMeteredChoice() { 561 return METERED_CHOICE_AUTO; 562 } 563 564 /** Returns whether the entry should let the user choose the metered treatment of a network */ canSetMeteredChoice()565 public boolean canSetMeteredChoice() { 566 return false; 567 } 568 569 /** 570 * Sets the user's choice for treating a network as metered, 571 * defined by the METERED_CHOICE constants 572 */ setMeteredChoice(@eteredChoice int meteredChoice)573 public void setMeteredChoice(@MeteredChoice int meteredChoice) { 574 // Do nothing. 575 } 576 577 /** Returns whether the entry should let the user choose the MAC randomization setting */ canSetPrivacy()578 public boolean canSetPrivacy() { 579 return false; 580 } 581 582 /** Returns the MAC randomization setting defined by the PRIVACY constants */ 583 @Privacy getPrivacy()584 public int getPrivacy() { 585 return PRIVACY_UNKNOWN; 586 } 587 588 /** Sets the user's choice for MAC randomization defined by the PRIVACY constants */ setPrivacy(@rivacy int privacy)589 public void setPrivacy(@Privacy int privacy) { 590 // Do nothing. 591 } 592 593 /** Returns whether the network has auto-join enabled */ isAutoJoinEnabled()594 public boolean isAutoJoinEnabled() { 595 return false; 596 } 597 598 /** Returns whether the user can enable/disable auto-join */ canSetAutoJoinEnabled()599 public boolean canSetAutoJoinEnabled() { 600 return false; 601 } 602 603 /** Sets whether a network will be auto-joined or not */ setAutoJoinEnabled(boolean enabled)604 public void setAutoJoinEnabled(boolean enabled) { 605 // Do nothing. 606 } 607 608 /** Returns the string displayed for @Security */ getSecurityString(boolean concise)609 public String getSecurityString(boolean concise) { 610 return ""; 611 } 612 613 /** Returns whether subscription of the entry is expired */ isExpired()614 public boolean isExpired() { 615 return false; 616 } 617 618 619 /** Returns whether a user can manage their subscription through this WifiEntry */ canManageSubscription()620 public boolean canManageSubscription() { 621 return mManageSubscriptionAction.isPresent(); 622 }; 623 624 /** 625 * Return the URI string value of help, if it is not null, WifiPicker may show 626 * help icon and route the user to help page specified by the URI string. 627 * see {@link Intent#parseUri} 628 */ 629 @Nullable getHelpUriString()630 public String getHelpUriString() { 631 return null; 632 } 633 634 /** Allows the user to manage their subscription via an external flow */ manageSubscription()635 public void manageSubscription() { 636 mManageSubscriptionAction.ifPresent(ManageSubscriptionAction::onExecute); 637 }; 638 639 /** Set the action to be called on calling WifiEntry#manageSubscription. */ setManageSubscriptionAction( @onNull ManageSubscriptionAction manageSubscriptionAction)640 public void setManageSubscriptionAction( 641 @NonNull ManageSubscriptionAction manageSubscriptionAction) { 642 // only notify update on 1st time 643 boolean notify = !mManageSubscriptionAction.isPresent(); 644 645 mManageSubscriptionAction = Optional.of(manageSubscriptionAction); 646 if (notify) { 647 notifyOnUpdated(); 648 } 649 } 650 651 /** Returns the ScanResult information of a WifiEntry */ 652 @NonNull getScanResultDescription()653 protected String getScanResultDescription() { 654 return ""; 655 } 656 657 /** Returns the network selection information of a WifiEntry */ 658 @NonNull getNetworkSelectionDescription()659 String getNetworkSelectionDescription() { 660 return ""; 661 } 662 663 /** Returns the network capability information of a WifiEntry */ 664 @NonNull getNetworkCapabilityDescription()665 String getNetworkCapabilityDescription() { 666 final StringBuilder sb = new StringBuilder(); 667 if (getConnectedState() == CONNECTED_STATE_CONNECTED) { 668 sb.append("isValidated:") 669 .append(mIsValidated) 670 .append(", isDefaultNetwork:") 671 .append(mIsDefaultNetwork) 672 .append(", isLowQuality:") 673 .append(mIsLowQuality); 674 } 675 return sb.toString(); 676 } 677 678 /** 679 * In Wi-Fi picker, when users click a saved network, it will connect to the Wi-Fi network. 680 * However, for some special cases, Wi-Fi picker should show Wi-Fi editor UI for users to edit 681 * security or password before connecting. Or users will always get connection fail results. 682 */ shouldEditBeforeConnect()683 public boolean shouldEditBeforeConnect() { 684 return false; 685 } 686 687 /** 688 * Sets the callback listener for WifiEntryCallback methods. 689 * Subsequent calls will overwrite the previous listener. 690 */ setListener(WifiEntryCallback listener)691 public synchronized void setListener(WifiEntryCallback listener) { 692 mListener = listener; 693 } 694 695 /** 696 * Listener for changes to the state of the WifiEntry. 697 * This callback will be invoked on the main thread. 698 */ 699 public interface WifiEntryCallback { 700 /** 701 * Indicates the state of the WifiEntry has changed and clients may retrieve updates through 702 * the WifiEntry getter methods. 703 */ 704 @MainThread onUpdated()705 void onUpdated(); 706 } 707 708 @AnyThread notifyOnUpdated()709 protected void notifyOnUpdated() { 710 if (mListener != null) { 711 mCallbackHandler.post(() -> { 712 final WifiEntryCallback listener = mListener; 713 if (listener != null) { 714 listener.onUpdated(); 715 } 716 }); 717 } 718 } 719 720 /** 721 * Listener for changes to the state of the WifiEntry. 722 * This callback will be invoked on the main thread. 723 */ 724 public interface ConnectCallback { 725 @Retention(RetentionPolicy.SOURCE) 726 @IntDef(value = { 727 CONNECT_STATUS_SUCCESS, 728 CONNECT_STATUS_FAILURE_NO_CONFIG, 729 CONNECT_STATUS_FAILURE_UNKNOWN, 730 CONNECT_STATUS_FAILURE_SIM_ABSENT 731 }) 732 733 public @interface ConnectStatus {} 734 735 int CONNECT_STATUS_SUCCESS = 0; 736 int CONNECT_STATUS_FAILURE_NO_CONFIG = 1; 737 int CONNECT_STATUS_FAILURE_UNKNOWN = 2; 738 int CONNECT_STATUS_FAILURE_SIM_ABSENT = 3; 739 740 /** 741 * Result of the connect request indicated by the CONNECT_STATUS constants. 742 */ 743 @MainThread onConnectResult(@onnectStatus int status)744 void onConnectResult(@ConnectStatus int status); 745 } 746 747 /** 748 * Listener for changes to the state of the WifiEntry. 749 * This callback will be invoked on the main thread. 750 */ 751 public interface DisconnectCallback { 752 @Retention(RetentionPolicy.SOURCE) 753 @IntDef(value = { 754 DISCONNECT_STATUS_SUCCESS, 755 DISCONNECT_STATUS_FAILURE_UNKNOWN 756 }) 757 758 public @interface DisconnectStatus {} 759 760 int DISCONNECT_STATUS_SUCCESS = 0; 761 int DISCONNECT_STATUS_FAILURE_UNKNOWN = 1; 762 /** 763 * Result of the disconnect request indicated by the DISCONNECT_STATUS constants. 764 */ 765 @MainThread onDisconnectResult(@isconnectStatus int status)766 void onDisconnectResult(@DisconnectStatus int status); 767 } 768 769 /** 770 * Listener for changes to the state of the WifiEntry. 771 * This callback will be invoked on the main thread. 772 */ 773 public interface ForgetCallback { 774 @Retention(RetentionPolicy.SOURCE) 775 @IntDef(value = { 776 FORGET_STATUS_SUCCESS, 777 FORGET_STATUS_FAILURE_UNKNOWN 778 }) 779 780 public @interface ForgetStatus {} 781 782 int FORGET_STATUS_SUCCESS = 0; 783 int FORGET_STATUS_FAILURE_UNKNOWN = 1; 784 785 /** 786 * Result of the forget request indicated by the FORGET_STATUS constants. 787 */ 788 @MainThread onForgetResult(@orgetStatus int status)789 void onForgetResult(@ForgetStatus int status); 790 } 791 792 /** 793 * Listener for changes to the state of the WifiEntry. 794 * This callback will be invoked on the main thread. 795 */ 796 public interface SignInCallback { 797 @Retention(RetentionPolicy.SOURCE) 798 @IntDef(value = { 799 SIGNIN_STATUS_SUCCESS, 800 SIGNIN_STATUS_FAILURE_UNKNOWN 801 }) 802 803 public @interface SignInStatus {} 804 805 int SIGNIN_STATUS_SUCCESS = 0; 806 int SIGNIN_STATUS_FAILURE_UNKNOWN = 1; 807 808 /** 809 * Result of the sign-in request indicated by the SIGNIN_STATUS constants. 810 */ 811 @MainThread onSignInResult(@ignInStatus int status)812 void onSignInResult(@SignInStatus int status); 813 } 814 815 /** 816 * Returns whether or not the supplied WifiInfo and NetworkInfo represent this WifiEntry 817 */ connectionInfoMatches(@onNull WifiInfo wifiInfo, @NonNull NetworkInfo networkInfo)818 protected boolean connectionInfoMatches(@NonNull WifiInfo wifiInfo, 819 @NonNull NetworkInfo networkInfo) { 820 return false; 821 } 822 823 /** 824 * Updates information regarding the current network connection. If the supplied WifiInfo and 825 * NetworkInfo do not match this WifiEntry, then the WifiEntry will update to be 826 * unconnected. 827 */ 828 @WorkerThread updateConnectionInfo( @ullable WifiInfo wifiInfo, @Nullable NetworkInfo networkInfo)829 synchronized void updateConnectionInfo( 830 @Nullable WifiInfo wifiInfo, @Nullable NetworkInfo networkInfo) { 831 if (wifiInfo != null && networkInfo != null 832 && connectionInfoMatches(wifiInfo, networkInfo)) { 833 // Connection info matches, so the WifiInfo/NetworkInfo represent this network and 834 // the network is currently connecting or connected. 835 mWifiInfo = wifiInfo; 836 mNetworkInfo = networkInfo; 837 final int wifiInfoRssi = wifiInfo.getRssi(); 838 if (wifiInfoRssi != INVALID_RSSI) { 839 mLevel = mWifiManager.calculateSignalLevel(wifiInfoRssi); 840 mSpeed = getSpeedFromWifiInfo(mScoreCache, wifiInfo); 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 mIsValidated = false; 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 = NetUtils.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 mIsValidated = mNetworkCapabilities != null 956 && mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); 957 notifyOnUpdated(); 958 } 959 getWifiInfoDescription()960 synchronized String getWifiInfoDescription() { 961 final StringJoiner sj = new StringJoiner(" "); 962 if (getConnectedState() == CONNECTED_STATE_CONNECTED && mWifiInfo != null) { 963 sj.add("f = " + mWifiInfo.getFrequency()); 964 final String bssid = mWifiInfo.getBSSID(); 965 if (bssid != null) { 966 sj.add(bssid); 967 } 968 sj.add("standard = " + mWifiInfo.getWifiStandard()); 969 sj.add("rssi = " + mWifiInfo.getRssi()); 970 sj.add("score = " + mWifiInfo.getScore()); 971 sj.add(String.format(" tx=%.1f,", mWifiInfo.getSuccessfulTxPacketsPerSecond())); 972 sj.add(String.format("%.1f,", mWifiInfo.getRetriedTxPacketsPerSecond())); 973 sj.add(String.format("%.1f ", mWifiInfo.getLostTxPacketsPerSecond())); 974 sj.add(String.format("rx=%.1f", mWifiInfo.getSuccessfulRxPacketsPerSecond())); 975 } 976 return sj.toString(); 977 } 978 979 protected class ConnectActionListener implements WifiManager.ActionListener { 980 @Override onSuccess()981 public void onSuccess() { 982 synchronized (WifiEntry.this) { 983 mCalledConnect = true; 984 } 985 // If we aren't connected to the network after 10 seconds, trigger the failure callback 986 mCallbackHandler.postDelayed(() -> { 987 final ConnectCallback connectCallback = mConnectCallback; 988 if (connectCallback != null && mCalledConnect 989 && getConnectedState() == CONNECTED_STATE_DISCONNECTED) { 990 connectCallback.onConnectResult( 991 ConnectCallback.CONNECT_STATUS_FAILURE_UNKNOWN); 992 mCalledConnect = false; 993 } 994 }, 10_000 /* delayMillis */); 995 } 996 997 @Override onFailure(int i)998 public void onFailure(int i) { 999 mCallbackHandler.post(() -> { 1000 final ConnectCallback connectCallback = mConnectCallback; 1001 if (connectCallback != null) { 1002 connectCallback.onConnectResult( 1003 ConnectCallback.CONNECT_STATUS_FAILURE_UNKNOWN); 1004 } 1005 }); 1006 } 1007 } 1008 1009 protected class ForgetActionListener implements WifiManager.ActionListener { 1010 @Override onSuccess()1011 public void onSuccess() { 1012 mCallbackHandler.post(() -> { 1013 final ForgetCallback forgetCallback = mForgetCallback; 1014 if (forgetCallback != null) { 1015 forgetCallback.onForgetResult(ForgetCallback.FORGET_STATUS_SUCCESS); 1016 } 1017 }); 1018 } 1019 1020 @Override onFailure(int i)1021 public void onFailure(int i) { 1022 mCallbackHandler.post(() -> { 1023 final ForgetCallback forgetCallback = mForgetCallback; 1024 if (forgetCallback != null) { 1025 forgetCallback.onForgetResult(ForgetCallback.FORGET_STATUS_FAILURE_UNKNOWN); 1026 } 1027 }); 1028 } 1029 } 1030 1031 @Override compareTo(@onNull WifiEntry other)1032 public int compareTo(@NonNull WifiEntry other) { 1033 if (getLevel() != WIFI_LEVEL_UNREACHABLE && other.getLevel() == WIFI_LEVEL_UNREACHABLE) { 1034 return -1; 1035 } 1036 if (getLevel() == WIFI_LEVEL_UNREACHABLE && other.getLevel() != WIFI_LEVEL_UNREACHABLE) { 1037 return 1; 1038 } 1039 1040 if (isSubscription() && !other.isSubscription()) return -1; 1041 if (!isSubscription() && other.isSubscription()) return 1; 1042 1043 if (isSaved() && !other.isSaved()) return -1; 1044 if (!isSaved() && other.isSaved()) return 1; 1045 1046 if (isSuggestion() && !other.isSuggestion()) return -1; 1047 if (!isSuggestion() && other.isSuggestion()) return 1; 1048 1049 if (getLevel() > other.getLevel()) return -1; 1050 if (getLevel() < other.getLevel()) return 1; 1051 1052 return getTitle().compareTo(other.getTitle()); 1053 } 1054 1055 @Override equals(Object other)1056 public boolean equals(Object other) { 1057 if (!(other instanceof WifiEntry)) return false; 1058 return getKey().equals(((WifiEntry) other).getKey()); 1059 } 1060 1061 @Override toString()1062 public String toString() { 1063 return new StringBuilder() 1064 .append(getKey()) 1065 .append(",title:") 1066 .append(getTitle()) 1067 .append(",summary:") 1068 .append(getSummary()) 1069 .append(",isSaved:") 1070 .append(isSaved()) 1071 .append(",isSubscription:") 1072 .append(isSubscription()) 1073 .append(",isSuggestion:") 1074 .append(isSuggestion()) 1075 .append(",level:") 1076 .append(getLevel()) 1077 .append(shouldShowXLevelIcon() ? "X" : "") 1078 .append(",security:") 1079 .append(getSecurityTypes()) 1080 .append(",connected:") 1081 .append(getConnectedState() == CONNECTED_STATE_CONNECTED ? "true" : "false") 1082 .append(",connectedInfo:") 1083 .append(getConnectedInfo()) 1084 .append(",isValidated:") 1085 .append(mIsValidated) 1086 .append(",isDefaultNetwork:") 1087 .append(mIsDefaultNetwork) 1088 .toString(); 1089 } 1090 1091 /** 1092 * The action used to execute the calling of WifiEntry#manageSubscription. 1093 */ 1094 public interface ManageSubscriptionAction { 1095 /** 1096 * Execute the action of managing subscription. 1097 */ onExecute()1098 void onExecute(); 1099 } 1100 } 1101