Merge "IpManager: add a provisioning timeout option" into nyc-dev

This commit is contained in:
Erik Kline
2016-04-27 09:40:06 +00:00
committed by Android (Google) Code Review
2 changed files with 83 additions and 57 deletions

View File

@@ -105,26 +105,25 @@ public class DhcpClient extends StateMachine {
/* Commands from controller to start/stop DHCP */ /* Commands from controller to start/stop DHCP */
public static final int CMD_START_DHCP = PUBLIC_BASE + 1; public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2; public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2;
public static final int CMD_RENEW_DHCP = PUBLIC_BASE + 3;
/* Notification from DHCP state machine prior to DHCP discovery/renewal */ /* Notification from DHCP state machine prior to DHCP discovery/renewal */
public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 4; public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 3;
/* Notification from DHCP state machine post DHCP discovery/renewal. Indicates /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
* success/failure */ * success/failure */
public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 5; public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 4;
/* Notification from DHCP state machine before quitting */ /* Notification from DHCP state machine before quitting */
public static final int CMD_ON_QUIT = PUBLIC_BASE + 6; public static final int CMD_ON_QUIT = PUBLIC_BASE + 5;
/* Command from controller to indicate DHCP discovery/renewal can continue /* Command from controller to indicate DHCP discovery/renewal can continue
* after pre DHCP action is complete */ * after pre DHCP action is complete */
public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 7; public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 6;
/* Command and event notification to/from IpManager requesting the setting /* Command and event notification to/from IpManager requesting the setting
* (or clearing) of an IPv4 LinkAddress. * (or clearing) of an IPv4 LinkAddress.
*/ */
public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 8; public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 7;
public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 9; public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 8;
public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 10; public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 9;
/* Message.arg1 arguments to CMD_POST_DHCP notification */ /* Message.arg1 arguments to CMD_POST_DHCP notification */
public static final int DHCP_SUCCESS = 1; public static final int DHCP_SUCCESS = 1;
@@ -135,7 +134,7 @@ public class DhcpClient extends StateMachine {
private static final int CMD_KICK = PRIVATE_BASE + 1; private static final int CMD_KICK = PRIVATE_BASE + 1;
private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2; private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
private static final int CMD_TIMEOUT = PRIVATE_BASE + 3; private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
private static final int CMD_ONESHOT_TIMEOUT = PRIVATE_BASE + 4; private static final int CMD_RENEW_DHCP = PRIVATE_BASE + 4;
// For message logging. // For message logging.
private static final Class[] sMessageClasses = { DhcpClient.class }; private static final Class[] sMessageClasses = { DhcpClient.class };
@@ -177,7 +176,6 @@ public class DhcpClient extends StateMachine {
private final WakeupMessage mKickAlarm; private final WakeupMessage mKickAlarm;
private final WakeupMessage mTimeoutAlarm; private final WakeupMessage mTimeoutAlarm;
private final WakeupMessage mRenewAlarm; private final WakeupMessage mRenewAlarm;
private final WakeupMessage mOneshotTimeoutAlarm;
private final String mIfaceName; private final String mIfaceName;
private boolean mRegisteredForPreDhcpNotification; private boolean mRegisteredForPreDhcpNotification;
@@ -243,10 +241,6 @@ public class DhcpClient extends StateMachine {
mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT); mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
// Used to schedule DHCP renews. // Used to schedule DHCP renews.
mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP); mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
// Used to tell the caller when its request (CMD_START_DHCP or CMD_RENEW_DHCP) timed out.
// TODO: when the legacy DHCP client is gone, make the client fully asynchronous and
// remove this.
mOneshotTimeoutAlarm = makeWakeupMessage("ONESHOT_TIMEOUT", CMD_ONESHOT_TIMEOUT);
} }
public void registerForPreDhcpNotification() { public void registerForPreDhcpNotification() {
@@ -506,29 +500,12 @@ public class DhcpClient extends StateMachine {
} }
} }
// The one-shot timeout is used to implement the timeout for CMD_START_DHCP. We can't use a
// state timeout to do this because obtaining an IP address involves passing through more than
// one state (specifically, it passes at least once through DhcpInitState and once through
// DhcpRequestingState). The one-shot timeout is created when CMD_START_DHCP is received, and is
// cancelled when exiting DhcpState (either due to a CMD_STOP_DHCP, or because of an error), or
// when we get an IP address (when entering DhcpBoundState). If it fires, we send ourselves
// CMD_ONESHOT_TIMEOUT and notify the caller that DHCP failed, but we take no other action. For
// example, if we're in DhcpInitState and sending DISCOVERs, we continue to do so.
//
// The one-shot timeout is not used for CMD_RENEW_DHCP because that is implemented using only
// one state, so we can just use the state timeout.
private void scheduleOneshotTimeout() {
final long alarmTime = SystemClock.elapsedRealtime() + DHCP_TIMEOUT_MS;
mOneshotTimeoutAlarm.schedule(alarmTime);
}
class StoppedState extends LoggingState { class StoppedState extends LoggingState {
@Override @Override
public boolean processMessage(Message message) { public boolean processMessage(Message message) {
super.processMessage(message); super.processMessage(message);
switch (message.what) { switch (message.what) {
case CMD_START_DHCP: case CMD_START_DHCP:
scheduleOneshotTimeout();
if (mRegisteredForPreDhcpNotification) { if (mRegisteredForPreDhcpNotification) {
transitionTo(mWaitBeforeStartState); transitionTo(mWaitBeforeStartState);
} else { } else {
@@ -571,7 +548,6 @@ public class DhcpClient extends StateMachine {
@Override @Override
public void exit() { public void exit() {
mOneshotTimeoutAlarm.cancel();
if (mReceiveThread != null) { if (mReceiveThread != null) {
mReceiveThread.halt(); // Also closes sockets. mReceiveThread.halt(); // Also closes sockets.
mReceiveThread = null; mReceiveThread = null;
@@ -586,10 +562,6 @@ public class DhcpClient extends StateMachine {
case CMD_STOP_DHCP: case CMD_STOP_DHCP:
transitionTo(mStoppedState); transitionTo(mStoppedState);
return HANDLED; return HANDLED;
case CMD_ONESHOT_TIMEOUT:
if (DBG) Log.d(TAG, "Timed out");
notifyFailure();
return HANDLED;
default: default:
return NOT_HANDLED; return NOT_HANDLED;
} }
@@ -822,7 +794,6 @@ public class DhcpClient extends StateMachine {
@Override @Override
public void enter() { public void enter() {
super.enter(); super.enter();
mOneshotTimeoutAlarm.cancel();
notifySuccess(); notifySuccess();
// TODO: DhcpStateMachine only supported renewing at 50% of the lease time, // TODO: DhcpStateMachine only supported renewing at 50% of the lease time,
// and did not support rebinding. Now that the legacy DHCP client is gone, fix this. // and did not support rebinding. Now that the legacy DHCP client is gone, fix this.
@@ -888,7 +859,7 @@ public class DhcpClient extends StateMachine {
@Override @Override
protected void timeout() { protected void timeout() {
transitionTo(mDhcpInitState); transitionTo(mDhcpInitState);
sendMessage(CMD_ONESHOT_TIMEOUT); notifyFailure();
} }
} }

View File

@@ -17,6 +17,7 @@
package android.net.ip; package android.net.ip;
import com.android.internal.util.MessageUtils; import com.android.internal.util.MessageUtils;
import com.android.internal.util.WakeupMessage;
import android.content.Context; import android.content.Context;
import android.net.apf.ApfCapabilities; import android.net.apf.ApfCapabilities;
@@ -53,6 +54,7 @@ import java.net.InetAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
import java.net.SocketException; import java.net.SocketException;
import java.util.Objects; import java.util.Objects;
import java.util.StringJoiner;
/** /**
@@ -250,6 +252,7 @@ public class IpManager extends StateMachine {
* final ProvisioningConfiguration config = * final ProvisioningConfiguration config =
* mIpManager.buildProvisioningConfiguration() * mIpManager.buildProvisioningConfiguration()
* .withPreDhcpAction() * .withPreDhcpAction()
* .withProvisioningTimeoutMs(36 * 1000)
* .build(); * .build();
* mIpManager.startProvisioning(config); * mIpManager.startProvisioning(config);
* ... * ...
@@ -260,6 +263,15 @@ public class IpManager extends StateMachine {
* must specify the configuration again. * must specify the configuration again.
*/ */
public static class ProvisioningConfiguration { public static class ProvisioningConfiguration {
// TODO: Delete this default timeout once those callers that care are
// fixed to pass in their preferred timeout.
//
// We pick 36 seconds so we can send DHCP requests at
//
// t=0, t=2, t=6, t=14, t=30
//
// allowing for 10% jitter.
private static final int DEFAULT_TIMEOUT_MS = 36 * 1000;
public static class Builder { public static class Builder {
private ProvisioningConfiguration mConfig = new ProvisioningConfiguration(); private ProvisioningConfiguration mConfig = new ProvisioningConfiguration();
@@ -284,6 +296,11 @@ public class IpManager extends StateMachine {
return this; return this;
} }
public Builder withProvisioningTimeoutMs(int timeoutMs) {
mConfig.mProvisioningTimeoutMs = timeoutMs;
return this;
}
public ProvisioningConfiguration build() { public ProvisioningConfiguration build() {
return new ProvisioningConfiguration(mConfig); return new ProvisioningConfiguration(mConfig);
} }
@@ -293,6 +310,7 @@ public class IpManager extends StateMachine {
/* package */ boolean mRequestedPreDhcpAction; /* package */ boolean mRequestedPreDhcpAction;
/* package */ StaticIpConfiguration mStaticIpConfig; /* package */ StaticIpConfiguration mStaticIpConfig;
/* package */ ApfCapabilities mApfCapabilities; /* package */ ApfCapabilities mApfCapabilities;
/* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
public ProvisioningConfiguration() {} public ProvisioningConfiguration() {}
@@ -301,6 +319,18 @@ public class IpManager extends StateMachine {
mRequestedPreDhcpAction = other.mRequestedPreDhcpAction; mRequestedPreDhcpAction = other.mRequestedPreDhcpAction;
mStaticIpConfig = other.mStaticIpConfig; mStaticIpConfig = other.mStaticIpConfig;
mApfCapabilities = other.mApfCapabilities; mApfCapabilities = other.mApfCapabilities;
mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
}
@Override
public String toString() {
return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
.add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
.add("mRequestedPreDhcpAction: " + mRequestedPreDhcpAction)
.add("mStaticIpConfig: " + mStaticIpConfig)
.add("mApfCapabilities: " + mApfCapabilities)
.add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
.toString();
} }
} }
@@ -315,6 +345,7 @@ public class IpManager extends StateMachine {
private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 6; private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 6;
private static final int CMD_UPDATE_HTTP_PROXY = 7; private static final int CMD_UPDATE_HTTP_PROXY = 7;
private static final int CMD_SET_MULTICAST_FILTER = 8; private static final int CMD_SET_MULTICAST_FILTER = 8;
private static final int EVENT_PROVISIONING_TIMEOUT = 9;
private static final int MAX_LOG_RECORDS = 500; private static final int MAX_LOG_RECORDS = 500;
@@ -337,6 +368,7 @@ public class IpManager extends StateMachine {
protected final Callback mCallback; protected final Callback mCallback;
private final INetworkManagementService mNwService; private final INetworkManagementService mNwService;
private final NetlinkTracker mNetlinkTracker; private final NetlinkTracker mNetlinkTracker;
private final WakeupMessage mProvisioningTimeoutAlarm;
private final LocalLog mLocalLog; private final LocalLog mLocalLog;
private NetworkInterface mNetworkInterface; private NetworkInterface mNetworkInterface;
@@ -411,6 +443,9 @@ public class IpManager extends StateMachine {
resetLinkProperties(); resetLinkProperties();
mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
// Super simple StateMachine. // Super simple StateMachine.
addState(mStoppedState); addState(mStoppedState);
addState(mStartedState); addState(mStartedState);
@@ -649,7 +684,6 @@ public class IpManager extends StateMachine {
} }
private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) { private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
if (mApfFilter != null) mApfFilter.setLinkProperties(newLp);
switch (delta) { switch (delta) {
case GAINED_PROVISIONING: case GAINED_PROVISIONING:
if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); } if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
@@ -670,7 +704,13 @@ public class IpManager extends StateMachine {
} }
} }
// Updates all IpManager-related state concerned with LinkProperties.
// Returns a ProvisioningChange for possibly notifying other interested
// parties that are not fronted by IpManager.
private ProvisioningChange setLinkProperties(LinkProperties newLp) { private ProvisioningChange setLinkProperties(LinkProperties newLp) {
if (mApfFilter != null) {
mApfFilter.setLinkProperties(newLp);
}
if (mIpReachabilityMonitor != null) { if (mIpReachabilityMonitor != null) {
mIpReachabilityMonitor.updateLinkProperties(newLp); mIpReachabilityMonitor.updateLinkProperties(newLp);
} }
@@ -678,13 +718,10 @@ public class IpManager extends StateMachine {
ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp); ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp);
mLinkProperties = new LinkProperties(newLp); mLinkProperties = new LinkProperties(newLp);
if (DBG) { if (delta == ProvisioningChange.GAINED_PROVISIONING) {
switch (delta) { // TODO: Add a proper ProvisionedState and cancel the alarm in
case GAINED_PROVISIONING: // its enter() method.
case LOST_PROVISIONING: mProvisioningTimeoutAlarm.cancel();
Log.d(mTag, "provisioning: " + delta);
break;
}
} }
return delta; return delta;
@@ -798,33 +835,39 @@ public class IpManager extends StateMachine {
Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
} }
mCallback.onNewDhcpResults(dhcpResults); mCallback.onNewDhcpResults(dhcpResults);
dispatchCallback(delta, newLp); dispatchCallback(delta, newLp);
} }
private void handleIPv4Failure() { private void handleIPv4Failure() {
// TODO: Investigate deleting this clearIPv4Address() call.
//
// DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
// that could trigger a call to this function. If we missed handling
// that message in StartedState for some reason we would still clear
// any addresses upon entry to StoppedState.
clearIPv4Address(); clearIPv4Address();
mDhcpResults = null; mDhcpResults = null;
if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
mCallback.onNewDhcpResults(null);
handleProvisioningFailure();
}
private void handleProvisioningFailure() {
final LinkProperties newLp = assembleLinkProperties(); final LinkProperties newLp = assembleLinkProperties();
ProvisioningChange delta = setLinkProperties(newLp); ProvisioningChange delta = setLinkProperties(newLp);
// If we've gotten here and we're still not provisioned treat that as // If we've gotten here and we're still not provisioned treat that as
// a total loss of provisioning. // a total loss of provisioning.
// //
// Either (a) static IP configuration failed or (b) DHCPv4 failed AND // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
// there was no usable IPv6 obtained before the DHCPv4 timeout. // there was no usable IPv6 obtained before a non-zero provisioning
// timeout expired.
// //
// Regardless: GAME OVER. // Regardless: GAME OVER.
//
// TODO: Make the DHCP client not time out and just continue in
// exponential backoff. Callers such as Wi-Fi which need a timeout
// should implement it themselves.
if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) { if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
delta = ProvisioningChange.LOST_PROVISIONING; delta = ProvisioningChange.LOST_PROVISIONING;
} }
if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
mCallback.onNewDhcpResults(null);
dispatchCallback(delta, newLp); dispatchCallback(delta, newLp);
if (delta == ProvisioningChange.LOST_PROVISIONING) { if (delta == ProvisioningChange.LOST_PROVISIONING) {
transitionTo(mStoppingState); transitionTo(mStoppingState);
@@ -968,11 +1011,19 @@ public class IpManager extends StateMachine {
mInterfaceName); mInterfaceName);
mDhcpClient.registerForPreDhcpNotification(); mDhcpClient.registerForPreDhcpNotification();
mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
if (mConfiguration.mProvisioningTimeoutMs > 0) {
final long alarmTime = SystemClock.elapsedRealtime() +
mConfiguration.mProvisioningTimeoutMs;
mProvisioningTimeoutAlarm.schedule(alarmTime);
}
} }
} }
@Override @Override
public void exit() { public void exit() {
mProvisioningTimeoutAlarm.cancel();
if (mIpReachabilityMonitor != null) { if (mIpReachabilityMonitor != null) {
mIpReachabilityMonitor.stop(); mIpReachabilityMonitor.stop();
mIpReachabilityMonitor = null; mIpReachabilityMonitor = null;
@@ -995,7 +1046,7 @@ public class IpManager extends StateMachine {
public boolean processMessage(Message msg) { public boolean processMessage(Message msg) {
switch (msg.what) { switch (msg.what) {
case CMD_STOP: case CMD_STOP:
transitionTo(mStoppedState); transitionTo(mStoppingState);
break; break;
case CMD_START: case CMD_START:
@@ -1023,7 +1074,7 @@ public class IpManager extends StateMachine {
case EVENT_NETLINK_LINKPROPERTIES_CHANGED: case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) { if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
transitionTo(mStoppedState); transitionTo(mStoppingState);
} }
break; break;
@@ -1049,6 +1100,10 @@ public class IpManager extends StateMachine {
break; break;
} }
case EVENT_PROVISIONING_TIMEOUT:
handleProvisioningFailure();
break;
case DhcpClient.CMD_PRE_DHCP_ACTION: case DhcpClient.CMD_PRE_DHCP_ACTION:
if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); } if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); }
if (mConfiguration.mRequestedPreDhcpAction) { if (mConfiguration.mRequestedPreDhcpAction) {