From 9e078d2f145d2b5c55a8fead43de6204f741d352 Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Tue, 9 Dec 2014 11:43:45 -0500 Subject: [PATCH] Don't blindly teardown unvalidated networks when releasing NetworkRequests. These networks may be on their way to becoming validated at which point they could satisfy the default NetworkRequest. This change unifies the is-this-network-needed code into a single function. bug:18652378 Change-Id: Ia511d5c66be79b47dd7c9348ec02784ab30b960c --- .../android/server/ConnectivityService.java | 87 ++++++++++--------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 766e4c766e..5e43cf6786 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2268,6 +2268,46 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + // Is nai unneeded by all NetworkRequests (and should be disconnected)? + // For validated Networks this is simply whether it is satsifying any NetworkRequests. + // For unvalidated Networks this is whether it is satsifying any NetworkRequests or + // were it to become validated, would it have a chance of satisfying any NetworkRequests. + private boolean unneeded(NetworkAgentInfo nai) { + if (!nai.created || nai.isVPN()) return false; + boolean unneeded = true; + if (nai.everValidated) { + for (int i = 0; i < nai.networkRequests.size() && unneeded; i++) { + final NetworkRequest nr = nai.networkRequests.valueAt(i); + try { + if (isRequest(nr)) unneeded = false; + } catch (Exception e) { + loge("Request " + nr + " not found in mNetworkRequests."); + loge(" it came from request list of " + nai.name()); + } + } + } else { + for (NetworkRequestInfo nri : mNetworkRequests.values()) { + // If this Network is already the highest scoring Network for a request, or if + // there is hope for it to become one if it validated, then it is needed. + if (nri.isRequest && nai.satisfies(nri.request) && + (nai.networkRequests.get(nri.request.requestId) != null || + // Note that this catches two important cases: + // 1. Unvalidated cellular will not be reaped when unvalidated WiFi + // is currently satisfying the request. This is desirable when + // cellular ends up validating but WiFi does not. + // 2. Unvalidated WiFi will not be reaped when validated cellular + // is currently satsifying the request. This is desirable when + // WiFi ends up validating and out scoring cellular. + mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() < + nai.getCurrentScoreAsValidated())) { + unneeded = false; + break; + } + } + } + return unneeded; + } + private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) { NetworkRequestInfo nri = mNetworkRequests.get(request); if (nri != null) { @@ -2289,16 +2329,9 @@ public class ConnectivityService extends IConnectivityManager.Stub ", leaving " + nai.networkRequests.size() + " requests."); } - // check if has any requests remaining and if not, - // disconnect (unless it's a VPN). - boolean keep = nai.isVPN(); - for (int i = 0; i < nai.networkRequests.size() && !keep; i++) { - NetworkRequest r = nai.networkRequests.valueAt(i); - if (isRequest(r)) keep = true; - } - if (!keep) { + if (unneeded(nai)) { if (DBG) log("no live requests for " + nai.name() + "; disconnecting"); - nai.asyncChannel.disconnect(); + teardownUnneededNetwork(nai); } } } @@ -4052,19 +4085,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Linger any networks that are no longer needed. for (NetworkAgentInfo nai : affectedNetworks) { - boolean teardown = !nai.isVPN() && nai.everValidated; - for (int i = 0; i < nai.networkRequests.size() && teardown; i++) { - NetworkRequest nr = nai.networkRequests.valueAt(i); - try { - if (isRequest(nr)) { - teardown = false; - } - } catch (Exception e) { - loge("Request " + nr + " not found in mNetworkRequests."); - loge(" it came from request list of " + nai.name()); - } - } - if (teardown) { + if (nai.everValidated && unneeded(nai)) { nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER); notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING); } else { @@ -4164,27 +4185,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) { for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { - if (!nai.created || nai.everValidated || nai.isVPN()) continue; - boolean reap = true; - for (NetworkRequestInfo nri : mNetworkRequests.values()) { - // If this Network is already the highest scoring Network for a request, or if - // there is hope for it to become one if it validated, then don't reap it. - if (nri.isRequest && nai.satisfies(nri.request) && - (nai.networkRequests.get(nri.request.requestId) != null || - // Note that this catches two important cases: - // 1. Unvalidated cellular will not be reaped when unvalidated WiFi - // is currently satisfying the request. This is desirable when - // cellular ends up validating but WiFi does not. - // 2. Unvalidated WiFi will not be reaped when validated cellular - // is currently satsifying the request. This is desirable when - // WiFi ends up validating and out scoring cellular. - mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() < - nai.getCurrentScoreAsValidated())) { - reap = false; - break; - } - } - if (reap) { + if (!nai.everValidated && unneeded(nai)) { if (DBG) log("Reaping " + nai.name()); teardownUnneededNetwork(nai); }