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