Merge "Move captive portal detection to another thread"
am: 9514b362cd
Change-Id: Ibd8ebcae32c906717ffbf52bf874556db86cc792
This commit is contained in:
@@ -227,6 +227,12 @@ public class NetworkMonitor extends StateMachine {
|
||||
public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14;
|
||||
private static final int CMD_EVALUATE_PRIVATE_DNS = BASE + 15;
|
||||
|
||||
/**
|
||||
* Message to self indicating captive portal detection is completed.
|
||||
* obj = CaptivePortalProbeResult for detection result;
|
||||
*/
|
||||
public static final int CMD_PROBE_COMPLETE = BASE + 16;
|
||||
|
||||
// Start mReevaluateDelayMs at this value and double.
|
||||
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
|
||||
private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
|
||||
@@ -290,6 +296,7 @@ public class NetworkMonitor extends StateMachine {
|
||||
private final State mEvaluatingState = new EvaluatingState();
|
||||
private final State mCaptivePortalState = new CaptivePortalState();
|
||||
private final State mEvaluatingPrivateDnsState = new EvaluatingPrivateDnsState();
|
||||
private final State mProbingState = new ProbingState();
|
||||
|
||||
private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
|
||||
|
||||
@@ -304,6 +311,9 @@ public class NetworkMonitor extends StateMachine {
|
||||
private final Random mRandom;
|
||||
private int mNextFallbackUrlIndex = 0;
|
||||
|
||||
private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
|
||||
private int mEvaluateAttempts = 0;
|
||||
|
||||
public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
|
||||
NetworkRequest defaultRequest) {
|
||||
this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog(),
|
||||
@@ -334,6 +344,7 @@ public class NetworkMonitor extends StateMachine {
|
||||
addState(mDefaultState);
|
||||
addState(mMaybeNotifyState, mDefaultState);
|
||||
addState(mEvaluatingState, mMaybeNotifyState);
|
||||
addState(mProbingState, mEvaluatingState);
|
||||
addState(mCaptivePortalState, mMaybeNotifyState);
|
||||
addState(mEvaluatingPrivateDnsState, mDefaultState);
|
||||
addState(mValidatedState, mDefaultState);
|
||||
@@ -582,9 +593,6 @@ public class NetworkMonitor extends StateMachine {
|
||||
// Being in the EvaluatingState State indicates the Network is being evaluated for internet
|
||||
// connectivity, or that the user has indicated that this network is unwanted.
|
||||
private class EvaluatingState extends State {
|
||||
private int mReevaluateDelayMs;
|
||||
private int mAttempts;
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
// If we have already started to track time spent in EvaluatingState
|
||||
@@ -599,7 +607,7 @@ public class NetworkMonitor extends StateMachine {
|
||||
mUidResponsibleForReeval = INVALID_UID;
|
||||
}
|
||||
mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
|
||||
mAttempts = 0;
|
||||
mEvaluateAttempts = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -630,42 +638,15 @@ public class NetworkMonitor extends StateMachine {
|
||||
transitionTo(mValidatedState);
|
||||
return HANDLED;
|
||||
}
|
||||
mAttempts++;
|
||||
// Note: This call to isCaptivePortal() could take up to a minute. Resolving the
|
||||
// server's IP addresses could hit the DNS timeout, and attempting connections
|
||||
// to each of the server's several IP addresses (currently one IPv4 and one
|
||||
// IPv6) could each take SOCKET_TIMEOUT_MS. During this time this StateMachine
|
||||
// will be unresponsive. isCaptivePortal() could be executed on another Thread
|
||||
// if this is found to cause problems.
|
||||
CaptivePortalProbeResult probeResult = isCaptivePortal();
|
||||
if (probeResult.isSuccessful()) {
|
||||
// Transit EvaluatingPrivateDnsState to get to Validated
|
||||
// state (even if no Private DNS validation required).
|
||||
transitionTo(mEvaluatingPrivateDnsState);
|
||||
} else if (probeResult.isPortal()) {
|
||||
notifyNetworkTestResultInvalid(probeResult.redirectUrl);
|
||||
mLastPortalProbeResult = probeResult;
|
||||
transitionTo(mCaptivePortalState);
|
||||
} else {
|
||||
final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
|
||||
sendMessageDelayed(msg, mReevaluateDelayMs);
|
||||
logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
|
||||
notifyNetworkTestResultInvalid(probeResult.redirectUrl);
|
||||
if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
|
||||
// Don't continue to blame UID forever.
|
||||
TrafficStats.clearThreadStatsUid();
|
||||
}
|
||||
mReevaluateDelayMs *= 2;
|
||||
if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
|
||||
mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
|
||||
}
|
||||
}
|
||||
mEvaluateAttempts++;
|
||||
|
||||
transitionTo(mProbingState);
|
||||
return HANDLED;
|
||||
case CMD_FORCE_REEVALUATION:
|
||||
// Before IGNORE_REEVALUATE_ATTEMPTS attempts are made,
|
||||
// ignore any re-evaluation requests. After, restart the
|
||||
// evaluation process via EvaluatingState#enter.
|
||||
return (mAttempts < IGNORE_REEVALUATE_ATTEMPTS) ? HANDLED : NOT_HANDLED;
|
||||
return (mEvaluateAttempts < IGNORE_REEVALUATE_ATTEMPTS) ? HANDLED : NOT_HANDLED;
|
||||
default:
|
||||
return NOT_HANDLED;
|
||||
}
|
||||
@@ -852,6 +833,76 @@ public class NetworkMonitor extends StateMachine {
|
||||
}
|
||||
}
|
||||
|
||||
private class ProbingState extends State {
|
||||
private Thread mThread;
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE,
|
||||
isCaptivePortal())));
|
||||
mThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case CMD_PROBE_COMPLETE:
|
||||
// Currently, it's not possible to exit this state without mThread having
|
||||
// terminated. Therefore, this state can never get CMD_PROBE_COMPLETE from a
|
||||
// stale thread that is not mThread.
|
||||
// TODO: As soon as it's possible to exit this state without mThread having
|
||||
// terminated, ensure that CMD_PROBE_COMPLETE from stale threads are ignored.
|
||||
// This could be done via a sequence number, or by changing mThread to a class
|
||||
// that has a stopped volatile boolean or AtomicBoolean.
|
||||
final CaptivePortalProbeResult probeResult =
|
||||
(CaptivePortalProbeResult) message.obj;
|
||||
|
||||
if (probeResult.isSuccessful()) {
|
||||
// Transit EvaluatingPrivateDnsState to get to Validated
|
||||
// state (even if no Private DNS validation required).
|
||||
transitionTo(mEvaluatingPrivateDnsState);
|
||||
} else if (probeResult.isPortal()) {
|
||||
notifyNetworkTestResultInvalid(probeResult.redirectUrl);
|
||||
mLastPortalProbeResult = probeResult;
|
||||
transitionTo(mCaptivePortalState);
|
||||
} else {
|
||||
final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
|
||||
sendMessageDelayed(msg, mReevaluateDelayMs);
|
||||
logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
|
||||
notifyNetworkTestResultInvalid(probeResult.redirectUrl);
|
||||
if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
|
||||
// Don't continue to blame UID forever.
|
||||
TrafficStats.clearThreadStatsUid();
|
||||
}
|
||||
mReevaluateDelayMs *= 2;
|
||||
if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
|
||||
mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
|
||||
}
|
||||
}
|
||||
return HANDLED;
|
||||
case CMD_REEVALUATE:
|
||||
// Leave the event to EvaluatingState. Defer this message will result in reset
|
||||
// of mReevaluateDelayMs and mEvaluateAttempts.
|
||||
return NOT_HANDLED;
|
||||
default:
|
||||
// TODO: Some events may able to handle in this state, instead of deferring to
|
||||
// next state.
|
||||
deferMessage(message);
|
||||
return HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit() {
|
||||
// If StateMachine get here, the probe started in enter() is guaranteed to have
|
||||
// completed, because in this state, all messages except CMD_PROBE_COMPLETE and
|
||||
// CMD_REEVALUATE are deferred. CMD_REEVALUATE cannot be in the queue, because it is
|
||||
// only ever sent in EvaluatingState#enter, and the StateMachine reach this state by
|
||||
// processing it. Therefore, there is no need to stop the thread.
|
||||
mThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Limits the list of IP addresses returned by getAllByName or tried by openConnection to at
|
||||
// most one per address family. This ensures we only wait up to 20 seconds for TCP connections
|
||||
// to complete, regardless of how many IP addresses a host has.
|
||||
@@ -1031,10 +1082,10 @@ public class NetworkMonitor extends StateMachine {
|
||||
result.isPortal() /* isCaptivePortal */,
|
||||
startTime, endTime);
|
||||
|
||||
log("isCaptivePortal: isSuccessful()=" + result.isSuccessful() +
|
||||
" isPortal()=" + result.isPortal() +
|
||||
" RedirectUrl=" + result.redirectUrl +
|
||||
" StartTime=" + startTime + " EndTime=" + endTime);
|
||||
log("isCaptivePortal: isSuccessful()=" + result.isSuccessful()
|
||||
+ " isPortal()=" + result.isPortal()
|
||||
+ " RedirectUrl=" + result.redirectUrl
|
||||
+ " Time=" + (endTime - startTime) + "ms");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user