Merge \"Rewrite lingering.\" into nyc-mr1-dev
am: 673bdd122b
Change-Id: I62d69f20101318b42d4d333b104dcf4f64dc1725
This commit is contained in:
@@ -94,6 +94,7 @@ import android.os.PowerManager;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
@@ -125,6 +126,7 @@ import com.android.internal.net.VpnProfile;
|
|||||||
import com.android.internal.util.AsyncChannel;
|
import com.android.internal.util.AsyncChannel;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
import com.android.internal.util.MessageUtils;
|
import com.android.internal.util.MessageUtils;
|
||||||
|
import com.android.internal.util.WakeupMessage;
|
||||||
import com.android.internal.util.XmlUtils;
|
import com.android.internal.util.XmlUtils;
|
||||||
import com.android.server.am.BatteryStatsService;
|
import com.android.server.am.BatteryStatsService;
|
||||||
import com.android.server.connectivity.DataConnectionStats;
|
import com.android.server.connectivity.DataConnectionStats;
|
||||||
@@ -171,7 +173,7 @@ import java.util.TreeSet;
|
|||||||
*/
|
*/
|
||||||
public class ConnectivityService extends IConnectivityManager.Stub
|
public class ConnectivityService extends IConnectivityManager.Stub
|
||||||
implements PendingIntent.OnFinished {
|
implements PendingIntent.OnFinished {
|
||||||
private static final String TAG = "ConnectivityService";
|
private static final String TAG = ConnectivityService.class.getSimpleName();
|
||||||
|
|
||||||
private static final boolean DBG = true;
|
private static final boolean DBG = true;
|
||||||
private static final boolean VDBG = false;
|
private static final boolean VDBG = false;
|
||||||
@@ -191,6 +193,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
// connect anyway?" dialog after the user selects a network that doesn't validate.
|
// connect anyway?" dialog after the user selects a network that doesn't validate.
|
||||||
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
|
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
|
||||||
|
|
||||||
|
// Default to 30s linger time-out. Modifiable only for testing.
|
||||||
|
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
|
||||||
|
private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
|
||||||
|
@VisibleForTesting
|
||||||
|
protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
|
||||||
|
|
||||||
// How long to delay to removal of a pending intent based request.
|
// How long to delay to removal of a pending intent based request.
|
||||||
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
|
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
|
||||||
private final int mReleasePendingIntentDelayMs;
|
private final int mReleasePendingIntentDelayMs;
|
||||||
@@ -239,7 +247,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
private static final int DISABLED = 0;
|
private static final int DISABLED = 0;
|
||||||
|
|
||||||
private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
|
private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
|
||||||
new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class });
|
new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class,
|
||||||
|
NetworkAgentInfo.class });
|
||||||
|
|
||||||
private enum ReapUnvalidatedNetworks {
|
private enum ReapUnvalidatedNetworks {
|
||||||
// Tear down networks that have no chance (e.g. even if validated) of becoming
|
// Tear down networks that have no chance (e.g. even if validated) of becoming
|
||||||
@@ -681,6 +690,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
|
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
|
||||||
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
|
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
|
||||||
|
|
||||||
|
mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
|
||||||
|
|
||||||
mContext = checkNotNull(context, "missing Context");
|
mContext = checkNotNull(context, "missing Context");
|
||||||
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
|
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
|
||||||
mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
|
mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
|
||||||
@@ -1905,7 +1916,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
|
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
|
||||||
pw.println(nai.toString());
|
pw.println(nai.toString());
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
pw.println("Requests:");
|
pw.println(String.format("Requests: %d request/%d total",
|
||||||
|
nai.numRequestNetworkRequests(), nai.numNetworkRequests()));
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
for (int i = 0; i < nai.numNetworkRequests(); i++) {
|
for (int i = 0; i < nai.numNetworkRequests(); i++) {
|
||||||
pw.println(nai.requestAt(i).toString());
|
pw.println(nai.requestAt(i).toString());
|
||||||
@@ -1913,7 +1925,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
pw.println("Lingered:");
|
pw.println("Lingered:");
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
for (NetworkRequest nr : nai.networkLingered) pw.println(nr.toString());
|
nai.dumpLingerTimers(pw);
|
||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
}
|
}
|
||||||
@@ -2158,13 +2170,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: {
|
|
||||||
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
|
|
||||||
if (isLiveNetworkAgent(nai, msg.what)) {
|
|
||||||
handleLingerComplete(nai);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
|
case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
|
||||||
final int netId = msg.arg2;
|
final int netId = msg.arg2;
|
||||||
final boolean visible = (msg.arg1 != 0);
|
final boolean visible = (msg.arg1 != 0);
|
||||||
@@ -2197,33 +2202,50 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean maybeHandleNetworkAgentInfoMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
case NetworkAgentInfo.EVENT_NETWORK_LINGER_COMPLETE: {
|
||||||
|
NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
|
||||||
|
if (nai != null && isLiveNetworkAgent(nai, msg.what)) {
|
||||||
|
handleLingerComplete(nai);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
if (!maybeHandleAsyncChannelMessage(msg) && !maybeHandleNetworkMonitorMessage(msg)) {
|
if (!maybeHandleAsyncChannelMessage(msg) &&
|
||||||
|
!maybeHandleNetworkMonitorMessage(msg) &&
|
||||||
|
!maybeHandleNetworkAgentInfoMessage(msg)) {
|
||||||
maybeHandleNetworkAgentMessage(msg);
|
maybeHandleNetworkAgentMessage(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void linger(NetworkAgentInfo nai) {
|
private void updateLingerState(NetworkAgentInfo nai, long now) {
|
||||||
nai.lingering = true;
|
// 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
|
||||||
logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
|
// 2. If the network was lingering and there are now requests, unlinger it.
|
||||||
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER);
|
// 3. If this network is unneeded (which implies it is not lingering), and there is at least
|
||||||
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING);
|
// one lingered request, start lingering.
|
||||||
}
|
nai.updateLingerTimer();
|
||||||
|
if (nai.isLingering() && nai.numRequestNetworkRequests() > 0) {
|
||||||
// Cancel any lingering so the linger timeout doesn't teardown a network.
|
if (DBG) log("Unlingering " + nai.name());
|
||||||
// This should be called when a network begins satisfying a NetworkRequest.
|
nai.unlinger();
|
||||||
// Note: depending on what state the NetworkMonitor is in (e.g.,
|
|
||||||
// if it's awaiting captive portal login, or if validation failed), this
|
|
||||||
// may trigger a re-evaluation of the network.
|
|
||||||
private void unlinger(NetworkAgentInfo nai) {
|
|
||||||
nai.networkLingered.clear();
|
|
||||||
if (!nai.lingering) return;
|
|
||||||
nai.lingering = false;
|
|
||||||
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
|
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
|
||||||
if (VDBG) log("Canceling linger of " + nai.name());
|
} else if (unneeded(nai) && nai.getLingerExpiry() > 0) { // unneeded() calls isLingering()
|
||||||
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
|
int lingerTime = (int) (nai.getLingerExpiry() - now);
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "Lingering " + nai.name() + " for " + lingerTime + "ms");
|
||||||
|
}
|
||||||
|
nai.linger();
|
||||||
|
logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
|
||||||
|
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAsyncChannelHalfConnect(Message msg) {
|
private void handleAsyncChannelHalfConnect(Message msg) {
|
||||||
@@ -2313,6 +2335,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
sendUpdatedScoreToFactories(request, 0);
|
sendUpdatedScoreToFactories(request, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
nai.clearLingerState();
|
||||||
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
|
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
|
||||||
removeDataActivityTracking(nai);
|
removeDataActivityTracking(nai);
|
||||||
notifyLockdownVpn(nai);
|
notifyLockdownVpn(nai);
|
||||||
@@ -2400,7 +2423,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
// This is whether it is satisfying any NetworkRequests or were it to become validated,
|
// This is whether it is satisfying any NetworkRequests or were it to become validated,
|
||||||
// would it have a chance of satisfying any NetworkRequests.
|
// would it have a chance of satisfying any NetworkRequests.
|
||||||
private boolean unneeded(NetworkAgentInfo nai) {
|
private boolean unneeded(NetworkAgentInfo nai) {
|
||||||
if (!nai.everConnected || nai.isVPN() || nai.lingering) return false;
|
if (!nai.everConnected || nai.isVPN() ||
|
||||||
|
nai.isLingering() || nai.numRequestNetworkRequests() > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
|
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
|
||||||
// If this Network is already the highest scoring Network for a request, or if
|
// 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.
|
// there is hope for it to become one if it validated, then it is needed.
|
||||||
@@ -2453,6 +2479,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
log(" Removing from current network " + nai.name() +
|
log(" Removing from current network " + nai.name() +
|
||||||
", leaving " + nai.numNetworkRequests() + " requests.");
|
", leaving " + nai.numNetworkRequests() + " requests.");
|
||||||
}
|
}
|
||||||
|
// If there are still lingered requests on this network, don't tear it down,
|
||||||
|
// but resume lingering instead.
|
||||||
|
updateLingerState(nai, SystemClock.elapsedRealtime());
|
||||||
if (unneeded(nai)) {
|
if (unneeded(nai)) {
|
||||||
if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
|
if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
|
||||||
teardownUnneededNetwork(nai);
|
teardownUnneededNetwork(nai);
|
||||||
@@ -2516,7 +2545,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED);
|
callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4503,7 +4532,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void callCallbackForRequest(NetworkRequestInfo nri,
|
private void callCallbackForRequest(NetworkRequestInfo nri,
|
||||||
NetworkAgentInfo networkAgent, int notificationType) {
|
NetworkAgentInfo networkAgent, int notificationType, int arg1) {
|
||||||
if (nri.messenger == null) return; // Default request has no msgr
|
if (nri.messenger == null) return; // Default request has no msgr
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelable(NetworkRequest.class.getSimpleName(),
|
bundle.putParcelable(NetworkRequest.class.getSimpleName(),
|
||||||
@@ -4515,7 +4544,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
switch (notificationType) {
|
switch (notificationType) {
|
||||||
case ConnectivityManager.CALLBACK_LOSING: {
|
case ConnectivityManager.CALLBACK_LOSING: {
|
||||||
msg.arg1 = 30 * 1000; // TODO - read this from NetworkMonitor
|
msg.arg1 = arg1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
|
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
|
||||||
@@ -4562,8 +4591,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (DBG) log("handleLingerComplete for " + oldNetwork.name());
|
if (DBG) log("handleLingerComplete for " + oldNetwork.name());
|
||||||
|
|
||||||
|
// If we get here it means that the last linger timeout for this network expired. So there
|
||||||
|
// must be no other active linger timers, and we must stop lingering.
|
||||||
|
oldNetwork.clearLingerState();
|
||||||
|
|
||||||
|
if (unneeded(oldNetwork)) {
|
||||||
teardownUnneededNetwork(oldNetwork);
|
teardownUnneededNetwork(oldNetwork);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void makeDefault(NetworkAgentInfo newNetwork) {
|
private void makeDefault(NetworkAgentInfo newNetwork) {
|
||||||
if (DBG) log("Switching to new default network: " + newNetwork);
|
if (DBG) log("Switching to new default network: " + newNetwork);
|
||||||
@@ -4607,7 +4643,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
// performed to tear down unvalidated networks that have no chance (i.e. even if
|
// performed to tear down unvalidated networks that have no chance (i.e. even if
|
||||||
// validated) of becoming the highest scoring network.
|
// validated) of becoming the highest scoring network.
|
||||||
private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork,
|
private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork,
|
||||||
ReapUnvalidatedNetworks reapUnvalidatedNetworks) {
|
ReapUnvalidatedNetworks reapUnvalidatedNetworks, long now) {
|
||||||
if (!newNetwork.everConnected) return;
|
if (!newNetwork.everConnected) return;
|
||||||
boolean keep = newNetwork.isVPN();
|
boolean keep = newNetwork.isVPN();
|
||||||
boolean isNewDefault = false;
|
boolean isNewDefault = false;
|
||||||
@@ -4653,12 +4689,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
if (currentNetwork != null) {
|
if (currentNetwork != null) {
|
||||||
if (VDBG) log(" accepting network in place of " + currentNetwork.name());
|
if (VDBG) log(" accepting network in place of " + currentNetwork.name());
|
||||||
currentNetwork.removeRequest(nri.request.requestId);
|
currentNetwork.removeRequest(nri.request.requestId);
|
||||||
currentNetwork.networkLingered.add(nri.request);
|
currentNetwork.lingerRequest(nri.request, now, mLingerDelayMs);
|
||||||
affectedNetworks.add(currentNetwork);
|
affectedNetworks.add(currentNetwork);
|
||||||
} else {
|
} else {
|
||||||
if (VDBG) log(" accepting network in place of null");
|
if (VDBG) log(" accepting network in place of null");
|
||||||
}
|
}
|
||||||
unlinger(newNetwork);
|
newNetwork.unlingerRequest(nri.request);
|
||||||
mNetworkForRequestId.put(nri.request.requestId, newNetwork);
|
mNetworkForRequestId.put(nri.request.requestId, newNetwork);
|
||||||
if (!newNetwork.addRequest(nri.request)) {
|
if (!newNetwork.addRequest(nri.request)) {
|
||||||
Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request);
|
Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request);
|
||||||
@@ -4706,23 +4742,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
// a) be requested and b) change is NET_CAPABILITY_TRUSTED,
|
// a) be requested and b) change is NET_CAPABILITY_TRUSTED,
|
||||||
// so this code is only incorrect for a network that loses
|
// so this code is only incorrect for a network that loses
|
||||||
// the TRUSTED capability, which is a rare case.
|
// the TRUSTED capability, which is a rare case.
|
||||||
callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST);
|
callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST, 0);
|
||||||
}
|
|
||||||
}
|
|
||||||
// Linger any networks that are no longer needed.
|
|
||||||
for (NetworkAgentInfo nai : affectedNetworks) {
|
|
||||||
if (nai.lingering) {
|
|
||||||
// Already lingered. Nothing to do. This can only happen if "nai" is in
|
|
||||||
// "affectedNetworks" twice. The reasoning being that to get added to
|
|
||||||
// "affectedNetworks", "nai" must have been satisfying a NetworkRequest
|
|
||||||
// (i.e. not lingered) so it could have only been lingered by this loop.
|
|
||||||
// unneeded(nai) will be false and we'll call unlinger() below which would
|
|
||||||
// be bad, so handle it here.
|
|
||||||
} else if (unneeded(nai)) {
|
|
||||||
linger(nai);
|
|
||||||
} else {
|
|
||||||
// Clear nai.networkLingered we might have added above.
|
|
||||||
unlinger(nai);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isNewDefault) {
|
if (isNewDefault) {
|
||||||
@@ -4747,6 +4767,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
// before LegacyTypeTracker sends legacy broadcasts
|
// before LegacyTypeTracker sends legacy broadcasts
|
||||||
for (NetworkRequestInfo nri : addedRequests) notifyNetworkCallback(newNetwork, nri);
|
for (NetworkRequestInfo nri : addedRequests) notifyNetworkCallback(newNetwork, nri);
|
||||||
|
|
||||||
|
// Linger any networks that are no longer needed. This should be done after sending the
|
||||||
|
// available callback for newNetwork.
|
||||||
|
for (NetworkAgentInfo nai : affectedNetworks) {
|
||||||
|
updateLingerState(nai, now);
|
||||||
|
}
|
||||||
|
// Possibly unlinger newNetwork. Unlingering a network does not send any callbacks so it
|
||||||
|
// does not need to be done in any particular order.
|
||||||
|
updateLingerState(newNetwork, now);
|
||||||
|
|
||||||
if (isNewDefault) {
|
if (isNewDefault) {
|
||||||
// Maintain the illusion: since the legacy API only
|
// Maintain the illusion: since the legacy API only
|
||||||
// understands one network at a time, we must pretend
|
// understands one network at a time, we must pretend
|
||||||
@@ -4812,12 +4841,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
|
if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
|
||||||
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
|
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
|
||||||
if (unneeded(nai)) {
|
if (unneeded(nai)) {
|
||||||
|
if (nai.getLingerExpiry() > 0) {
|
||||||
|
// This network has active linger timers and no requests, but is not
|
||||||
|
// lingering. Linger it.
|
||||||
|
//
|
||||||
|
// One way (the only way?) this can happen if this network is unvalidated
|
||||||
|
// and became unneeded due to another network improving its score to the
|
||||||
|
// point where this network will no longer be able to satisfy any requests
|
||||||
|
// even if it validates.
|
||||||
|
updateLingerState(nai, now);
|
||||||
|
} else {
|
||||||
if (DBG) log("Reaping " + nai.name());
|
if (DBG) log("Reaping " + nai.name());
|
||||||
teardownUnneededNetwork(nai);
|
teardownUnneededNetwork(nai);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to rematch all Networks with NetworkRequests. This may result in Networks
|
* Attempt to rematch all Networks with NetworkRequests. This may result in Networks
|
||||||
@@ -4840,8 +4880,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
// Optimization: Only reprocess "changed" if its score improved. This is safe because it
|
// Optimization: Only reprocess "changed" if its score improved. This is safe because it
|
||||||
// can only add more NetworkRequests satisfied by "changed", and this is exactly what
|
// can only add more NetworkRequests satisfied by "changed", and this is exactly what
|
||||||
// rematchNetworkAndRequests() handles.
|
// rematchNetworkAndRequests() handles.
|
||||||
|
final long now = SystemClock.elapsedRealtime();
|
||||||
if (changed != null && oldScore < changed.getCurrentScore()) {
|
if (changed != null && oldScore < changed.getCurrentScore()) {
|
||||||
rematchNetworkAndRequests(changed, ReapUnvalidatedNetworks.REAP);
|
rematchNetworkAndRequests(changed, ReapUnvalidatedNetworks.REAP, now);
|
||||||
} else {
|
} else {
|
||||||
final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
|
final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
|
||||||
new NetworkAgentInfo[mNetworkAgentInfos.size()]);
|
new NetworkAgentInfo[mNetworkAgentInfos.size()]);
|
||||||
@@ -4855,7 +4896,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
// is complete could incorrectly teardown a network that hasn't yet been
|
// is complete could incorrectly teardown a network that hasn't yet been
|
||||||
// rematched.
|
// rematched.
|
||||||
(nai != nais[nais.length-1]) ? ReapUnvalidatedNetworks.DONT_REAP
|
(nai != nais[nais.length-1]) ? ReapUnvalidatedNetworks.DONT_REAP
|
||||||
: ReapUnvalidatedNetworks.REAP);
|
: ReapUnvalidatedNetworks.REAP,
|
||||||
|
now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4965,7 +5007,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
|
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
|
||||||
|
|
||||||
// Consider network even though it is not yet validated.
|
// Consider network even though it is not yet validated.
|
||||||
rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP);
|
final long now = SystemClock.elapsedRealtime();
|
||||||
|
rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
|
||||||
|
|
||||||
// This has to happen after matching the requests, because callbacks are just requests.
|
// This has to happen after matching the requests, because callbacks are just requests.
|
||||||
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
|
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
|
||||||
@@ -5013,14 +5056,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
// notify only this one new request of the current state
|
// notify only this one new request of the current state
|
||||||
protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
|
protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
|
||||||
int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
|
int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
|
||||||
// TODO - read state from monitor to decide what to send.
|
|
||||||
// if (nai.networkMonitor.isLingering()) {
|
|
||||||
// notifyType = NetworkCallbacks.LOSING;
|
|
||||||
// } else if (nai.networkMonitor.isEvaluating()) {
|
|
||||||
// notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
|
|
||||||
// }
|
|
||||||
if (nri.mPendingIntent == null) {
|
if (nri.mPendingIntent == null) {
|
||||||
callCallbackForRequest(nri, nai, notifyType);
|
callCallbackForRequest(nri, nai, notifyType, 0);
|
||||||
} else {
|
} else {
|
||||||
sendPendingIntentForRequest(nri, nai, notifyType);
|
sendPendingIntentForRequest(nri, nai, notifyType);
|
||||||
}
|
}
|
||||||
@@ -5072,20 +5109,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
|
protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType, int arg1) {
|
||||||
if (VDBG) log("notifyType " + notifyTypeToName(notifyType) + " for " + networkAgent.name());
|
if (VDBG) log("notifyType " + notifyTypeToName(notifyType) + " for " + networkAgent.name());
|
||||||
for (int i = 0; i < networkAgent.numNetworkRequests(); i++) {
|
for (int i = 0; i < networkAgent.numNetworkRequests(); i++) {
|
||||||
NetworkRequest nr = networkAgent.requestAt(i);
|
NetworkRequest nr = networkAgent.requestAt(i);
|
||||||
NetworkRequestInfo nri = mNetworkRequests.get(nr);
|
NetworkRequestInfo nri = mNetworkRequests.get(nr);
|
||||||
if (VDBG) log(" sending notification for " + nr);
|
if (VDBG) log(" sending notification for " + nr);
|
||||||
if (nri.mPendingIntent == null) {
|
if (nri.mPendingIntent == null) {
|
||||||
callCallbackForRequest(nri, networkAgent, notifyType);
|
callCallbackForRequest(nri, networkAgent, notifyType, arg1);
|
||||||
} else {
|
} else {
|
||||||
sendPendingIntentForRequest(nri, networkAgent, notifyType);
|
sendPendingIntentForRequest(nri, networkAgent, notifyType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
|
||||||
|
notifyNetworkCallbacks(networkAgent, notifyType, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private String notifyTypeToName(int notifyType) {
|
private String notifyTypeToName(int notifyType) {
|
||||||
switch (notifyType) {
|
switch (notifyType) {
|
||||||
case ConnectivityManager.CALLBACK_PRECHECK: return "PRECHECK";
|
case ConnectivityManager.CALLBACK_PRECHECK: return "PRECHECK";
|
||||||
@@ -5216,6 +5257,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
return new NetworkMonitor(context, handler, nai, defaultRequest);
|
return new NetworkMonitor(context, handler, nai, defaultRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public WakeupMessage makeWakeupMessage(Context c, Handler h, String s, int cmd, Object obj) {
|
||||||
|
return new WakeupMessage(c, h, s, cmd, 0, 0, obj);
|
||||||
|
}
|
||||||
|
|
||||||
private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
|
private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
|
||||||
int newNetid = NETID_UNSET;
|
int newNetid = NETID_UNSET;
|
||||||
int prevNetid = NETID_UNSET;
|
int prevNetid = NETID_UNSET;
|
||||||
|
|||||||
@@ -28,14 +28,21 @@ import android.net.NetworkRequest;
|
|||||||
import android.net.NetworkState;
|
import android.net.NetworkState;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.internal.util.AsyncChannel;
|
import com.android.internal.util.AsyncChannel;
|
||||||
|
import com.android.internal.util.WakeupMessage;
|
||||||
import com.android.server.ConnectivityService;
|
import com.android.server.ConnectivityService;
|
||||||
import com.android.server.connectivity.NetworkMonitor;
|
import com.android.server.connectivity.NetworkMonitor;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A bag class used by ConnectivityService for holding a collection of most recent
|
* A bag class used by ConnectivityService for holding a collection of most recent
|
||||||
@@ -143,12 +150,69 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
// Whether a captive portal was found during the last network validation attempt.
|
// Whether a captive portal was found during the last network validation attempt.
|
||||||
public boolean lastCaptivePortalDetected;
|
public boolean lastCaptivePortalDetected;
|
||||||
|
|
||||||
// Indicates whether the network is lingering. Networks are lingered when they become unneeded
|
// Networks are lingered when they become unneeded as a result of their NetworkRequests being
|
||||||
// as a result of their NetworkRequests being satisfied by a different network, so as to allow
|
// satisfied by a higher-scoring network. so as to allow communication to wrap up before the
|
||||||
// communication to wrap up before the network is taken down. This usually only happens to the
|
// network is taken down. This usually only happens to the default network. Lingering ends with
|
||||||
// default network. Lingering ends with either the linger timeout expiring and the network
|
// either the linger timeout expiring and the network being taken down, or the network
|
||||||
// being taken down, or the network satisfying a request again.
|
// satisfying a request again.
|
||||||
public boolean lingering;
|
public static class LingerTimer implements Comparable<LingerTimer> {
|
||||||
|
public final NetworkRequest request;
|
||||||
|
public final long expiryMs;
|
||||||
|
|
||||||
|
public LingerTimer(NetworkRequest request, long expiryMs) {
|
||||||
|
this.request = request;
|
||||||
|
this.expiryMs = expiryMs;
|
||||||
|
}
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof LingerTimer)) return false;
|
||||||
|
LingerTimer other = (LingerTimer) o;
|
||||||
|
return (request.requestId == other.request.requestId) && (expiryMs == other.expiryMs);
|
||||||
|
}
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(request.requestId, expiryMs);
|
||||||
|
}
|
||||||
|
public int compareTo(LingerTimer other) {
|
||||||
|
return (expiryMs != other.expiryMs) ?
|
||||||
|
Long.compare(expiryMs, other.expiryMs) :
|
||||||
|
Integer.compare(request.requestId, other.request.requestId);
|
||||||
|
}
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%s, expires %dms", request.toString(),
|
||||||
|
expiryMs - SystemClock.elapsedRealtime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inform ConnectivityService that the network LINGER period has
|
||||||
|
* expired.
|
||||||
|
* obj = this NetworkAgentInfo
|
||||||
|
*/
|
||||||
|
public static final int EVENT_NETWORK_LINGER_COMPLETE = 1001;
|
||||||
|
|
||||||
|
// All linger timers for this network, sorted by expiry time. A linger timer is added whenever
|
||||||
|
// a request is moved to a network with a better score, regardless of whether the network is or
|
||||||
|
// was lingering or not.
|
||||||
|
// TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
|
||||||
|
// SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
|
||||||
|
private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
|
||||||
|
|
||||||
|
// For fast lookups. Indexes into mLingerTimers by request ID.
|
||||||
|
private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
|
||||||
|
|
||||||
|
// Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
|
||||||
|
// network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
|
||||||
|
// When the timer fires, all linger state is cleared, and if the network has no requests, it is
|
||||||
|
// torn down.
|
||||||
|
private WakeupMessage mLingerMessage;
|
||||||
|
|
||||||
|
// Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
|
||||||
|
private long mLingerExpiryMs;
|
||||||
|
|
||||||
|
// Whether the network is lingering or not. Must be maintained separately from the above because
|
||||||
|
// it depends on the state of other networks and requests, which only ConnectivityService knows.
|
||||||
|
// (Example: we don't linger a network if it would become the best for a NetworkRequest if it
|
||||||
|
// validated).
|
||||||
|
private boolean mLingering;
|
||||||
|
|
||||||
// This represents the last score received from the NetworkAgent.
|
// This represents the last score received from the NetworkAgent.
|
||||||
private int currentScore;
|
private int currentScore;
|
||||||
@@ -165,8 +229,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
|
private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
|
||||||
// The list of NetworkRequests that this Network previously satisfied with the highest
|
// The list of NetworkRequests that this Network previously satisfied with the highest
|
||||||
// score. A non-empty list indicates that if this Network was validated it is lingered.
|
// score. A non-empty list indicates that if this Network was validated it is lingered.
|
||||||
// NOTE: This list is only used for debugging.
|
|
||||||
public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
|
|
||||||
// How many of the satisfied requests are actual requests and not listens.
|
// How many of the satisfied requests are actual requests and not listens.
|
||||||
private int mNumRequestNetworkRequests = 0;
|
private int mNumRequestNetworkRequests = 0;
|
||||||
|
|
||||||
@@ -176,6 +238,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
// Used by ConnectivityService to keep track of 464xlat.
|
// Used by ConnectivityService to keep track of 464xlat.
|
||||||
public Nat464Xlat clatd;
|
public Nat464Xlat clatd;
|
||||||
|
|
||||||
|
private static final String TAG = ConnectivityService.class.getSimpleName();
|
||||||
|
private static final boolean VDBG = false;
|
||||||
|
private final ConnectivityService mConnService;
|
||||||
|
private final Context mContext;
|
||||||
|
private final Handler mHandler;
|
||||||
|
|
||||||
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
|
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
|
||||||
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
|
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
|
||||||
NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
|
NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
|
||||||
@@ -186,7 +254,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
linkProperties = lp;
|
linkProperties = lp;
|
||||||
networkCapabilities = nc;
|
networkCapabilities = nc;
|
||||||
currentScore = score;
|
currentScore = score;
|
||||||
networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
|
mConnService = connService;
|
||||||
|
mContext = context;
|
||||||
|
mHandler = handler;
|
||||||
|
networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
|
||||||
networkMisc = misc;
|
networkMisc = misc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,8 +284,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
*/
|
*/
|
||||||
public void removeRequest(int requestId) {
|
public void removeRequest(int requestId) {
|
||||||
NetworkRequest existing = mNetworkRequests.get(requestId);
|
NetworkRequest existing = mNetworkRequests.get(requestId);
|
||||||
if (existing != null && existing.isRequest()) mNumRequestNetworkRequests--;
|
if (existing == null) return;
|
||||||
mNetworkRequests.remove(requestId);
|
mNetworkRequests.remove(requestId);
|
||||||
|
if (existing.isRequest()) {
|
||||||
|
mNumRequestNetworkRequests--;
|
||||||
|
unlingerRequest(existing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -316,13 +391,100 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the specified request to linger on this network for the specified time. Called by
|
||||||
|
* ConnectivityService when the request is moved to another network with a higher score.
|
||||||
|
*/
|
||||||
|
public void lingerRequest(NetworkRequest request, long now, long duration) {
|
||||||
|
if (mLingerTimerForRequest.get(request.requestId) != null) {
|
||||||
|
// Cannot happen. Once a request is lingering on a particular network, we cannot
|
||||||
|
// re-linger it unless that network becomes the best for that request again, in which
|
||||||
|
// case we should have unlingered it.
|
||||||
|
Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered");
|
||||||
|
}
|
||||||
|
final long expiryMs = now + duration;
|
||||||
|
LingerTimer timer = new LingerTimer(request, expiryMs);
|
||||||
|
if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name());
|
||||||
|
mLingerTimers.add(timer);
|
||||||
|
mLingerTimerForRequest.put(request.requestId, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel lingering. Called by ConnectivityService when a request is added to this network.
|
||||||
|
*/
|
||||||
|
public void unlingerRequest(NetworkRequest request) {
|
||||||
|
LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
|
||||||
|
if (timer != null) {
|
||||||
|
if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
|
||||||
|
mLingerTimers.remove(timer);
|
||||||
|
mLingerTimerForRequest.remove(request.requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLingerExpiry() {
|
||||||
|
return mLingerExpiryMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateLingerTimer() {
|
||||||
|
long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
|
||||||
|
if (newExpiry == mLingerExpiryMs) return;
|
||||||
|
|
||||||
|
// Even if we're going to reschedule the timer, cancel it first. This is because the
|
||||||
|
// semantics of WakeupMessage guarantee that if cancel is called then the alarm will
|
||||||
|
// never call its callback (handleLingerComplete), even if it has already fired.
|
||||||
|
// WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
|
||||||
|
// has already been dispatched, rescheduling to some time in the future it won't stop it
|
||||||
|
// from calling its callback immediately.
|
||||||
|
if (mLingerMessage != null) {
|
||||||
|
mLingerMessage.cancel();
|
||||||
|
mLingerMessage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newExpiry > 0) {
|
||||||
|
mLingerMessage = mConnService.makeWakeupMessage(
|
||||||
|
mContext, mHandler,
|
||||||
|
"NETWORK_LINGER_COMPLETE." + network.netId,
|
||||||
|
EVENT_NETWORK_LINGER_COMPLETE, this);
|
||||||
|
mLingerMessage.schedule(newExpiry);
|
||||||
|
}
|
||||||
|
|
||||||
|
mLingerExpiryMs = newExpiry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void linger() {
|
||||||
|
mLingering = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unlinger() {
|
||||||
|
mLingering = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLingering() {
|
||||||
|
return mLingering;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearLingerState() {
|
||||||
|
if (mLingerMessage != null) {
|
||||||
|
mLingerMessage.cancel();
|
||||||
|
mLingerMessage = null;
|
||||||
|
}
|
||||||
|
mLingerTimers.clear();
|
||||||
|
mLingerTimerForRequest.clear();
|
||||||
|
updateLingerTimer(); // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
|
||||||
|
mLingering = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dumpLingerTimers(PrintWriter pw) {
|
||||||
|
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
|
||||||
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
|
return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
|
||||||
"network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
|
"network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
|
||||||
"lp{" + linkProperties + "} " +
|
"lp{" + linkProperties + "} " +
|
||||||
"nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " +
|
"nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " +
|
||||||
"everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " +
|
"everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " +
|
||||||
"created{" + created + "} lingering{" + lingering + "} " +
|
"created{" + created + "} lingering{" + isLingering() + "} " +
|
||||||
"explicitlySelected{" + networkMisc.explicitlySelected + "} " +
|
"explicitlySelected{" + networkMisc.explicitlySelected + "} " +
|
||||||
"acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
|
"acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
|
||||||
"everCaptivePortalDetected{" + everCaptivePortalDetected + "} " +
|
"everCaptivePortalDetected{" + everCaptivePortalDetected + "} " +
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
private static final String TAG = "ConnectivityServiceTest";
|
private static final String TAG = "ConnectivityServiceTest";
|
||||||
|
|
||||||
private static final int TIMEOUT_MS = 500;
|
private static final int TIMEOUT_MS = 500;
|
||||||
|
private static final int TEST_LINGER_DELAY_MS = 120;
|
||||||
|
|
||||||
private BroadcastInterceptingContext mServiceContext;
|
private BroadcastInterceptingContext mServiceContext;
|
||||||
private WrappedConnectivityService mService;
|
private WrappedConnectivityService mService;
|
||||||
@@ -330,7 +331,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
* @param validated Indicate if network should pretend to be validated.
|
* @param validated Indicate if network should pretend to be validated.
|
||||||
*/
|
*/
|
||||||
public void connect(boolean validated) {
|
public void connect(boolean validated) {
|
||||||
assertEquals(mNetworkInfo.getDetailedState(), DetailedState.IDLE);
|
assertEquals("MockNetworkAgents can only be connected once",
|
||||||
|
mNetworkInfo.getDetailedState(), DetailedState.IDLE);
|
||||||
assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
|
assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
|
||||||
|
|
||||||
NetworkCallback callback = null;
|
NetworkCallback callback = null;
|
||||||
@@ -548,6 +550,11 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
super(context, handler, cmdName, cmd);
|
super(context, handler, cmdName, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd,
|
||||||
|
int arg1, int arg2, Object obj) {
|
||||||
|
super(context, handler, cmdName, cmd, arg1, arg2, obj);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void schedule(long when) {
|
public void schedule(long when) {
|
||||||
long delayMs = when - SystemClock.elapsedRealtime();
|
long delayMs = when - SystemClock.elapsedRealtime();
|
||||||
@@ -556,12 +563,13 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
fail("Attempting to send msg more than " + UNREASONABLY_LONG_WAIT +
|
fail("Attempting to send msg more than " + UNREASONABLY_LONG_WAIT +
|
||||||
"ms into the future: " + delayMs);
|
"ms into the future: " + delayMs);
|
||||||
}
|
}
|
||||||
mHandler.sendEmptyMessageDelayed(mCmd, delayMs);
|
Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
|
||||||
|
mHandler.sendMessageDelayed(msg, delayMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
mHandler.removeMessages(mCmd);
|
mHandler.removeMessages(mCmd, mObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -585,12 +593,6 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
protected CaptivePortalProbeResult isCaptivePortal() {
|
protected CaptivePortalProbeResult isCaptivePortal() {
|
||||||
return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl);
|
return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected WakeupMessage makeWakeupMessage(
|
|
||||||
Context context, Handler handler, String cmdName, int cmd) {
|
|
||||||
return new FakeWakeupMessage(context, handler, cmdName, cmd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class WrappedConnectivityService extends ConnectivityService {
|
private class WrappedConnectivityService extends ConnectivityService {
|
||||||
@@ -599,6 +601,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
|
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
|
||||||
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
|
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
|
||||||
super(context, netManager, statsService, policyManager);
|
super(context, netManager, statsService, policyManager);
|
||||||
|
mLingerDelayMs = TEST_LINGER_DELAY_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -642,6 +645,12 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
return monitor;
|
return monitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WakeupMessage makeWakeupMessage(
|
||||||
|
Context context, Handler handler, String cmdName, int cmd, Object obj) {
|
||||||
|
return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
|
||||||
|
}
|
||||||
|
|
||||||
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
|
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
|
||||||
return mLastCreatedNetworkMonitor;
|
return mLastCreatedNetworkMonitor;
|
||||||
}
|
}
|
||||||
@@ -686,8 +695,6 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
|
|
||||||
NetworkMonitor.SetDefaultLingerTime(120);
|
|
||||||
|
|
||||||
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
|
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
|
||||||
// http://b/25897652 .
|
// http://b/25897652 .
|
||||||
if (Looper.myLooper() == null) {
|
if (Looper.myLooper() == null) {
|
||||||
@@ -1051,42 +1058,58 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
private class CallbackInfo {
|
private class CallbackInfo {
|
||||||
public final CallbackState state;
|
public final CallbackState state;
|
||||||
public final Network network;
|
public final Network network;
|
||||||
public CallbackInfo(CallbackState s, Network n) { state = s; network = n; }
|
public Object arg;
|
||||||
|
public CallbackInfo(CallbackState s, Network n, Object o) {
|
||||||
|
state = s; network = n; arg = o;
|
||||||
|
}
|
||||||
public String toString() { return String.format("%s (%s)", state, network); }
|
public String toString() { return String.format("%s (%s)", state, network); }
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (!(o instanceof CallbackInfo)) return false;
|
if (!(o instanceof CallbackInfo)) return false;
|
||||||
|
// Ignore timeMs, since it's unpredictable.
|
||||||
CallbackInfo other = (CallbackInfo) o;
|
CallbackInfo other = (CallbackInfo) o;
|
||||||
return state == other.state && Objects.equals(network, other.network);
|
return state == other.state && Objects.equals(network, other.network);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
|
private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
private void setLastCallback(CallbackState state, Network network) {
|
private void setLastCallback(CallbackState state, Network network, Object o) {
|
||||||
mCallbacks.offer(new CallbackInfo(state, network));
|
mCallbacks.offer(new CallbackInfo(state, network, o));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onAvailable(Network network) {
|
public void onAvailable(Network network) {
|
||||||
setLastCallback(CallbackState.AVAILABLE, network);
|
setLastCallback(CallbackState.AVAILABLE, network, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLosing(Network network, int maxMsToLive) {
|
public void onLosing(Network network, int maxMsToLive) {
|
||||||
setLastCallback(CallbackState.LOSING, network);
|
setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLost(Network network) {
|
public void onLost(Network network) {
|
||||||
setLastCallback(CallbackState.LOST, network);
|
setLastCallback(CallbackState.LOST, network, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
void expectCallback(CallbackState state, MockNetworkAgent mockAgent, int timeoutMs) {
|
||||||
|
CallbackInfo expected = new CallbackInfo(
|
||||||
|
state, (mockAgent != null) ? mockAgent.getNetwork() : null, 0);
|
||||||
|
CallbackInfo actual;
|
||||||
|
try {
|
||||||
|
actual = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
|
||||||
|
assertEquals("Unexpected callback:", expected, actual);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
fail("Did not receive expected " + expected + " after " + TIMEOUT_MS + "ms");
|
||||||
|
actual = null; // Or the compiler can't tell it's never used uninitialized.
|
||||||
|
}
|
||||||
|
if (state == CallbackState.LOSING) {
|
||||||
|
String msg = String.format(
|
||||||
|
"Invalid linger time value %d, must be between %d and %d",
|
||||||
|
actual.arg, 0, TEST_LINGER_DELAY_MS);
|
||||||
|
int maxMsToLive = (Integer) actual.arg;
|
||||||
|
assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= TEST_LINGER_DELAY_MS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void expectCallback(CallbackState state, MockNetworkAgent mockAgent) {
|
void expectCallback(CallbackState state, MockNetworkAgent mockAgent) {
|
||||||
CallbackInfo expected = new CallbackInfo(
|
expectCallback(state, mockAgent, TIMEOUT_MS);
|
||||||
state,
|
|
||||||
(mockAgent != null) ? mockAgent.getNetwork() : null);
|
|
||||||
try {
|
|
||||||
assertEquals("Unexpected callback:",
|
|
||||||
expected, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
fail("Did not receive expected " + expected + " after " + TIMEOUT_MS + "ms");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void assertNoCallback() {
|
void assertNoCallback() {
|
||||||
@@ -1249,6 +1272,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
callback.expectCallback(CallbackState.LOSING, oldNetwork);
|
callback.expectCallback(CallbackState.LOSING, oldNetwork);
|
||||||
|
// TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
|
||||||
|
// longer lingering?
|
||||||
defaultCallback.expectCallback(CallbackState.AVAILABLE, newNetwork);
|
defaultCallback.expectCallback(CallbackState.AVAILABLE, newNetwork);
|
||||||
assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork());
|
assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork());
|
||||||
}
|
}
|
||||||
@@ -1306,8 +1331,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||||
mWiFiNetworkAgent.adjustScore(50);
|
mWiFiNetworkAgent.adjustScore(50);
|
||||||
mWiFiNetworkAgent.connect(false); // Score: 70
|
mWiFiNetworkAgent.connect(false); // Score: 70
|
||||||
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
|
|
||||||
callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
|
callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
|
||||||
|
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
|
||||||
defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
|
defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
|
||||||
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
|
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
|
||||||
|
|
||||||
@@ -1318,24 +1343,24 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
|
defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
|
||||||
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
|
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
|
||||||
|
|
||||||
// Bring up wifi, then validate it. In this case we do not linger cell. What happens is that
|
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
|
||||||
// when wifi connects, we don't linger because cell could potentially become the default
|
// it's arguably correct to linger it, since it was the default network before it validated.
|
||||||
// network if it validated. Then, when wifi validates, we re-evaluate cell, see it has no
|
|
||||||
// requests, and tear it down because it's unneeded.
|
|
||||||
// TODO: can we linger in this case?
|
|
||||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||||
mWiFiNetworkAgent.connect(true);
|
mWiFiNetworkAgent.connect(true);
|
||||||
callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
|
callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
|
||||||
callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
|
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
|
||||||
defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
|
defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
|
||||||
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
|
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
|
||||||
|
|
||||||
mWiFiNetworkAgent.disconnect();
|
mWiFiNetworkAgent.disconnect();
|
||||||
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
|
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
|
||||||
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
|
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
|
||||||
|
defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
|
||||||
|
mCellNetworkAgent.disconnect();
|
||||||
|
callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
|
||||||
|
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
|
||||||
|
|
||||||
// The current code has a bug: if a network is lingering, and we add and then remove a
|
// If a network is lingering, and we add and remove a request from it, resume lingering.
|
||||||
// request from it, we forget that the network was lingering and tear it down immediately.
|
|
||||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||||
mCellNetworkAgent.connect(true);
|
mCellNetworkAgent.connect(true);
|
||||||
callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
|
callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
|
||||||
@@ -1350,11 +1375,43 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
|||||||
.addTransportType(TRANSPORT_CELLULAR).build();
|
.addTransportType(TRANSPORT_CELLULAR).build();
|
||||||
NetworkCallback noopCallback = new NetworkCallback();
|
NetworkCallback noopCallback = new NetworkCallback();
|
||||||
mCm.requestNetwork(cellRequest, noopCallback);
|
mCm.requestNetwork(cellRequest, noopCallback);
|
||||||
|
// TODO: should this cause an AVAILABLE callback, to indicate that the network is no longer
|
||||||
|
// lingering?
|
||||||
mCm.unregisterNetworkCallback(noopCallback);
|
mCm.unregisterNetworkCallback(noopCallback);
|
||||||
callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
|
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
|
||||||
|
|
||||||
|
// Similar to the above: lingering can start even after the lingered request is removed.
|
||||||
|
// Disconnect wifi and switch to cell.
|
||||||
mWiFiNetworkAgent.disconnect();
|
mWiFiNetworkAgent.disconnect();
|
||||||
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
|
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
|
||||||
|
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
|
||||||
|
defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
|
||||||
|
|
||||||
|
// Cell is now the default network. Pin it with a cell-specific request.
|
||||||
|
noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525
|
||||||
|
mCm.requestNetwork(cellRequest, noopCallback);
|
||||||
|
|
||||||
|
// Now connect wifi, and expect it to become the default network.
|
||||||
|
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||||
|
mWiFiNetworkAgent.connect(true);
|
||||||
|
callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
|
||||||
|
defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
|
||||||
|
// The default request is lingering on cell, but nothing happens to cell, and we send no
|
||||||
|
// callbacks for it, because it's kept up by cellRequest.
|
||||||
|
callback.assertNoCallback();
|
||||||
|
// Now unregister cellRequest and expect cell to start lingering.
|
||||||
|
mCm.unregisterNetworkCallback(noopCallback);
|
||||||
|
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
|
||||||
|
|
||||||
|
// Let linger run its course.
|
||||||
|
callback.assertNoCallback();
|
||||||
|
callback.expectCallback(CallbackState.LOST, mCellNetworkAgent,
|
||||||
|
TEST_LINGER_DELAY_MS /* timeoutMs */);
|
||||||
|
|
||||||
|
// Clean up.
|
||||||
|
mWiFiNetworkAgent.disconnect();
|
||||||
|
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
|
||||||
|
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
|
||||||
|
|
||||||
mCm.unregisterNetworkCallback(callback);
|
mCm.unregisterNetworkCallback(callback);
|
||||||
mCm.unregisterNetworkCallback(defaultCallback);
|
mCm.unregisterNetworkCallback(defaultCallback);
|
||||||
|
|||||||
Reference in New Issue
Block a user