summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/net/dhcp/DhcpClient.java204
-rw-r--r--src/android/net/ip/IpClient.java142
2 files changed, 296 insertions, 50 deletions
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java
index 9e24ca8..68f7ab0 100644
--- a/src/android/net/dhcp/DhcpClient.java
+++ b/src/android/net/dhcp/DhcpClient.java
@@ -49,6 +49,8 @@ import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
import android.content.Context;
import android.net.DhcpResults;
import android.net.InetAddresses;
+import android.net.Layer2PacketParcelable;
+import android.net.MacAddress;
import android.net.NetworkStackIpMemoryStore;
import android.net.TrafficStats;
import android.net.ip.IpClient;
@@ -169,6 +171,10 @@ public class DhcpClient extends StateMachine {
public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 8;
public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 9;
+ // Command to IpClient starting/aborting preconnection process.
+ public static final int CMD_START_PRECONNECTION = PUBLIC_BASE + 10;
+ public static final int CMD_ABORT_PRECONNECTION = PUBLIC_BASE + 11;
+
/* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
public static final int DHCP_SUCCESS = 1;
public static final int DHCP_FAILURE = 2;
@@ -239,7 +245,7 @@ public class DhcpClient extends StateMachine {
private DhcpResults mDhcpLease;
private long mDhcpLeaseExpiry;
private DhcpResults mOffer;
- private String mL2Key;
+ private Configuration mConfiguration;
private Inet4Address mLastAssignedIpv4Address;
private long mLastAssignedIpv4AddressExpiry;
private Dependencies mDependencies;
@@ -256,6 +262,7 @@ public class DhcpClient extends StateMachine {
private State mStoppedState = new StoppedState();
private State mDhcpState = new DhcpState();
private State mDhcpInitState = new DhcpInitState();
+ private State mDhcpPreconnectingState = new DhcpPreconnectingState();
private State mDhcpSelectingState = new DhcpSelectingState();
private State mDhcpRequestingState = new DhcpRequestingState();
private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
@@ -320,6 +327,7 @@ public class DhcpClient extends StateMachine {
addState(mDhcpInitState, mDhcpState);
addState(mWaitBeforeStartState, mDhcpState);
addState(mWaitBeforeObtainingConfigurationState, mDhcpState);
+ addState(mDhcpPreconnectingState, mDhcpState);
addState(mObtainingConfigurationState, mDhcpState);
addState(mDhcpSelectingState, mDhcpState);
addState(mDhcpRequestingState, mDhcpState);
@@ -578,7 +586,7 @@ public class DhcpClient extends StateMachine {
}
private void setLeaseExpiredToIpMemoryStore() {
- final String l2Key = mL2Key;
+ final String l2Key = mConfiguration.l2Key;
if (l2Key == null) return;
final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
// TODO: clear out the address and lease instead of storing an expired lease
@@ -591,7 +599,7 @@ public class DhcpClient extends StateMachine {
}
private void maybeSaveLeaseToIpMemoryStore() {
- final String l2Key = mL2Key;
+ final String l2Key = mConfiguration.l2Key;
if (l2Key == null || mDhcpLease == null || mDhcpLease.ipAddress == null) return;
final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
na.setAssignedV4Address((Inet4Address) mDhcpLease.ipAddress.getAddress());
@@ -711,7 +719,11 @@ public class DhcpClient extends StateMachine {
// Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
// CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
abstract class WaitBeforeOtherState extends LoggingState {
- protected State mOtherState;
+ private final State mOtherState;
+
+ WaitBeforeOtherState(State otherState) {
+ mOtherState = otherState;
+ }
@Override
public void enter() {
@@ -732,18 +744,46 @@ public class DhcpClient extends StateMachine {
}
}
+ /**
+ * Helper method to transition to the appropriate state according to whether the pre dhcp
+ * action (e.g. turn off power optimization while doing DHCP) is required to execute.
+ * waitStateForPreDhcpAction is used to wait the pre dhcp action completed before moving to
+ * other state. If the pre dhcp action is unnecessary, transition to the target state directly.
+ */
+ private void preDhcpTransitionTo(final State waitStateForPreDhcpAction,
+ final State targetState) {
+ transitionTo(mRegisteredForPreDhcpNotification ? waitStateForPreDhcpAction : targetState);
+ }
+
+ /**
+ * This class is used to convey initial configuration to DhcpClient when starting DHCP.
+ */
+ public static class Configuration {
+ // This is part of the initial configuration because it is passed in on startup and
+ // never updated.
+ // TODO: decide what to do about L2 key changes while the client is connected.
+ public final String l2Key;
+ public final boolean isPreconnectionEnabled;
+
+ public Configuration(String l2Key, boolean isPreconnectionEnabled) {
+ this.l2Key = l2Key;
+ this.isPreconnectionEnabled = isPreconnectionEnabled;
+ }
+ }
+
class StoppedState extends State {
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START_DHCP:
- mL2Key = (String) message.obj;
- if (mRegisteredForPreDhcpNotification) {
- transitionTo(isDhcpLeaseCacheEnabled()
- ? mWaitBeforeObtainingConfigurationState : mWaitBeforeStartState);
+ mConfiguration = (Configuration) message.obj;
+ if (mConfiguration.isPreconnectionEnabled) {
+ transitionTo(mDhcpPreconnectingState);
+ } else if (isDhcpLeaseCacheEnabled()) {
+ preDhcpTransitionTo(mWaitBeforeObtainingConfigurationState,
+ mObtainingConfigurationState);
} else {
- transitionTo(isDhcpLeaseCacheEnabled()
- ? mObtainingConfigurationState : mDhcpInitState);
+ preDhcpTransitionTo(mWaitBeforeStartState, mDhcpInitState);
}
return HANDLED;
default:
@@ -754,22 +794,19 @@ public class DhcpClient extends StateMachine {
class WaitBeforeStartState extends WaitBeforeOtherState {
WaitBeforeStartState(State otherState) {
- super();
- mOtherState = otherState;
+ super(otherState);
}
}
class WaitBeforeRenewalState extends WaitBeforeOtherState {
WaitBeforeRenewalState(State otherState) {
- super();
- mOtherState = otherState;
+ super(otherState);
}
}
class WaitBeforeObtainingConfigurationState extends WaitBeforeOtherState {
WaitBeforeObtainingConfigurationState(State otherState) {
- super();
- mOtherState = otherState;
+ super(otherState);
}
}
@@ -957,7 +994,7 @@ public class DhcpClient extends StateMachine {
}
sendMessage(EVENT_CONFIGURATION_OBTAINED, attributes);
};
- mIpMemoryStore.retrieveNetworkAttributes(mL2Key, listener);
+ mIpMemoryStore.retrieveNetworkAttributes(mConfiguration.l2Key, listener);
}
@Override
@@ -973,7 +1010,7 @@ public class DhcpClient extends StateMachine {
final long currentTime = System.currentTimeMillis();
NetworkAttributes attributes = (NetworkAttributes) message.obj;
if (DBG) {
- Log.d(TAG, "l2key: " + mL2Key
+ Log.d(TAG, "l2key: " + mConfiguration.l2Key
+ " lease address: " + attributes.assignedV4Address
+ " lease expiry: " + attributes.assignedV4AddressExpiry
+ " current time: " + currentTime);
@@ -1002,6 +1039,32 @@ public class DhcpClient extends StateMachine {
}
}
+ private void receiveOfferOrAckPacket(final DhcpPacket packet, final boolean acceptRapidCommit) {
+ if (!isValidPacket(packet)) return;
+
+ // 1. received the DHCPOFFER packet, process it by following RFC2131.
+ // 2. received the DHCPACK packet from DHCP Servers that support Rapid
+ // Commit option, process it by following RFC4039.
+ if (packet instanceof DhcpOfferPacket) {
+ mOffer = packet.toDhcpResults();
+ if (mOffer != null) {
+ Log.d(TAG, "Got pending lease: " + mOffer);
+ transitionTo(mDhcpRequestingState);
+ }
+ } else if (packet instanceof DhcpAckPacket) {
+ // If rapid commit is not enabled in DhcpInitState, or enablePreconnection is
+ // not enabled in DhcpPreconnectingState, ignore DHCPACK packet. Only DHCPACK
+ // with the rapid commit option are valid.
+ if (!acceptRapidCommit || !packet.mRapidCommit) return;
+
+ final DhcpResults results = packet.toDhcpResults();
+ if (results != null) {
+ confirmDhcpLease(packet, results);
+ transitionTo(mConfiguringInterfaceState);
+ }
+ }
+ }
+
class DhcpInitState extends PacketRetransmittingState {
public DhcpInitState() {
super();
@@ -1019,27 +1082,86 @@ public class DhcpClient extends StateMachine {
}
protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
+ receiveOfferOrAckPacket(packet, isDhcpRapidCommitEnabled());
+ }
+ }
- // 1. received the DHCPOFFER packet, process it by following RFC2131.
- // 2. received the DHCPACK packet from DHCP Servers who support Rapid
- // Commit option, process it by following RFC4039.
- if (packet instanceof DhcpOfferPacket) {
- mOffer = packet.toDhcpResults();
- if (mOffer != null) {
- Log.d(TAG, "Got pending lease: " + mOffer);
- transitionTo(mDhcpRequestingState);
- }
- } else if (packet instanceof DhcpAckPacket) {
- // If received DHCPACK packet w/o Rapid Commit option in this state,
- // just drop it and wait for the next DHCPOFFER packet or DHCPACK w/
- // Rapid Commit option.
- if (!isDhcpRapidCommitEnabled() || !packet.mRapidCommit) return;
- final DhcpResults results = packet.toDhcpResults();
- if (results != null) {
- confirmDhcpLease(packet, results);
- transitionTo(mConfiguringInterfaceState);
- }
+ private void startInitRebootOrInit() {
+ if (isDhcpLeaseCacheEnabled()) {
+ preDhcpTransitionTo(mWaitBeforeObtainingConfigurationState,
+ mObtainingConfigurationState);
+ } else {
+ preDhcpTransitionTo(mWaitBeforeStartState, mDhcpInitState);
+ }
+ }
+
+ class DhcpPreconnectingState extends TimeoutState {
+ // This state is used to support Fast Initial Link Setup (FILS) IP Address Setup
+ // procedure defined in the IEEE802.11ai (2016) currently. However, this state could
+ // be extended to support other intended useage as well in the future, e.g. pre-actions
+ // should be completed in advance before the normal DHCP solicit process starts.
+ DhcpPreconnectingState() {
+ mTimeout = FIRST_TIMEOUT_MS;
+ }
+
+ @Override
+ public void enter() {
+ super.enter();
+ startNewTransaction();
+ mLastInitEnterTime = SystemClock.elapsedRealtime();
+ sendPreconnectionPacket();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (super.processMessage(message) == HANDLED) {
+ return HANDLED;
+ }
+
+ switch (message.what) {
+ case CMD_RECEIVED_PACKET:
+ receiveOfferOrAckPacket((DhcpPacket) message.obj,
+ mConfiguration.isPreconnectionEnabled);
+ return HANDLED;
+ case CMD_ABORT_PRECONNECTION:
+ startInitRebootOrInit();
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+
+ // This timeout is necessary and cannot just be replaced with a notification that
+ // preconnection is complete. This is because:
+ // - The preconnection complete notification could arrive before the ACK with rapid
+ // commit arrives. In this case we would go back to init state, pick a new transaction
+ // ID, and when the ACK with rapid commit arrives, we would ignore it because the
+ // transaction ID doesn't match.
+ // - We cannot just wait in this state until the ACK with rapid commit arrives, because
+ // if that ACK never arrives (e.g., dropped by the network), we'll never go back to init
+ // and send a DISCOVER.
+ @Override
+ public void timeout() {
+ startInitRebootOrInit();
+ }
+
+ private void sendPreconnectionPacket() {
+ final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable();
+ final ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
+ DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
+ DO_UNICAST, REQUESTED_PARAMS, true /* rapid commit */);
+
+ l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST);
+ l2Packet.payload = packet.array();
+ mController.sendMessage(CMD_START_PRECONNECTION, l2Packet);
+ }
+
+ private void startInitRebootOrInit() {
+ if (isDhcpLeaseCacheEnabled()) {
+ preDhcpTransitionTo(mWaitBeforeObtainingConfigurationState,
+ mObtainingConfigurationState);
+ } else {
+ preDhcpTransitionTo(mWaitBeforeStartState, mDhcpInitState);
}
}
}
@@ -1165,11 +1287,7 @@ public class DhcpClient extends StateMachine {
super.processMessage(message);
switch (message.what) {
case CMD_RENEW_DHCP:
- if (mRegisteredForPreDhcpNotification) {
- transitionTo(mWaitBeforeRenewalState);
- } else {
- transitionTo(mDhcpRenewingState);
- }
+ preDhcpTransitionTo(mWaitBeforeRenewalState, mDhcpRenewingState);
return HANDLED;
default:
return NOT_HANDLED;
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index 669dbfd..8e95ab5 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -27,6 +27,7 @@ import android.net.ConnectivityManager;
import android.net.DhcpResults;
import android.net.INetd;
import android.net.IpPrefix;
+import android.net.Layer2PacketParcelable;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NattKeepalivePacketDataParcelable;
@@ -38,6 +39,7 @@ import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfCapabilities;
import android.net.apf.ApfFilter;
import android.net.dhcp.DhcpClient;
+import android.net.dhcp.DhcpClient.Configuration;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpManagerEvent;
import android.net.shared.InitialConfiguration;
@@ -76,6 +78,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -178,6 +181,10 @@ public class IpClient extends StateMachine {
mLog.e(PREFIX + msg, e);
}
+ /**
+ * Callback called prior to DHCP discovery/renewal only if the pre DHCP action
+ * is enabled.
+ */
public void onPreDhcpAction() {
log("onPreDhcpAction()");
try {
@@ -187,6 +194,10 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * Callback called after DHCP discovery/renewal only if the pre DHCP action
+ * is enabled.
+ */
public void onPostDhcpAction() {
log("onPostDhcpAction()");
try {
@@ -196,6 +207,9 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * Callback called when new DHCP results are available.
+ */
public void onNewDhcpResults(DhcpResults dhcpResults) {
log("onNewDhcpResults({" + dhcpResults + "})");
try {
@@ -205,6 +219,9 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * Indicates that provisioning was successful.
+ */
public void onProvisioningSuccess(LinkProperties newLp) {
log("onProvisioningSuccess({" + newLp + "})");
try {
@@ -214,6 +231,9 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * Indicates that provisioning failed.
+ */
public void onProvisioningFailure(LinkProperties newLp) {
log("onProvisioningFailure({" + newLp + "})");
try {
@@ -223,6 +243,9 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * Invoked on LinkProperties changes.
+ */
public void onLinkPropertiesChange(LinkProperties newLp) {
log("onLinkPropertiesChange({" + newLp + "})");
try {
@@ -232,6 +255,10 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * Called when the internal IpReachabilityMonitor (if enabled) has detected the loss of
+ * required neighbors (e.g. on-link default gw or dns servers) due to NUD_FAILED.
+ */
public void onReachabilityLost(String logMsg) {
log("onReachabilityLost(" + logMsg + ")");
try {
@@ -241,6 +268,9 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * Called when the IpClient state machine terminates.
+ */
public void onQuit() {
log("onQuit()");
try {
@@ -250,6 +280,9 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * Called to indicate that a new APF program must be installed to filter incoming packets.
+ */
public void installPacketFilter(byte[] filter) {
log("installPacketFilter(byte[" + filter.length + "])");
try {
@@ -259,6 +292,10 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * Called to indicate that the APF Program & data buffer must be read asynchronously from
+ * the wifi driver.
+ */
public void startReadPacketFilter() {
log("startReadPacketFilter()");
try {
@@ -268,6 +305,10 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * If multicast filtering cannot be accomplished with APF, this function will be called to
+ * actuate multicast filtering using another means.
+ */
public void setFallbackMulticastFilter(boolean enabled) {
log("setFallbackMulticastFilter(" + enabled + ")");
try {
@@ -277,6 +318,10 @@ public class IpClient extends StateMachine {
}
}
+ /**
+ * Enabled/disable Neighbor Discover offload functionality. This is called, for example,
+ * whenever 464xlat is being started or stopped.
+ */
public void setNeighborDiscoveryOffload(boolean enable) {
log("setNeighborDiscoveryOffload(" + enable + ")");
try {
@@ -285,6 +330,18 @@ public class IpClient extends StateMachine {
log("Failed to call setNeighborDiscoveryOffload", e);
}
}
+
+ /**
+ * Invoked on starting preconnection process.
+ */
+ public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {
+ log("onPreconnectionStart(Layer2Packets[" + packets.size() + "])");
+ try {
+ mCallback.onPreconnectionStart(packets);
+ } catch (RemoteException e) {
+ log("Failed to call onPreconnectionStart", e);
+ }
+ }
}
public static final String DUMP_ARG_CONFIRM = "confirm";
@@ -306,6 +363,7 @@ public class IpClient extends StateMachine {
private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13;
private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14;
private static final int CMD_UPDATE_L2KEY_GROUPHINT = 15;
+ protected static final int CMD_COMPLETE_PRECONNECTION = 16;
// Internal commands to use instead of trying to call transitionTo() inside
// a given State's enter() method. Calling transitionTo() from enter/exit
@@ -345,6 +403,7 @@ public class IpClient extends StateMachine {
private final State mClearingIpAddressesState = new ClearingIpAddressesState();
private final State mStartedState = new StartedState();
private final State mRunningState = new RunningState();
+ private final State mPreconnectingState = new PreconnectingState();
private final String mTag;
private final Context mContext;
@@ -607,6 +666,11 @@ public class IpClient extends StateMachine {
enforceNetworkStackCallingPermission();
IpClient.this.removeKeepalivePacketFilter(slot);
}
+ @Override
+ public void notifyPreconnectionComplete(boolean success) {
+ enforceNetworkStackCallingPermission();
+ IpClient.this.notifyPreconnectionComplete(success);
+ }
@Override
public int getInterfaceVersion() {
@@ -622,6 +686,7 @@ public class IpClient extends StateMachine {
// CHECKSTYLE:OFF IndentationCheck
addState(mStoppedState);
addState(mStartedState);
+ addState(mPreconnectingState, mStartedState);
addState(mClearingIpAddressesState, mStartedState);
addState(mRunningState, mStartedState);
addState(mStoppingState);
@@ -767,6 +832,15 @@ public class IpClient extends StateMachine {
}
/**
+ * Notify IpClient that preconnection is complete and that the link is ready for use.
+ * The success parameter indicates whether the packets passed in by onPreconnectionStart were
+ * successfully sent to the network or not.
+ */
+ public void notifyPreconnectionComplete(boolean success) {
+ sendMessage(CMD_COMPLETE_PRECONNECTION, success ? 1 : 0);
+ }
+
+ /**
* Dump logs of this IpClient.
*/
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -1229,11 +1303,10 @@ public class IpClient extends StateMachine {
return false;
}
} else {
- // Start DHCPv4.
- mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams,
- mDependencies.getDhcpClientDependencies(mIpMemoryStore));
- mDhcpClient.registerForPreDhcpNotification();
- mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP, mL2Key);
+ if (mDhcpClient != null) {
+ Log.wtf(mTag, "DhcpClient should never be non-null in startIPv4()");
+ }
+ startDhcpClient();
}
return true;
@@ -1439,6 +1512,25 @@ public class IpClient extends StateMachine {
}
}
+ private boolean isUsingPreconnection() {
+ return mConfiguration.mEnablePreconnection && mConfiguration.mStaticIpConfig == null;
+ }
+
+ private void startDhcpClient() {
+ // Start DHCPv4.
+ mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams,
+ mDependencies.getDhcpClientDependencies(mIpMemoryStore));
+
+ // If preconnection is enabled, there is no need to ask Wi-Fi to disable powersaving
+ // during DHCP, because the DHCP handshake will happen during association. In order to
+ // ensure that future renews still do the DHCP action (if configured),
+ // registerForPreDhcpNotification is called later when processing the CMD_*_PRECONNECTION
+ // messages.
+ if (!isUsingPreconnection()) mDhcpClient.registerForPreDhcpNotification();
+ mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP, new Configuration(mL2Key,
+ isUsingPreconnection()));
+ }
+
class ClearingIpAddressesState extends State {
@Override
public void enter() {
@@ -1456,7 +1548,7 @@ public class IpClient extends StateMachine {
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_ADDRESSES_CLEARED:
- transitionTo(mRunningState);
+ transitionTo(isUsingPreconnection() ? mPreconnectingState : mRunningState);
break;
case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
@@ -1487,6 +1579,42 @@ public class IpClient extends StateMachine {
}
}
+ class PreconnectingState extends State {
+ @Override
+ public void enter() {
+ startDhcpClient();
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_COMPLETE_PRECONNECTION:
+ boolean success = (msg.arg1 == 1);
+ mDhcpClient.registerForPreDhcpNotification();
+ if (!success) {
+ mDhcpClient.sendMessage(DhcpClient.CMD_ABORT_PRECONNECTION);
+ }
+ // The link is ready for use. Advance to running state, start IPv6, etc.
+ transitionTo(mRunningState);
+ break;
+
+ case DhcpClient.CMD_START_PRECONNECTION:
+ final Layer2PacketParcelable l2Packet = (Layer2PacketParcelable) msg.obj;
+ mCallback.onPreconnectionStart(Collections.singletonList(l2Packet));
+ break;
+
+ case CMD_STOP:
+ case EVENT_PROVISIONING_TIMEOUT:
+ // Fall through to StartedState.
+ return NOT_HANDLED;
+
+ default:
+ deferMessage(msg);
+ }
+ return HANDLED;
+ }
+ }
+
class StartedState extends State {
@Override
public void enter() {
@@ -1566,7 +1694,7 @@ public class IpClient extends StateMachine {
return;
}
- if (mConfiguration.mEnableIPv4 && !startIPv4()) {
+ if (mConfiguration.mEnableIPv4 && !isUsingPreconnection() && !startIPv4()) {
doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
enqueueJumpToStoppingState();
return;