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