diff options
author | 2016-02-10 21:15:11 +0000 | |
---|---|---|
committer | 2016-02-10 21:15:11 +0000 | |
commit | f94dc18746d93e98d2e48cdd25b799aa20a142ab (patch) | |
tree | 35400c3839b85e59a6123431594b26cc076723a9 | |
parent | d9c4bc0c4db7a8b16d8ea0f58ed0615efc167f47 (diff) | |
parent | 990fc4968d58574f4280194e67cdb6e6ecb69699 (diff) |
Merge "Incorporate historical WifiStateMachine notions of provisioning." into mm-wireless-dev
-rw-r--r-- | services/net/java/android/net/ip/IpManager.java | 243 |
1 files changed, 199 insertions, 44 deletions
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index 06b6ee75da7a..027c3254396f 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -92,13 +92,18 @@ public class IpManager extends StateMachine { */ // Implementations must call IpManager#completedPreDhcpAction(). + // TODO: Remove this requirement, perhaps via some + // registerForPreDhcpAction()-style mechanism. public void onPreDhcpAction() {} public void onPostDhcpAction() {} - // TODO: Kill with fire once DHCP and static configuration are moved - // out of WifiStateMachine. - public void onIPv4ProvisioningSuccess(DhcpResults dhcpResults) {} - public void onIPv4ProvisioningFailure() {} + // This is purely advisory and not an indication of provisioning + // success or failure. This is only here for callers that want to + // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress). + // DHCPv4 or static IPv4 configuration failure or success can be + // determined by whether or not the passed-in DhcpResults object is + // null or not. + public void onNewDhcpResults(DhcpResults dhcpResults) {} public void onProvisioningSuccess(LinkProperties newLp) {} public void onProvisioningFailure(LinkProperties newLp) {} @@ -122,6 +127,7 @@ public class IpManager extends StateMachine { private final Object mLock = new Object(); private final State mStoppedState = new StoppedState(); + private final State mStoppingState = new StoppingState(); private final State mStartedState = new StartedState(); private final Context mContext; @@ -179,6 +185,8 @@ public class IpManager extends StateMachine { // Super simple StateMachine. addState(mStoppedState); addState(mStartedState); + addState(mStoppingState); + setInitialState(mStoppedState); setLogRecSize(MAX_LOG_RECORDS); super.start(); @@ -203,13 +211,11 @@ public class IpManager extends StateMachine { public void startProvisioning(StaticIpConfiguration staticIpConfig) { getInterfaceIndex(); - sendMessage(CMD_START, staticIpConfig); } public void startProvisioning() { getInterfaceIndex(); - sendMessage(CMD_START); } @@ -236,6 +242,42 @@ public class IpManager extends StateMachine { * Internals. */ + @Override + protected String getWhatToString(int what) { + // TODO: Investigate switching to reflection. + switch (what) { + case CMD_STOP: + return "CMD_STOP"; + case CMD_START: + return "CMD_START"; + case CMD_CONFIRM: + return "CMD_CONFIRM"; + case EVENT_PRE_DHCP_ACTION_COMPLETE: + return "EVENT_PRE_DHCP_ACTION_COMPLETE"; + case EVENT_NETLINK_LINKPROPERTIES_CHANGED: + return "EVENT_NETLINK_LINKPROPERTIES_CHANGED"; + case DhcpStateMachine.CMD_PRE_DHCP_ACTION: + return "DhcpStateMachine.CMD_PRE_DHCP_ACTION"; + case DhcpStateMachine.CMD_POST_DHCP_ACTION: + return "DhcpStateMachine.CMD_POST_DHCP_ACTION"; + case DhcpStateMachine.CMD_ON_QUIT: + return "DhcpStateMachine.CMD_ON_QUIT"; + } + return "UNKNOWN:" + Integer.toString(what); + } + + @Override + protected String getLogRecString(Message msg) { + final String logLine = String.format( + "iface{%s/%d} arg1{%d} arg2{%d} obj{%s}", + mInterfaceName, mInterfaceIndex, + msg.arg1, msg.arg2, Objects.toString(msg.obj)); + if (VDBG) { + Log.d(TAG, getWhatToString(msg.what) + " " + logLine); + } + return logLine; + } + private void getInterfaceIndex() { try { mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex(); @@ -260,16 +302,93 @@ public class IpManager extends StateMachine { } } + // For now: use WifiStateMachine's historical notion of provisioned. + private static boolean isProvisioned(LinkProperties lp) { + // For historical reasons, we should connect even if all we have is + // an IPv4 address and nothing else. + return lp.isProvisioned() || lp.hasIPv4Address(); + } + + // TODO: Investigate folding all this into the existing static function + // LinkProperties.compareProvisioning() or some other single function that + // takes two LinkProperties objects and returns a ProvisioningChange + // object that is a correct and complete assessment of what changed, taking + // account of the asymmetries described in the comments in this function. + // Then switch to using it everywhere (IpReachabilityMonitor, etc.). + private static ProvisioningChange compareProvisioning( + LinkProperties oldLp, LinkProperties newLp) { + ProvisioningChange delta; + + final boolean wasProvisioned = isProvisioned(oldLp); + final boolean isProvisioned = isProvisioned(newLp); + + if (!wasProvisioned && isProvisioned) { + delta = ProvisioningChange.GAINED_PROVISIONING; + } else if (wasProvisioned && isProvisioned) { + delta = ProvisioningChange.STILL_PROVISIONED; + } else if (!wasProvisioned && !isProvisioned) { + delta = ProvisioningChange.STILL_NOT_PROVISIONED; + } else { + // (wasProvisioned && !isProvisioned) + // + // Note that this is true even if we lose a configuration element + // (e.g., a default gateway) that would not be required to advance + // into provisioned state. This is intended: if we have a default + // router and we lose it, that's a sure sign of a problem, but if + // we connect to a network with no IPv4 DNS servers, we consider + // that to be a network without DNS servers and connect anyway. + // + // See the comment below. + delta = ProvisioningChange.LOST_PROVISIONING; + } + + // Additionally: + // + // Partial configurations (e.g., only an IPv4 address with no DNS + // servers and no default route) are accepted as long as DHCPv4 + // succeeds. On such a network, isProvisioned() will always return + // false, because the configuration is not complete, but we want to + // connect anyway. It might be a disconnected network such as a + // Chromecast or a wireless printer, for example. + // + // Because on such a network isProvisioned() will always return false, + // delta will never be LOST_PROVISIONING. So check for loss of + // provisioning here too. + if ((oldLp.hasIPv4Address() && !newLp.hasIPv4Address()) || + (oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned())) { + delta = ProvisioningChange.LOST_PROVISIONING; + } + + return delta; + } + + private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) { + switch (delta) { + case GAINED_PROVISIONING: + if (VDBG) { Log.d(TAG, "onProvisioningSuccess()"); } + mCallback.onProvisioningSuccess(newLp); + break; + + case LOST_PROVISIONING: + if (VDBG) { Log.d(TAG, "onProvisioningFailure()"); } + mCallback.onProvisioningFailure(newLp); + break; + + default: + if (VDBG) { Log.d(TAG, "onLinkPropertiesChange()"); } + mCallback.onLinkPropertiesChange(newLp); + break; + } + } + private ProvisioningChange setLinkProperties(LinkProperties newLp) { if (mIpReachabilityMonitor != null) { mIpReachabilityMonitor.updateLinkProperties(newLp); } - // TODO: Figure out whether and how to incorporate static configuration - // into the notion of provisioning. ProvisioningChange delta; synchronized (mLock) { - delta = LinkProperties.compareProvisioning(mLinkProperties, newLp); + delta = compareProvisioning(mLinkProperties, newLp); mLinkProperties = new LinkProperties(newLp); } @@ -351,15 +470,45 @@ public class IpManager extends StateMachine { private void handleIPv4Success(DhcpResults dhcpResults) { mDhcpResults = new DhcpResults(dhcpResults); - setLinkProperties(assembleLinkProperties()); - mCallback.onIPv4ProvisioningSuccess(dhcpResults); + final LinkProperties newLp = assembleLinkProperties(); + final ProvisioningChange delta = setLinkProperties(newLp); + + if (VDBG) { + Log.d(TAG, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); + } + mCallback.onNewDhcpResults(dhcpResults); + + dispatchCallback(delta, newLp); } private void handleIPv4Failure() { + // TODO: Figure out to de-dup this and the same code in DhcpClient. clearIPv4Address(); mDhcpResults = null; - setLinkProperties(assembleLinkProperties()); - mCallback.onIPv4ProvisioningFailure(); + final LinkProperties newLp = assembleLinkProperties(); + ProvisioningChange delta = setLinkProperties(newLp); + // If we've gotten here and we're still not provisioned treat that as + // a total loss of provisioning. + // + // Either (a) static IP configuration failed or (b) DHCPv4 failed AND + // there was no usable IPv6 obtained before the DHCPv4 timeout. + // + // 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) { + delta = ProvisioningChange.LOST_PROVISIONING; + } + + if (VDBG) { Log.d(TAG, "onNewDhcpResults(null)"); } + mCallback.onNewDhcpResults(null); + + dispatchCallback(delta, newLp); + if (delta == ProvisioningChange.LOST_PROVISIONING) { + transitionTo(mStoppingState); + } } class StoppedState extends State { @@ -391,13 +540,8 @@ public class IpManager extends StateMachine { break; case DhcpStateMachine.CMD_ON_QUIT: - // CMD_ON_QUIT is really more like "EVENT_ON_QUIT". - // Shutting down DHCPv4 progresses simultaneously with - // transitioning to StoppedState, so we can receive this - // message after we've already transitioned here. - // - // TODO: Figure out if this is actually useful and if not - // expunge it. + // Everything is already stopped. + Log.e(TAG, "Unexpected CMD_ON_QUIT (already stopped)."); break; default: @@ -407,6 +551,30 @@ public class IpManager extends StateMachine { } } + class StoppingState extends State { + @Override + public void enter() { + if (mDhcpStateMachine == null) { + // There's no DHCPv4 for which to wait; proceed to stopped. + transitionTo(mStoppedState); + } + } + + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case DhcpStateMachine.CMD_ON_QUIT: + mDhcpStateMachine = null; + transitionTo(mStoppedState); + break; + + default: + deferMessage(msg); + } + return HANDLED; + } + } + class StartedState extends State { @Override public void enter() { @@ -439,7 +607,9 @@ public class IpManager extends StateMachine { if (applyStaticIpConfig()) { handleIPv4Success(new DhcpResults(mStaticIpConfig)); } else { - handleIPv4Failure(); + if (VDBG) { Log.d(TAG, "onProvisioningFailure()"); } + mCallback.onProvisioningFailure(getLinkProperties()); + transitionTo(mStoppingState); } } else { // Start DHCPv4. @@ -457,7 +627,6 @@ public class IpManager extends StateMachine { if (mDhcpStateMachine != null) { mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP); mDhcpStateMachine.doQuit(); - mDhcpStateMachine = null; } resetLinkProperties(); @@ -500,28 +669,15 @@ public class IpManager extends StateMachine { break; } final ProvisioningChange delta = setLinkProperties(newLp); - - // NOTE: The only receiver of these callbacks currently - // treats all three of them identically, namely it calls - // IpManager#getLinkProperties() and makes its own determination. - switch (delta) { - case GAINED_PROVISIONING: - mCallback.onProvisioningSuccess(newLp); - break; - - case LOST_PROVISIONING: - mCallback.onProvisioningFailure(newLp); - break; - - default: - // TODO: Only notify on STILL_PROVISIONED? - mCallback.onLinkPropertiesChange(newLp); - break; + dispatchCallback(delta, newLp); + if (delta == ProvisioningChange.LOST_PROVISIONING) { + transitionTo(mStoppedState); } break; } case DhcpStateMachine.CMD_PRE_DHCP_ACTION: + if (VDBG) { Log.d(TAG, "onPreDhcpAction()"); } mCallback.onPreDhcpAction(); break; @@ -529,6 +685,7 @@ public class IpManager extends StateMachine { // Note that onPostDhcpAction() is likely to be // asynchronous, and thus there is no guarantee that we // will be able to observe any of its effects here. + if (VDBG) { Log.d(TAG, "onPostDhcpAction()"); } mCallback.onPostDhcpAction(); final DhcpResults dhcpResults = (DhcpResults) msg.obj; @@ -546,11 +703,9 @@ public class IpManager extends StateMachine { } case DhcpStateMachine.CMD_ON_QUIT: - // CMD_ON_QUIT is really more like "EVENT_ON_QUIT". - // Regardless, we ignore it. - // - // TODO: Figure out if this is actually useful and if not - // expunge it. + // DHCPv4 quit early for some reason. + Log.e(TAG, "Unexpected CMD_ON_QUIT."); + mDhcpStateMachine = null; break; default: |