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