summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/android/net/dhcp/DhcpClient.java207
-rw-r--r--src/android/net/ip/IpClient.java20
-rw-r--r--src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java3
-rw-r--r--tests/unit/src/android/net/ip/IpClientTest.java9
-rw-r--r--tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java2
5 files changed, 222 insertions, 19 deletions
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java
index ca6c17a..bd36d16 100644
--- a/src/android/net/dhcp/DhcpClient.java
+++ b/src/android/net/dhcp/DhcpClient.java
@@ -28,6 +28,7 @@ import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
import static android.net.util.NetworkStackUtils.closeSocketQuietly;
import static android.net.util.SocketUtils.makePacketSocketAddress;
import static android.system.OsConstants.AF_INET;
@@ -43,11 +44,16 @@ import static android.system.OsConstants.SO_REUSEADDR;
import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.DhcpResults;
import android.net.InetAddresses;
+import android.net.NetworkStackIpMemoryStore;
import android.net.TrafficStats;
import android.net.ip.IpClient;
+import android.net.ipmemorystore.NetworkAttributes;
+import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
+import android.net.ipmemorystore.OnStatusListener;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.IpConnectivityLog;
@@ -62,6 +68,7 @@ import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
@@ -118,8 +125,9 @@ public class DhcpClient extends StateMachine {
// Timers and timeouts.
private static final int SECONDS = 1000;
- private static final int FIRST_TIMEOUT_MS = 2 * SECONDS;
- private static final int MAX_TIMEOUT_MS = 128 * SECONDS;
+ private static final int FIRST_TIMEOUT_MS = 2 * SECONDS;
+ private static final int MAX_TIMEOUT_MS = 128 * SECONDS;
+ private static final int IPMEMORYSTORE_TIMEOUT_MS = 1 * SECONDS;
// This is not strictly needed, since the client is asynchronous and implements exponential
// backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
@@ -166,6 +174,12 @@ public class DhcpClient extends StateMachine {
private static final int CMD_RENEW_DHCP = PRIVATE_BASE + 4;
private static final int CMD_REBIND_DHCP = PRIVATE_BASE + 5;
private static final int CMD_EXPIRE_DHCP = PRIVATE_BASE + 6;
+ private static final int EVENT_CONFIGURATION_TIMEOUT = PRIVATE_BASE + 7;
+ private static final int EVENT_CONFIGURATION_OBTAINED = PRIVATE_BASE + 8;
+ private static final int EVENT_CONFIGURATION_INVALID = PRIVATE_BASE + 9;
+
+ // constant to represent this DHCP lease has been expired.
+ private static final long EXPIRED_LEASE = 1L;
// For message logging.
private static final Class[] sMessageClasses = { DhcpClient.class };
@@ -222,6 +236,12 @@ public class DhcpClient extends StateMachine {
private DhcpResults mDhcpLease;
private long mDhcpLeaseExpiry;
private DhcpResults mOffer;
+ private String mL2Key;
+ private Inet4Address mLastAssignedIpv4Address;
+ private long mLastAssignedIpv4AddressExpiry;
+ private boolean mDhcpLeaseCacheEnabled;
+ @NonNull
+ private final NetworkStackIpMemoryStore mIpMemoryStore;
// Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
private long mLastInitEnterTime;
@@ -240,8 +260,11 @@ public class DhcpClient extends StateMachine {
private State mDhcpRebindingState = new DhcpRebindingState();
private State mDhcpInitRebootState = new DhcpInitRebootState();
private State mDhcpRebootingState = new DhcpRebootingState();
+ private State mObtainingConfigurationState = new ObtainingConfigurationState();
private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
+ private State mWaitBeforeObtainingConfigurationState =
+ new WaitBeforeObtainingConfigurationState(mObtainingConfigurationState);
private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
@@ -249,17 +272,22 @@ public class DhcpClient extends StateMachine {
}
// TODO: Take an InterfaceParams instance instead of an interface name String.
- private DhcpClient(Context context, StateMachine controller, String iface) {
+ private DhcpClient(Context context, StateMachine controller, String iface,
+ NetworkStackIpMemoryStore ipMemoryStore) {
super(TAG, controller.getHandler());
mContext = context;
mController = controller;
mIfaceName = iface;
+ mIpMemoryStore = ipMemoryStore;
+ // CHECKSTYLE:OFF IndentationCheck
addState(mStoppedState);
addState(mDhcpState);
addState(mDhcpInitState, mDhcpState);
addState(mWaitBeforeStartState, mDhcpState);
+ addState(mWaitBeforeObtainingConfigurationState, mDhcpState);
+ addState(mObtainingConfigurationState, mDhcpState);
addState(mDhcpSelectingState, mDhcpState);
addState(mDhcpRequestingState, mDhcpState);
addState(mDhcpHaveLeaseState, mDhcpState);
@@ -270,6 +298,7 @@ public class DhcpClient extends StateMachine {
addState(mDhcpRebindingState, mDhcpHaveLeaseState);
addState(mDhcpInitRebootState, mDhcpState);
addState(mDhcpRebootingState, mDhcpState);
+ // CHECKSTYLE:ON IndentationCheck
setInitialState(mStoppedState);
@@ -290,13 +319,34 @@ public class DhcpClient extends StateMachine {
}
public static DhcpClient makeDhcpClient(
- Context context, StateMachine controller, InterfaceParams ifParams) {
- DhcpClient client = new DhcpClient(context, controller, ifParams.name);
+ Context context, StateMachine controller, InterfaceParams ifParams,
+ NetworkStackIpMemoryStore ipMemoryStore) {
+ DhcpClient client = new DhcpClient(context, controller, ifParams.name, ipMemoryStore);
client.mIface = ifParams;
client.start();
return client;
}
+ /**
+ * check whether or not to support caching the last lease info and INIT-REBOOT state.
+ *
+ */
+ public boolean isDhcpLeaseCacheEnabled() {
+ // TODO: call DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY,
+ // DeviceConfig.PROPERTY);
+ // to fetch the dynamic experiment flag value. Return false by default.
+ return mDhcpLeaseCacheEnabled;
+ }
+
+ /**
+ * set DHCP lease cached enabled experiment flag.
+ *
+ */
+ @VisibleForTesting
+ public void setDhcpLeaseCacheEnabled(final boolean enabled) {
+ mDhcpLeaseCacheEnabled = enabled;
+ }
+
private boolean initInterface() {
if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
if (mIface == null) {
@@ -491,12 +541,48 @@ public class DhcpClient extends StateMachine {
Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
}
+ private void setLeaseExpiredToIpMemoryStore() {
+ final String l2Key = mL2Key;
+ if (l2Key == null) return;
+ final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
+ // TODO: clear out the address and lease instead of storing an expired lease
+ na.setAssignedV4AddressExpiry(EXPIRED_LEASE);
+
+ final OnStatusListener listener = status -> {
+ if (!status.isSuccess()) Log.e(TAG, "Failed to set lease expiry, status: " + status);
+ };
+ mIpMemoryStore.storeNetworkAttributes(l2Key, na.build(), listener);
+ }
+
+ private void maybeSaveLeaseToIpMemoryStore() {
+ final String l2Key = mL2Key;
+ if (l2Key == null || mDhcpLease == null || mDhcpLease.ipAddress == null) return;
+ final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
+ na.setAssignedV4Address((Inet4Address) mDhcpLease.ipAddress.getAddress());
+ na.setAssignedV4AddressExpiry((mDhcpLease.leaseDuration == INFINITE_LEASE)
+ ? Long.MAX_VALUE
+ : mDhcpLease.leaseDuration * 1000 + System.currentTimeMillis());
+ na.setDnsAddresses(mDhcpLease.dnsServers);
+ na.setMtu(mDhcpLease.mtu);
+
+ final OnStatusListener listener = status -> {
+ if (!status.isSuccess()) Log.e(TAG, "Failed to store network attrs, status: " + status);
+ };
+ mIpMemoryStore.storeNetworkAttributes(l2Key, na.build(), listener);
+ }
+
private void notifySuccess() {
+ if (isDhcpLeaseCacheEnabled()) {
+ maybeSaveLeaseToIpMemoryStore();
+ }
mController.sendMessage(
CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
}
private void notifyFailure() {
+ if (isDhcpLeaseCacheEnabled()) {
+ setLeaseExpiredToIpMemoryStore();
+ }
mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
}
@@ -616,10 +702,13 @@ public class DhcpClient extends StateMachine {
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START_DHCP:
+ mL2Key = (String) message.obj;
if (mRegisteredForPreDhcpNotification) {
- transitionTo(mWaitBeforeStartState);
+ transitionTo(isDhcpLeaseCacheEnabled()
+ ? mWaitBeforeObtainingConfigurationState : mWaitBeforeStartState);
} else {
- transitionTo(mDhcpInitState);
+ transitionTo(isDhcpLeaseCacheEnabled()
+ ? mObtainingConfigurationState : mDhcpInitState);
}
return HANDLED;
default:
@@ -629,14 +718,21 @@ public class DhcpClient extends StateMachine {
}
class WaitBeforeStartState extends WaitBeforeOtherState {
- public WaitBeforeStartState(State otherState) {
+ WaitBeforeStartState(State otherState) {
super();
mOtherState = otherState;
}
}
class WaitBeforeRenewalState extends WaitBeforeOtherState {
- public WaitBeforeRenewalState(State otherState) {
+ WaitBeforeRenewalState(State otherState) {
+ super();
+ mOtherState = otherState;
+ }
+ }
+
+ class WaitBeforeObtainingConfigurationState extends WaitBeforeOtherState {
+ WaitBeforeObtainingConfigurationState(State otherState) {
super();
mOtherState = otherState;
}
@@ -782,6 +878,69 @@ public class DhcpClient extends StateMachine {
}
}
+ class ObtainingConfigurationState extends LoggingState {
+ @Override
+ public void enter() {
+ super.enter();
+
+ // Set a timeout for retrieving network attributes operation
+ sendMessageDelayed(EVENT_CONFIGURATION_TIMEOUT, IPMEMORYSTORE_TIMEOUT_MS);
+
+ final OnNetworkAttributesRetrievedListener listener = (status, l2Key, attributes) -> {
+ if (null == attributes || null == attributes.assignedV4Address) {
+ if (!status.isSuccess()) {
+ Log.e(TAG, "Error retrieving network attributes: " + status);
+ }
+ sendMessage(EVENT_CONFIGURATION_INVALID);
+ return;
+ }
+ sendMessage(EVENT_CONFIGURATION_OBTAINED, attributes);
+ };
+ mIpMemoryStore.retrieveNetworkAttributes(mL2Key, listener);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ super.processMessage(message);
+ switch (message.what) {
+ case EVENT_CONFIGURATION_INVALID:
+ case EVENT_CONFIGURATION_TIMEOUT:
+ transitionTo(mDhcpInitState);
+ return HANDLED;
+
+ case EVENT_CONFIGURATION_OBTAINED:
+ final long currentTime = System.currentTimeMillis();
+ NetworkAttributes attributes = (NetworkAttributes) message.obj;
+ if (DBG) {
+ Log.d(TAG, "l2key: " + mL2Key
+ + " lease address: " + attributes.assignedV4Address
+ + " lease expiry: " + attributes.assignedV4AddressExpiry
+ + " current time: " + currentTime);
+ }
+ if (currentTime >= attributes.assignedV4AddressExpiry) {
+ // Lease has expired.
+ transitionTo(mDhcpInitState);
+ return HANDLED;
+ }
+ mLastAssignedIpv4Address = attributes.assignedV4Address;
+ mLastAssignedIpv4AddressExpiry = attributes.assignedV4AddressExpiry;
+ transitionTo(mDhcpInitRebootState);
+ return HANDLED;
+
+ default:
+ deferMessage(message);
+ return HANDLED;
+ }
+ }
+
+ @Override
+ public void exit() {
+ removeMessages(EVENT_CONFIGURATION_INVALID);
+ removeMessages(EVENT_CONFIGURATION_TIMEOUT);
+ removeMessages(EVENT_CONFIGURATION_OBTAINED);
+ }
+ }
+
class DhcpInitState extends PacketRetransmittingState {
public DhcpInitState() {
super();
@@ -1050,7 +1209,35 @@ public class DhcpClient extends StateMachine {
}
}
- class DhcpInitRebootState extends LoggingState {
+ class DhcpInitRebootState extends DhcpRequestingState {
+ @Override
+ public void enter() {
+ super.enter();
+ startNewTransaction();
+ }
+
+ // RFC 2131 4.3.2 describes generated DHCPREQUEST message during
+ // INIT-REBOOT state:
+ // 'server identifier' MUST NOT be filled in, 'requested IP address'
+ // option MUST be filled in with client's notion of its previously
+ // assigned address. 'ciaddr' MUST be zero. The client is seeking to
+ // verify a previously allocated, cached configuration. Server SHOULD
+ // send a DHCPNAK message to the client if the 'requested IP address'
+ // is incorrect, or is on the wrong network.
+ @Override
+ protected boolean sendPacket() {
+ return sendRequestPacket(
+ INADDR_ANY, // ciaddr
+ mLastAssignedIpv4Address, // DHCP_REQUESTED_IP
+ null, // DHCP_SERVER_IDENTIFIER
+ INADDR_BROADCAST); // packet destination address
+ }
+
+ @Override
+ public void exit() {
+ mLastAssignedIpv4Address = null;
+ mLastAssignedIpv4AddressExpiry = 0;
+ }
}
class DhcpRebootingState extends LoggingState {
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index 266b1b0..ea19c7b 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -78,7 +78,6 @@ import java.util.concurrent.CountDownLatch;
import java.util.function.Predicate;
import java.util.stream.Collectors;
-
/**
* IpClient
*
@@ -394,6 +393,14 @@ public class IpClient extends StateMachine {
public INetd getNetd(Context context) {
return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
}
+
+ /**
+ * Get a IpMemoryStore instance.
+ */
+ public NetworkStackIpMemoryStore getIpMemoryStore(Context context,
+ NetworkStackServiceManager nssManager) {
+ return new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService());
+ }
}
public IpClient(Context context, String ifName, IIpClientCallbacks callback,
@@ -418,8 +425,7 @@ public class IpClient extends StateMachine {
mShutdownLatch = new CountDownLatch(1);
mCm = mContext.getSystemService(ConnectivityManager.class);
mObserverRegistry = observerRegistry;
- mIpMemoryStore =
- new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService());
+ mIpMemoryStore = deps.getIpMemoryStore(context, nssManager);
sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
mLog = sSmLogs.get(mInterfaceName);
@@ -1124,6 +1130,7 @@ public class IpClient extends StateMachine {
}
mCallback.onNewDhcpResults(dhcpResults);
maybeSaveNetworkToIpMemoryStore();
+
dispatchCallback(delta, newLp);
}
@@ -1182,9 +1189,10 @@ public class IpClient extends StateMachine {
}
} else {
// Start DHCPv4.
- mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
+ mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams,
+ mIpMemoryStore);
mDhcpClient.registerForPreDhcpNotification();
- mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
+ mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP, mL2Key);
}
return true;
@@ -1369,7 +1377,6 @@ public class IpClient extends StateMachine {
@Override
public void enter() {
mStartTimeMillis = SystemClock.elapsedRealtime();
-
if (mConfiguration.mProvisioningTimeoutMs > 0) {
final long alarmTime = SystemClock.elapsedRealtime()
+ mConfiguration.mProvisioningTimeoutMs;
@@ -1426,7 +1433,6 @@ public class IpClient extends StateMachine {
case EVENT_PROVISIONING_TIMEOUT:
handleProvisioningFailure();
break;
-
default:
// It's safe to process messages out of order because the
// only message that can both
diff --git a/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java b/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
index a538a5b..4896ef2 100644
--- a/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
+++ b/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
@@ -73,7 +73,8 @@ public class IpMemoryStoreDatabase {
public static final String COLTYPE_ASSIGNEDV4ADDRESS = "INTEGER";
public static final String COLNAME_ASSIGNEDV4ADDRESSEXPIRY = "assignedV4AddressExpiry";
- // The lease expiry timestamp in uint of milliseconds
+ // The lease expiry timestamp in uint of milliseconds since the Epoch. Long.MAX_VALUE
+ // is used to represent "infinite lease".
public static final String COLTYPE_ASSIGNEDV4ADDRESSEXPIRY = "BIGINT";
// Please note that the group hint is only a *hint*, hence its name. The client can offer
diff --git a/tests/unit/src/android/net/ip/IpClientTest.java b/tests/unit/src/android/net/ip/IpClientTest.java
index 5f80006..1d6ce6e 100644
--- a/tests/unit/src/android/net/ip/IpClientTest.java
+++ b/tests/unit/src/android/net/ip/IpClientTest.java
@@ -55,6 +55,7 @@ import com.android.internal.R;
import com.android.server.NetworkObserver;
import com.android.server.NetworkObserverRegistry;
import com.android.server.NetworkStackService;
+import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;
import org.junit.Before;
import org.junit.Test;
@@ -69,6 +70,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
+
/**
* Tests for IpClient.
*/
@@ -98,6 +100,8 @@ public class IpClientTest {
@Mock private ContentResolver mContentResolver;
@Mock private NetworkStackService.NetworkStackServiceManager mNetworkStackServiceManager;
@Mock private NetworkStackIpMemoryStore mIpMemoryStore;
+ @Mock private IpMemoryStoreService mIpMemoryStoreService;
+ @Mock private InterfaceParams mInterfaceParams;
private NetworkObserver mObserver;
private InterfaceParams mIfParams;
@@ -113,6 +117,11 @@ public class IpClientTest {
when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
.thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ when(mNetworkStackServiceManager.getIpMemoryStoreService())
+ .thenReturn(mIpMemoryStoreService);
+ when(mDependencies.getInterfaceParams(any())).thenReturn(mInterfaceParams);
+ when(mDependencies.getIpMemoryStore(mContext, mNetworkStackServiceManager))
+ .thenReturn(mIpMemoryStore);
mIfParams = null;
}
diff --git a/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
index 64fe3a6..ba86fcf 100644
--- a/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -224,7 +224,7 @@ public class IpMemoryStoreServiceTest {
};
}
- /** Helper method to make an IOnSameNetworkResponseListener */
+ /** Helper method to make an IOnSameL3NetworkResponseListener */
private interface OnSameL3NetworkResponseListener {
void onSameL3NetworkResponse(Status status, SameL3NetworkResponse answer);
}