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 android.content.Context; 20 import android.net.IDnsResolver; 21 import android.net.INetd; 22 import android.net.INetworkMonitor; 23 import android.net.LinkProperties; 24 import android.net.Network; 25 import android.net.NetworkCapabilities; 26 import android.net.NetworkInfo; 27 import android.net.NetworkMisc; 28 import android.net.NetworkMonitorManager; 29 import android.net.NetworkRequest; 30 import android.net.NetworkState; 31 import android.os.Handler; 32 import android.os.INetworkManagementService; 33 import android.os.Messenger; 34 import android.os.SystemClock; 35 import android.util.Log; 36 import android.util.SparseArray; 37 38 import com.android.internal.util.AsyncChannel; 39 import com.android.internal.util.WakeupMessage; 40 import com.android.server.ConnectivityService; 41 42 import java.io.PrintWriter; 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 by ConnectivityService, via setNetworkCapabilities(). 126 // TODO: make this private with a getter. 127 public NetworkCapabilities networkCapabilities; 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 // Indicates the captive portal app was opened to show a login UI to the user, but the network 159 // has not validated yet. 160 public volatile boolean captivePortalValidationPending; 161 162 // Set to true when partial connectivity was detected. 163 public boolean partialConnectivity; 164 165 // Networks are lingered when they become unneeded as a result of their NetworkRequests being 166 // satisfied by a higher-scoring network. so as to allow communication to wrap up before the 167 // network is taken down. This usually only happens to the default network. Lingering ends with 168 // either the linger timeout expiring and the network being taken down, or the network 169 // satisfying a request again. 170 public static class LingerTimer implements Comparable<LingerTimer> { 171 public final NetworkRequest request; 172 public final long expiryMs; 173 LingerTimer(NetworkRequest request, long expiryMs)174 public LingerTimer(NetworkRequest request, long expiryMs) { 175 this.request = request; 176 this.expiryMs = expiryMs; 177 } equals(Object o)178 public boolean equals(Object o) { 179 if (!(o instanceof LingerTimer)) return false; 180 LingerTimer other = (LingerTimer) o; 181 return (request.requestId == other.request.requestId) && (expiryMs == other.expiryMs); 182 } hashCode()183 public int hashCode() { 184 return Objects.hash(request.requestId, expiryMs); 185 } compareTo(LingerTimer other)186 public int compareTo(LingerTimer other) { 187 return (expiryMs != other.expiryMs) ? 188 Long.compare(expiryMs, other.expiryMs) : 189 Integer.compare(request.requestId, other.request.requestId); 190 } toString()191 public String toString() { 192 return String.format("%s, expires %dms", request.toString(), 193 expiryMs - SystemClock.elapsedRealtime()); 194 } 195 } 196 197 /** 198 * Inform ConnectivityService that the network LINGER period has 199 * expired. 200 * obj = this NetworkAgentInfo 201 */ 202 public static final int EVENT_NETWORK_LINGER_COMPLETE = 1001; 203 204 // All linger timers for this network, sorted by expiry time. A linger timer is added whenever 205 // a request is moved to a network with a better score, regardless of whether the network is or 206 // was lingering or not. 207 // TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g., 208 // SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire. 209 private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>(); 210 211 // For fast lookups. Indexes into mLingerTimers by request ID. 212 private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>(); 213 214 // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the 215 // network is lingering or not. Always set to the expiry of the LingerTimer that expires last. 216 // When the timer fires, all linger state is cleared, and if the network has no requests, it is 217 // torn down. 218 private WakeupMessage mLingerMessage; 219 220 // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed. 221 private long mLingerExpiryMs; 222 223 // Whether the network is lingering or not. Must be maintained separately from the above because 224 // it depends on the state of other networks and requests, which only ConnectivityService knows. 225 // (Example: we don't linger a network if it would become the best for a NetworkRequest if it 226 // validated). 227 private boolean mLingering; 228 229 // This represents the last score received from the NetworkAgent. 230 private int currentScore; 231 232 // The list of NetworkRequests being satisfied by this Network. 233 private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>(); 234 235 // How many of the satisfied requests are actual requests and not listens. 236 private int mNumRequestNetworkRequests = 0; 237 238 // How many of the satisfied requests are of type BACKGROUND_REQUEST. 239 private int mNumBackgroundNetworkRequests = 0; 240 241 public final Messenger messenger; 242 public final AsyncChannel asyncChannel; 243 244 public final int factorySerialNumber; 245 246 // Used by ConnectivityService to keep track of 464xlat. 247 public final Nat464Xlat clatd; 248 249 // Set after asynchronous creation of the NetworkMonitor. 250 private volatile NetworkMonitorManager mNetworkMonitor; 251 252 private static final String TAG = ConnectivityService.class.getSimpleName(); 253 private static final boolean VDBG = false; 254 private final ConnectivityService mConnService; 255 private final Context mContext; 256 private final Handler mHandler; 257 NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, NetworkMisc misc, ConnectivityService connService, INetd netd, IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber)258 public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, 259 LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, 260 NetworkMisc misc, ConnectivityService connService, INetd netd, 261 IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) { 262 this.messenger = messenger; 263 asyncChannel = ac; 264 network = net; 265 networkInfo = info; 266 linkProperties = lp; 267 networkCapabilities = nc; 268 currentScore = score; 269 clatd = new Nat464Xlat(this, netd, dnsResolver, nms); 270 mConnService = connService; 271 mContext = context; 272 mHandler = handler; 273 networkMisc = misc; 274 this.factorySerialNumber = factorySerialNumber; 275 } 276 277 /** 278 * Inform NetworkAgentInfo that a new NetworkMonitor was created. 279 */ onNetworkMonitorCreated(INetworkMonitor networkMonitor)280 public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) { 281 mNetworkMonitor = new NetworkMonitorManager(networkMonitor); 282 } 283 284 /** 285 * Set the NetworkCapabilities on this NetworkAgentInfo. Also attempts to notify NetworkMonitor 286 * of the new capabilities, if NetworkMonitor has been created. 287 * 288 * <p>If {@link NetworkMonitor#notifyNetworkCapabilitiesChanged(NetworkCapabilities)} fails, 289 * the exception is logged but not reported to callers. 290 */ setNetworkCapabilities(NetworkCapabilities nc)291 public void setNetworkCapabilities(NetworkCapabilities nc) { 292 networkCapabilities = nc; 293 final NetworkMonitorManager nm = mNetworkMonitor; 294 if (nm != null) { 295 nm.notifyNetworkCapabilitiesChanged(nc); 296 } 297 } 298 connService()299 public ConnectivityService connService() { 300 return mConnService; 301 } 302 netMisc()303 public NetworkMisc netMisc() { 304 return networkMisc; 305 } 306 handler()307 public Handler handler() { 308 return mHandler; 309 } 310 network()311 public Network network() { 312 return network; 313 } 314 315 /** 316 * Get the NetworkMonitorManager in this NetworkAgentInfo. 317 * 318 * <p>This will be null before {@link #onNetworkMonitorCreated(INetworkMonitor)} is called. 319 */ networkMonitor()320 public NetworkMonitorManager networkMonitor() { 321 return mNetworkMonitor; 322 } 323 324 // Functions for manipulating the requests satisfied by this network. 325 // 326 // These functions must only called on ConnectivityService's main thread. 327 328 private static final boolean ADD = true; 329 private static final boolean REMOVE = false; 330 updateRequestCounts(boolean add, NetworkRequest request)331 private void updateRequestCounts(boolean add, NetworkRequest request) { 332 int delta = add ? +1 : -1; 333 switch (request.type) { 334 case REQUEST: 335 mNumRequestNetworkRequests += delta; 336 break; 337 338 case BACKGROUND_REQUEST: 339 mNumRequestNetworkRequests += delta; 340 mNumBackgroundNetworkRequests += delta; 341 break; 342 343 case TRACK_DEFAULT: 344 case LISTEN: 345 break; 346 347 case NONE: 348 default: 349 Log.wtf(TAG, "Unhandled request type " + request.type); 350 break; 351 } 352 } 353 354 /** 355 * Add {@code networkRequest} to this network as it's satisfied by this network. 356 * @return true if {@code networkRequest} was added or false if {@code networkRequest} was 357 * already present. 358 */ addRequest(NetworkRequest networkRequest)359 public boolean addRequest(NetworkRequest networkRequest) { 360 NetworkRequest existing = mNetworkRequests.get(networkRequest.requestId); 361 if (existing == networkRequest) return false; 362 if (existing != null) { 363 // Should only happen if the requestId wraps. If that happens lots of other things will 364 // be broken as well. 365 Log.wtf(TAG, String.format("Duplicate requestId for %s and %s on %s", 366 networkRequest, existing, name())); 367 updateRequestCounts(REMOVE, existing); 368 } 369 mNetworkRequests.put(networkRequest.requestId, networkRequest); 370 updateRequestCounts(ADD, networkRequest); 371 return true; 372 } 373 374 /** 375 * Remove the specified request from this network. 376 */ removeRequest(int requestId)377 public void removeRequest(int requestId) { 378 NetworkRequest existing = mNetworkRequests.get(requestId); 379 if (existing == null) return; 380 updateRequestCounts(REMOVE, existing); 381 mNetworkRequests.remove(requestId); 382 if (existing.isRequest()) { 383 unlingerRequest(existing); 384 } 385 } 386 387 /** 388 * Returns whether this network is currently satisfying the request with the specified ID. 389 */ isSatisfyingRequest(int id)390 public boolean isSatisfyingRequest(int id) { 391 return mNetworkRequests.get(id) != null; 392 } 393 394 /** 395 * Returns the request at the specified position in the list of requests satisfied by this 396 * network. 397 */ requestAt(int index)398 public NetworkRequest requestAt(int index) { 399 return mNetworkRequests.valueAt(index); 400 } 401 402 /** 403 * Returns the number of requests currently satisfied by this network for which 404 * {@link android.net.NetworkRequest#isRequest} returns {@code true}. 405 */ numRequestNetworkRequests()406 public int numRequestNetworkRequests() { 407 return mNumRequestNetworkRequests; 408 } 409 410 /** 411 * Returns the number of requests currently satisfied by this network of type 412 * {@link android.net.NetworkRequest.Type.BACKGROUND_REQUEST}. 413 */ numBackgroundNetworkRequests()414 public int numBackgroundNetworkRequests() { 415 return mNumBackgroundNetworkRequests; 416 } 417 418 /** 419 * Returns the number of foreground requests currently satisfied by this network. 420 */ numForegroundNetworkRequests()421 public int numForegroundNetworkRequests() { 422 return mNumRequestNetworkRequests - mNumBackgroundNetworkRequests; 423 } 424 425 /** 426 * Returns the number of requests of any type currently satisfied by this network. 427 */ numNetworkRequests()428 public int numNetworkRequests() { 429 return mNetworkRequests.size(); 430 } 431 432 /** 433 * Returns whether the network is a background network. A network is a background network if it 434 * does not have the NET_CAPABILITY_FOREGROUND capability, which implies it is satisfying no 435 * foreground request, is not lingering (i.e. kept for a while after being outscored), and is 436 * not a speculative network (i.e. kept pending validation when validation would have it 437 * outscore another foreground network). That implies it is being kept up by some background 438 * request (otherwise it would be torn down), maybe the mobile always-on request. 439 */ isBackgroundNetwork()440 public boolean isBackgroundNetwork() { 441 return !isVPN() && numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0 442 && !isLingering(); 443 } 444 445 /** 446 * Returns whether this network is currently suspended. A network is suspended if it is still 447 * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED} 448 * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}. 449 */ isSuspended()450 public boolean isSuspended() { 451 return networkInfo.getState() == NetworkInfo.State.SUSPENDED; 452 } 453 454 // Does this network satisfy request? satisfies(NetworkRequest request)455 public boolean satisfies(NetworkRequest request) { 456 return created && 457 request.networkCapabilities.satisfiedByNetworkCapabilities(networkCapabilities); 458 } 459 satisfiesImmutableCapabilitiesOf(NetworkRequest request)460 public boolean satisfiesImmutableCapabilitiesOf(NetworkRequest request) { 461 return created && 462 request.networkCapabilities.satisfiedByImmutableNetworkCapabilities( 463 networkCapabilities); 464 } 465 isVPN()466 public boolean isVPN() { 467 return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN); 468 } 469 getCurrentScore(boolean pretendValidated)470 private int getCurrentScore(boolean pretendValidated) { 471 // TODO: We may want to refactor this into a NetworkScore class that takes a base score from 472 // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the 473 // score. The NetworkScore class would provide a nice place to centralize score constants 474 // so they are not scattered about the transports. 475 476 // If this network is explicitly selected and the user has decided to use it even if it's 477 // unvalidated, give it the maximum score. Also give it the maximum score if it's explicitly 478 // selected and we're trying to see what its score could be. This ensures that we don't tear 479 // down an explicitly selected network before the user gets a chance to prefer it when 480 // a higher-scoring network (e.g., Ethernet) is available. 481 if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) { 482 return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE; 483 } 484 485 int score = currentScore; 486 if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) { 487 score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY; 488 } 489 if (score < 0) score = 0; 490 return score; 491 } 492 493 // Return true on devices configured to ignore score penalty for wifi networks 494 // that become unvalidated (b/31075769). ignoreWifiUnvalidationPenalty()495 private boolean ignoreWifiUnvalidationPenalty() { 496 boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && 497 networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 498 boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated; 499 return isWifi && !avoidBadWifi && everValidated; 500 } 501 502 // Get the current score for this Network. This may be modified from what the 503 // NetworkAgent sent, as it has modifiers applied to it. getCurrentScore()504 public int getCurrentScore() { 505 return getCurrentScore(false); 506 } 507 508 // Get the current score for this Network as if it was validated. This may be modified from 509 // what the NetworkAgent sent, as it has modifiers applied to it. getCurrentScoreAsValidated()510 public int getCurrentScoreAsValidated() { 511 return getCurrentScore(true); 512 } 513 setCurrentScore(int newScore)514 public void setCurrentScore(int newScore) { 515 currentScore = newScore; 516 } 517 getNetworkState()518 public NetworkState getNetworkState() { 519 synchronized (this) { 520 // Network objects are outwardly immutable so there is no point in duplicating. 521 // Duplicating also precludes sharing socket factories and connection pools. 522 final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null; 523 return new NetworkState(new NetworkInfo(networkInfo), 524 new LinkProperties(linkProperties), 525 new NetworkCapabilities(networkCapabilities), network, subscriberId, null); 526 } 527 } 528 529 /** 530 * Sets the specified request to linger on this network for the specified time. Called by 531 * ConnectivityService when the request is moved to another network with a higher score. 532 */ lingerRequest(NetworkRequest request, long now, long duration)533 public void lingerRequest(NetworkRequest request, long now, long duration) { 534 if (mLingerTimerForRequest.get(request.requestId) != null) { 535 // Cannot happen. Once a request is lingering on a particular network, we cannot 536 // re-linger it unless that network becomes the best for that request again, in which 537 // case we should have unlingered it. 538 Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered"); 539 } 540 final long expiryMs = now + duration; 541 LingerTimer timer = new LingerTimer(request, expiryMs); 542 if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name()); 543 mLingerTimers.add(timer); 544 mLingerTimerForRequest.put(request.requestId, timer); 545 } 546 547 /** 548 * Cancel lingering. Called by ConnectivityService when a request is added to this network. 549 * Returns true if the given request was lingering on this network, false otherwise. 550 */ unlingerRequest(NetworkRequest request)551 public boolean unlingerRequest(NetworkRequest request) { 552 LingerTimer timer = mLingerTimerForRequest.get(request.requestId); 553 if (timer != null) { 554 if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name()); 555 mLingerTimers.remove(timer); 556 mLingerTimerForRequest.remove(request.requestId); 557 return true; 558 } 559 return false; 560 } 561 getLingerExpiry()562 public long getLingerExpiry() { 563 return mLingerExpiryMs; 564 } 565 updateLingerTimer()566 public void updateLingerTimer() { 567 long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs; 568 if (newExpiry == mLingerExpiryMs) return; 569 570 // Even if we're going to reschedule the timer, cancel it first. This is because the 571 // semantics of WakeupMessage guarantee that if cancel is called then the alarm will 572 // never call its callback (handleLingerComplete), even if it has already fired. 573 // WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage 574 // has already been dispatched, rescheduling to some time in the future it won't stop it 575 // from calling its callback immediately. 576 if (mLingerMessage != null) { 577 mLingerMessage.cancel(); 578 mLingerMessage = null; 579 } 580 581 if (newExpiry > 0) { 582 mLingerMessage = mConnService.makeWakeupMessage( 583 mContext, mHandler, 584 "NETWORK_LINGER_COMPLETE." + network.netId, 585 EVENT_NETWORK_LINGER_COMPLETE, this); 586 mLingerMessage.schedule(newExpiry); 587 } 588 589 mLingerExpiryMs = newExpiry; 590 } 591 linger()592 public void linger() { 593 mLingering = true; 594 } 595 unlinger()596 public void unlinger() { 597 mLingering = false; 598 } 599 isLingering()600 public boolean isLingering() { 601 return mLingering; 602 } 603 clearLingerState()604 public void clearLingerState() { 605 if (mLingerMessage != null) { 606 mLingerMessage.cancel(); 607 mLingerMessage = null; 608 } 609 mLingerTimers.clear(); 610 mLingerTimerForRequest.clear(); 611 updateLingerTimer(); // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage. 612 mLingering = false; 613 } 614 dumpLingerTimers(PrintWriter pw)615 public void dumpLingerTimers(PrintWriter pw) { 616 for (LingerTimer timer : mLingerTimers) { pw.println(timer); } 617 } 618 619 // TODO: Print shorter members first and only print the boolean variable which value is true 620 // to improve readability. toString()621 public String toString() { 622 return "NetworkAgentInfo{ ni{" + networkInfo + "} " 623 + "network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " 624 + "lp{" + linkProperties + "} " 625 + "nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " 626 + "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " 627 + "created{" + created + "} lingering{" + isLingering() + "} " 628 + "explicitlySelected{" + networkMisc.explicitlySelected + "} " 629 + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " 630 + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " 631 + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " 632 + "captivePortalValidationPending{" + captivePortalValidationPending + "} " 633 + "partialConnectivity{" + partialConnectivity + "} " 634 + "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} " 635 + "clat{" + clatd + "} " 636 + "}"; 637 } 638 name()639 public String name() { 640 return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" + 641 networkInfo.getSubtypeName() + ") - " + Objects.toString(network) + "]"; 642 } 643 644 // Enables sorting in descending order of score. 645 @Override compareTo(NetworkAgentInfo other)646 public int compareTo(NetworkAgentInfo other) { 647 return other.getCurrentScore() - getCurrentScore(); 648 } 649 } 650