• 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.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