1 /*
2  * Copyright (C) 2014 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.server.connectivity;
18 
19 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
20 
21 import android.content.Context;
22 import android.net.LinkProperties;
23 import android.net.Network;
24 import android.net.NetworkCapabilities;
25 import android.net.NetworkInfo;
26 import android.net.NetworkMisc;
27 import android.net.NetworkRequest;
28 import android.net.NetworkState;
29 import android.os.Handler;
30 import android.os.Messenger;
31 import android.os.SystemClock;
32 import android.util.Log;
33 import android.util.SparseArray;
34 
35 import com.android.internal.util.AsyncChannel;
36 import com.android.internal.util.WakeupMessage;
37 import com.android.server.ConnectivityService;
38 import com.android.server.connectivity.NetworkMonitor;
39 
40 import java.io.PrintWriter;
41 import java.util.ArrayList;
42 import java.util.Comparator;
43 import java.util.Objects;
44 import java.util.SortedSet;
45 import java.util.TreeSet;
46 
47 /**
48  * A bag class used by ConnectivityService for holding a collection of most recent
49  * information published by a particular NetworkAgent as well as the
50  * AsyncChannel/messenger for reaching that NetworkAgent and lists of NetworkRequests
51  * interested in using it.  Default sort order is descending by score.
52  */
53 // States of a network:
54 // --------------------
55 // 1. registered, uncreated, disconnected, unvalidated
56 //    This state is entered when a NetworkFactory registers a NetworkAgent in any state except
57 //    the CONNECTED state.
58 // 2. registered, uncreated, connecting, unvalidated
59 //    This state is entered when a registered NetworkAgent for a VPN network transitions to the
60 //    CONNECTING state (TODO: go through this state for every network, not just VPNs).
61 //    ConnectivityService will tell netd to create the network early in order to add extra UID
62 //    routing rules referencing the netID. These rules need to be in place before the network is
63 //    connected to avoid racing against client apps trying to connect to a half-setup network.
64 // 3. registered, uncreated, connected, unvalidated
65 //    This state is entered when a registered NetworkAgent transitions to the CONNECTED state.
66 //    ConnectivityService will tell netd to create the network if it was not already created, and
67 //    immediately transition to state #4.
68 // 4. registered, created, connected, unvalidated
69 //    If this network can satisfy the default NetworkRequest, then NetworkMonitor will
70 //    probe for Internet connectivity.
71 //    If this network cannot satisfy the default NetworkRequest, it will immediately be
72 //    transitioned to state #5.
73 //    A network may remain in this state if NetworkMonitor fails to find Internet connectivity,
74 //    for example:
75 //    a. a captive portal is present, or
76 //    b. a WiFi router whose Internet backhaul is down, or
77 //    c. a wireless connection stops transfering packets temporarily (e.g. device is in elevator
78 //       or tunnel) but does not disconnect from the AP/cell tower, or
79 //    d. a stand-alone device offering a WiFi AP without an uplink for configuration purposes.
80 // 5. registered, created, connected, validated
81 //
82 // The device's default network connection:
83 // ----------------------------------------
84 // Networks in states #4 and #5 may be used as a device's default network connection if they
85 // satisfy the default NetworkRequest.
86 // A network, that satisfies the default NetworkRequest, in state #5 should always be chosen
87 // in favor of a network, that satisfies the default NetworkRequest, in state #4.
88 // When deciding between two networks, that both satisfy the default NetworkRequest, to select
89 // for the default network connection, the one with the higher score should be chosen.
90 //
91 // When a network disconnects:
92 // ---------------------------
93 // If a network's transport disappears, for example:
94 // a. WiFi turned off, or
95 // b. cellular data turned off, or
96 // c. airplane mode is turned on, or
97 // d. a wireless connection disconnects from AP/cell tower entirely (e.g. device is out of range
98 //    of AP for an extended period of time, or switches to another AP without roaming)
99 // then that network can transition from any state (#1-#5) to unregistered.  This happens by
100 // the transport disconnecting their NetworkAgent's AsyncChannel with ConnectivityManager.
101 // ConnectivityService also tells netd to destroy the network.
102 //
103 // When ConnectivityService disconnects a network:
104 // -----------------------------------------------
105 // If a network has no chance of satisfying any requests (even if it were to become validated
106 // and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
107 //
108 // If the network was satisfying a foreground NetworkRequest (i.e. had been the highest scoring that
109 // satisfied the NetworkRequest's constraints), but is no longer the highest scoring network for any
110 // foreground NetworkRequest, then there will be a 30s pause to allow network communication to be
111 // wrapped up rather than abruptly terminated. During this pause the network is said to be
112 // "lingering". During this pause if the network begins satisfying a foreground NetworkRequest,
113 // ConnectivityService will cancel the future disconnection of the NetworkAgent's AsyncChannel, and
114 // the network is no longer considered "lingering". After the linger timer expires, if the network
115 // is satisfying one or more background NetworkRequests it is kept up in the background. If it is
116 // not, ConnectivityService disconnects the NetworkAgent's AsyncChannel.
117 public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
118 
119     public NetworkInfo networkInfo;
120     // This Network object should always be used if possible, so as to encourage reuse of the
121     // enclosed socket factory and connection pool.  Avoid creating other Network objects.
122     // This Network object is always valid.
123     public final Network network;
124     public LinkProperties linkProperties;
125     // This should only be modified via ConnectivityService.updateCapabilities().
126     public NetworkCapabilities networkCapabilities;
127     public final NetworkMonitor networkMonitor;
128     public final NetworkMisc networkMisc;
129     // Indicates if netd has been told to create this Network. From this point on the appropriate
130     // routing rules are setup and routes are added so packets can begin flowing over the Network.
131     // This is a sticky bit; once set it is never cleared.
132     public boolean created;
133     // Set to true after the first time this network is marked as CONNECTED. Once set, the network
134     // shows up in API calls, is able to satisfy NetworkRequests and can become the default network.
135     // This is a sticky bit; once set it is never cleared.
136     public boolean everConnected;
137     // Set to true if this Network successfully passed validation or if it did not satisfy the
138     // default NetworkRequest in which case validation will not be attempted.
139     // This is a sticky bit; once set it is never cleared even if future validation attempts fail.
140     public boolean everValidated;
141 
142     // The result of the last validation attempt on this network (true if validated, false if not).
143     public boolean lastValidated;
144 
145     // If true, becoming unvalidated will lower the network's score. This is only meaningful if the
146     // system is configured not to do this for certain networks, e.g., if the
147     // config_networkAvoidBadWifi option is set to 0 and the user has not overridden that via
148     // Settings.Global.NETWORK_AVOID_BAD_WIFI.
149     public boolean avoidUnvalidated;
150 
151     // Whether a captive portal was ever detected on this network.
152     // This is a sticky bit; once set it is never cleared.
153     public boolean everCaptivePortalDetected;
154 
155     // Whether a captive portal was found during the last network validation attempt.
156     public boolean lastCaptivePortalDetected;
157 
158     // Networks are lingered when they become unneeded as a result of their NetworkRequests being
159     // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
160     // network is taken down.  This usually only happens to the default network. Lingering ends with
161     // either the linger timeout expiring and the network being taken down, or the network
162     // satisfying a request again.
163     public static class LingerTimer implements Comparable<LingerTimer> {
164         public final NetworkRequest request;
165         public final long expiryMs;
166 
LingerTimer(NetworkRequest request, long expiryMs)167         public LingerTimer(NetworkRequest request, long expiryMs) {
168             this.request = request;
169             this.expiryMs = expiryMs;
170         }
equals(Object o)171         public boolean equals(Object o) {
172             if (!(o instanceof LingerTimer)) return false;
173             LingerTimer other = (LingerTimer) o;
174             return (request.requestId == other.request.requestId) && (expiryMs == other.expiryMs);
175         }
hashCode()176         public int hashCode() {
177             return Objects.hash(request.requestId, expiryMs);
178         }
compareTo(LingerTimer other)179         public int compareTo(LingerTimer other) {
180             return (expiryMs != other.expiryMs) ?
181                     Long.compare(expiryMs, other.expiryMs) :
182                     Integer.compare(request.requestId, other.request.requestId);
183         }
toString()184         public String toString() {
185             return String.format("%s, expires %dms", request.toString(),
186                     expiryMs - SystemClock.elapsedRealtime());
187         }
188     }
189 
190     /**
191      * Inform ConnectivityService that the network LINGER period has
192      * expired.
193      * obj = this NetworkAgentInfo
194      */
195     public static final int EVENT_NETWORK_LINGER_COMPLETE = 1001;
196 
197     // All linger timers for this network, sorted by expiry time. A linger timer is added whenever
198     // a request is moved to a network with a better score, regardless of whether the network is or
199     // was lingering or not.
200     // TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
201     // SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
202     private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
203 
204     // For fast lookups. Indexes into mLingerTimers by request ID.
205     private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
206 
207     // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
208     // network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
209     // When the timer fires, all linger state is cleared, and if the network has no requests, it is
210     // torn down.
211     private WakeupMessage mLingerMessage;
212 
213     // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
214     private long mLingerExpiryMs;
215 
216     // Whether the network is lingering or not. Must be maintained separately from the above because
217     // it depends on the state of other networks and requests, which only ConnectivityService knows.
218     // (Example: we don't linger a network if it would become the best for a NetworkRequest if it
219     // validated).
220     private boolean mLingering;
221 
222     // This represents the last score received from the NetworkAgent.
223     private int currentScore;
224     // Penalty applied to scores of Networks that have not been validated.
225     private static final int UNVALIDATED_SCORE_PENALTY = 40;
226 
227     // Score for explicitly connected network.
228     //
229     // This ensures that a) the explicitly selected network is never trumped by anything else, and
230     // b) the explicitly selected network is never torn down.
231     private static final int MAXIMUM_NETWORK_SCORE = 100;
232 
233     // The list of NetworkRequests being satisfied by this Network.
234     private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
235 
236     // How many of the satisfied requests are actual requests and not listens.
237     private int mNumRequestNetworkRequests = 0;
238 
239     // How many of the satisfied requests are of type BACKGROUND_REQUEST.
240     private int mNumBackgroundNetworkRequests = 0;
241 
242     public final Messenger messenger;
243     public final AsyncChannel asyncChannel;
244 
245     // Used by ConnectivityService to keep track of 464xlat.
246     public Nat464Xlat clatd;
247 
248     private static final String TAG = ConnectivityService.class.getSimpleName();
249     private static final boolean VDBG = false;
250     private final ConnectivityService mConnService;
251     private final Context mContext;
252     private final Handler mHandler;
253 
NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService)254     public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
255             LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
256             NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
257         this.messenger = messenger;
258         asyncChannel = ac;
259         network = net;
260         networkInfo = info;
261         linkProperties = lp;
262         networkCapabilities = nc;
263         currentScore = score;
264         mConnService = connService;
265         mContext = context;
266         mHandler = handler;
267         networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
268         networkMisc = misc;
269     }
270 
271     // Functions for manipulating the requests satisfied by this network.
272     //
273     // These functions must only called on ConnectivityService's main thread.
274 
275     private static final boolean ADD = true;
276     private static final boolean REMOVE = false;
277 
updateRequestCounts(boolean add, NetworkRequest request)278     private void updateRequestCounts(boolean add, NetworkRequest request) {
279         int delta = add ? +1 : -1;
280         switch (request.type) {
281             case REQUEST:
282             case TRACK_DEFAULT:
283                 mNumRequestNetworkRequests += delta;
284                 break;
285 
286             case BACKGROUND_REQUEST:
287                 mNumRequestNetworkRequests += delta;
288                 mNumBackgroundNetworkRequests += delta;
289                 break;
290 
291             case LISTEN:
292                 break;
293 
294             case NONE:
295             default:
296                 Log.wtf(TAG, "Unhandled request type " + request.type);
297                 break;
298         }
299     }
300 
301     /**
302      * Add {@code networkRequest} to this network as it's satisfied by this network.
303      * @return true if {@code networkRequest} was added or false if {@code networkRequest} was
304      *         already present.
305      */
addRequest(NetworkRequest networkRequest)306     public boolean addRequest(NetworkRequest networkRequest) {
307         NetworkRequest existing = mNetworkRequests.get(networkRequest.requestId);
308         if (existing == networkRequest) return false;
309         if (existing != null) {
310             // Should only happen if the requestId wraps. If that happens lots of other things will
311             // be broken as well.
312             Log.wtf(TAG, String.format("Duplicate requestId for %s and %s on %s",
313                     networkRequest, existing, name()));
314             updateRequestCounts(REMOVE, existing);
315         }
316         mNetworkRequests.put(networkRequest.requestId, networkRequest);
317         updateRequestCounts(ADD, networkRequest);
318         return true;
319     }
320 
321     /**
322      * Remove the specified request from this network.
323      */
removeRequest(int requestId)324     public void removeRequest(int requestId) {
325         NetworkRequest existing = mNetworkRequests.get(requestId);
326         if (existing == null) return;
327         updateRequestCounts(REMOVE, existing);
328         mNetworkRequests.remove(requestId);
329         if (existing.isRequest()) {
330             unlingerRequest(existing);
331         }
332     }
333 
334     /**
335      * Returns whether this network is currently satisfying the request with the specified ID.
336      */
isSatisfyingRequest(int id)337     public boolean isSatisfyingRequest(int id) {
338         return mNetworkRequests.get(id) != null;
339     }
340 
341     /**
342      * Returns the request at the specified position in the list of requests satisfied by this
343      * network.
344      */
requestAt(int index)345     public NetworkRequest requestAt(int index) {
346         return mNetworkRequests.valueAt(index);
347     }
348 
349     /**
350      * Returns the number of requests currently satisfied by this network for which
351      * {@link android.net.NetworkRequest#isRequest} returns {@code true}.
352      */
numRequestNetworkRequests()353     public int numRequestNetworkRequests() {
354         return mNumRequestNetworkRequests;
355     }
356 
357     /**
358      * Returns the number of requests currently satisfied by this network of type
359      * {@link android.net.NetworkRequest.Type.BACKGROUND_REQUEST}.
360      */
numBackgroundNetworkRequests()361     public int numBackgroundNetworkRequests() {
362         return mNumBackgroundNetworkRequests;
363     }
364 
365     /**
366      * Returns the number of foreground requests currently satisfied by this network.
367      */
numForegroundNetworkRequests()368     public int numForegroundNetworkRequests() {
369         return mNumRequestNetworkRequests - mNumBackgroundNetworkRequests;
370     }
371 
372     /**
373      * Returns the number of requests of any type currently satisfied by this network.
374      */
numNetworkRequests()375     public int numNetworkRequests() {
376         return mNetworkRequests.size();
377     }
378 
379     /**
380      * Returns whether the network is a background network. A network is a background network if it
381      * is satisfying no foreground requests and at least one background request. (If it did not have
382      * a background request, it would be a speculative network that is only being kept up because
383      * it might satisfy a request if it validated).
384      */
isBackgroundNetwork()385     public boolean isBackgroundNetwork() {
386         return !isVPN() && numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0;
387     }
388 
389     // Does this network satisfy request?
satisfies(NetworkRequest request)390     public boolean satisfies(NetworkRequest request) {
391         return created &&
392                 request.networkCapabilities.satisfiedByNetworkCapabilities(networkCapabilities);
393     }
394 
satisfiesImmutableCapabilitiesOf(NetworkRequest request)395     public boolean satisfiesImmutableCapabilitiesOf(NetworkRequest request) {
396         return created &&
397                 request.networkCapabilities.satisfiedByImmutableNetworkCapabilities(
398                         networkCapabilities);
399     }
400 
isVPN()401     public boolean isVPN() {
402         return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
403     }
404 
getCurrentScore(boolean pretendValidated)405     private int getCurrentScore(boolean pretendValidated) {
406         // TODO: We may want to refactor this into a NetworkScore class that takes a base score from
407         // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the
408         // score.  The NetworkScore class would provide a nice place to centralize score constants
409         // so they are not scattered about the transports.
410 
411         // If this network is explicitly selected and the user has decided to use it even if it's
412         // unvalidated, give it the maximum score. Also give it the maximum score if it's explicitly
413         // selected and we're trying to see what its score could be. This ensures that we don't tear
414         // down an explicitly selected network before the user gets a chance to prefer it when
415         // a higher-scoring network (e.g., Ethernet) is available.
416         if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
417             return MAXIMUM_NETWORK_SCORE;
418         }
419 
420         int score = currentScore;
421         if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
422             score -= UNVALIDATED_SCORE_PENALTY;
423         }
424         if (score < 0) score = 0;
425         return score;
426     }
427 
428     // Return true on devices configured to ignore score penalty for wifi networks
429     // that become unvalidated (b/31075769).
ignoreWifiUnvalidationPenalty()430     private boolean ignoreWifiUnvalidationPenalty() {
431         boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
432                 networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
433         boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated;
434         return isWifi && !avoidBadWifi && everValidated;
435     }
436 
437     // Get the current score for this Network.  This may be modified from what the
438     // NetworkAgent sent, as it has modifiers applied to it.
getCurrentScore()439     public int getCurrentScore() {
440         return getCurrentScore(false);
441     }
442 
443     // Get the current score for this Network as if it was validated.  This may be modified from
444     // what the NetworkAgent sent, as it has modifiers applied to it.
getCurrentScoreAsValidated()445     public int getCurrentScoreAsValidated() {
446         return getCurrentScore(true);
447     }
448 
setCurrentScore(int newScore)449     public void setCurrentScore(int newScore) {
450         currentScore = newScore;
451     }
452 
getNetworkState()453     public NetworkState getNetworkState() {
454         synchronized (this) {
455             // Network objects are outwardly immutable so there is no point to duplicating.
456             // Duplicating also precludes sharing socket factories and connection pools.
457             final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null;
458             return new NetworkState(new NetworkInfo(networkInfo),
459                     new LinkProperties(linkProperties),
460                     new NetworkCapabilities(networkCapabilities), network, subscriberId, null);
461         }
462     }
463 
464     /**
465      * Sets the specified request to linger on this network for the specified time. Called by
466      * ConnectivityService when the request is moved to another network with a higher score.
467      */
lingerRequest(NetworkRequest request, long now, long duration)468     public void lingerRequest(NetworkRequest request, long now, long duration) {
469         if (mLingerTimerForRequest.get(request.requestId) != null) {
470             // Cannot happen. Once a request is lingering on a particular network, we cannot
471             // re-linger it unless that network becomes the best for that request again, in which
472             // case we should have unlingered it.
473             Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered");
474         }
475         final long expiryMs = now + duration;
476         LingerTimer timer = new LingerTimer(request, expiryMs);
477         if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name());
478         mLingerTimers.add(timer);
479         mLingerTimerForRequest.put(request.requestId, timer);
480     }
481 
482     /**
483      * Cancel lingering. Called by ConnectivityService when a request is added to this network.
484      * Returns true if the given request was lingering on this network, false otherwise.
485      */
unlingerRequest(NetworkRequest request)486     public boolean unlingerRequest(NetworkRequest request) {
487         LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
488         if (timer != null) {
489             if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
490             mLingerTimers.remove(timer);
491             mLingerTimerForRequest.remove(request.requestId);
492             return true;
493         }
494         return false;
495     }
496 
getLingerExpiry()497     public long getLingerExpiry() {
498         return mLingerExpiryMs;
499     }
500 
updateLingerTimer()501     public void updateLingerTimer() {
502         long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
503         if (newExpiry == mLingerExpiryMs) return;
504 
505         // Even if we're going to reschedule the timer, cancel it first. This is because the
506         // semantics of WakeupMessage guarantee that if cancel is called then the alarm will
507         // never call its callback (handleLingerComplete), even if it has already fired.
508         // WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
509         // has already been dispatched, rescheduling to some time in the future it won't stop it
510         // from calling its callback immediately.
511         if (mLingerMessage != null) {
512             mLingerMessage.cancel();
513             mLingerMessage = null;
514         }
515 
516         if (newExpiry > 0) {
517             mLingerMessage = mConnService.makeWakeupMessage(
518                     mContext, mHandler,
519                     "NETWORK_LINGER_COMPLETE." + network.netId,
520                     EVENT_NETWORK_LINGER_COMPLETE, this);
521             mLingerMessage.schedule(newExpiry);
522         }
523 
524         mLingerExpiryMs = newExpiry;
525     }
526 
linger()527     public void linger() {
528         mLingering = true;
529     }
530 
unlinger()531     public void unlinger() {
532         mLingering = false;
533     }
534 
isLingering()535     public boolean isLingering() {
536         return mLingering;
537     }
538 
clearLingerState()539     public void clearLingerState() {
540         if (mLingerMessage != null) {
541             mLingerMessage.cancel();
542             mLingerMessage = null;
543         }
544         mLingerTimers.clear();
545         mLingerTimerForRequest.clear();
546         updateLingerTimer();  // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
547         mLingering = false;
548     }
549 
dumpLingerTimers(PrintWriter pw)550     public void dumpLingerTimers(PrintWriter pw) {
551         for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
552     }
553 
toString()554     public String toString() {
555         return "NetworkAgentInfo{ ni{" + networkInfo + "}  " +
556                 "network{" + network + "}  nethandle{" + network.getNetworkHandle() + "}  " +
557                 "lp{" + linkProperties + "}  " +
558                 "nc{" + networkCapabilities + "}  Score{" + getCurrentScore() + "}  " +
559                 "everValidated{" + everValidated + "}  lastValidated{" + lastValidated + "}  " +
560                 "created{" + created + "} lingering{" + isLingering() + "} " +
561                 "explicitlySelected{" + networkMisc.explicitlySelected + "} " +
562                 "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
563                 "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " +
564                 "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " +
565                 "}";
566     }
567 
name()568     public String name() {
569         return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
570                 networkInfo.getSubtypeName() + ") - " +
571                 (network == null ? "null" : network.toString()) + "]";
572     }
573 
574     // Enables sorting in descending order of score.
575     @Override
compareTo(NetworkAgentInfo other)576     public int compareTo(NetworkAgentInfo other) {
577         return other.getCurrentScore() - getCurrentScore();
578     }
579 }
580