Loading src/android/net/dhcp/DhcpClient.java +197 −10 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -120,6 +127,7 @@ public class DhcpClient extends StateMachine { 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 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 Loading Loading @@ -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 }; Loading Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading @@ -270,6 +298,7 @@ public class DhcpClient extends StateMachine { addState(mDhcpRebindingState, mDhcpHaveLeaseState); addState(mDhcpInitRebootState, mDhcpState); addState(mDhcpRebootingState, mDhcpState); // CHECKSTYLE:ON IndentationCheck setInitialState(mStoppedState); Loading @@ -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) { Loading Loading @@ -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); } Loading Loading @@ -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: Loading @@ -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; } Loading Loading @@ -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(); Loading Loading @@ -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 { Loading src/android/net/ip/IpClient.java +13 −7 Original line number Diff line number Diff line Loading @@ -78,7 +78,6 @@ import java.util.concurrent.CountDownLatch; import java.util.function.Predicate; import java.util.stream.Collectors; /** * IpClient * Loading Loading @@ -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, Loading @@ -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); Loading Loading @@ -1124,6 +1130,7 @@ public class IpClient extends StateMachine { } mCallback.onNewDhcpResults(dhcpResults); maybeSaveNetworkToIpMemoryStore(); dispatchCallback(delta, newLp); } Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java +2 −1 Original line number Diff line number Diff line Loading @@ -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 Loading tests/unit/src/android/net/ip/IpClientTest.java +9 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -69,6 +70,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; /** * Tests for IpClient. */ Loading Loading @@ -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; Loading @@ -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; } Loading tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading
src/android/net/dhcp/DhcpClient.java +197 −10 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -120,6 +127,7 @@ public class DhcpClient extends StateMachine { 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 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 Loading Loading @@ -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 }; Loading Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading @@ -270,6 +298,7 @@ public class DhcpClient extends StateMachine { addState(mDhcpRebindingState, mDhcpHaveLeaseState); addState(mDhcpInitRebootState, mDhcpState); addState(mDhcpRebootingState, mDhcpState); // CHECKSTYLE:ON IndentationCheck setInitialState(mStoppedState); Loading @@ -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) { Loading Loading @@ -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); } Loading Loading @@ -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: Loading @@ -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; } Loading Loading @@ -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(); Loading Loading @@ -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 { Loading
src/android/net/ip/IpClient.java +13 −7 Original line number Diff line number Diff line Loading @@ -78,7 +78,6 @@ import java.util.concurrent.CountDownLatch; import java.util.function.Predicate; import java.util.stream.Collectors; /** * IpClient * Loading Loading @@ -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, Loading @@ -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); Loading Loading @@ -1124,6 +1130,7 @@ public class IpClient extends StateMachine { } mCallback.onNewDhcpResults(dhcpResults); maybeSaveNetworkToIpMemoryStore(); dispatchCallback(delta, newLp); } Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading
src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java +2 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
tests/unit/src/android/net/ip/IpClientTest.java +9 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -69,6 +70,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; /** * Tests for IpClient. */ Loading Loading @@ -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; Loading @@ -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; } Loading
tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -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); } Loading