Move IPv4 address setting to IpManager
Bug: 24837343
Bug: 27605330
Change-Id: I19ac80e45b3e9200f81d1166ac6094fd19aee963
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 6d0808f..2b6c916 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -32,8 +32,6 @@
import android.net.NetworkUtils;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -121,6 +119,13 @@
* after pre DHCP action is complete */
public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 7;
+ /* Command and event notification to/from IpManager requesting the setting
+ * (or clearing) of an IPv4 LinkAddress.
+ */
+ public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 8;
+ public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 9;
+ public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 10;
+
/* Message.arg1 arguments to CMD_POST_DHCP notification */
public static final int DHCP_SUCCESS = 1;
public static final int DHCP_FAILURE = 2;
@@ -157,7 +162,6 @@
// System services / libraries we use.
private final Context mContext;
private final Random mRandom;
- private final INetworkManagementService mNMService;
// Sockets.
// - We use a packet socket to receive, because servers send us packets bound for IP addresses
@@ -192,7 +196,8 @@
private State mDhcpInitState = new DhcpInitState();
private State mDhcpSelectingState = new DhcpSelectingState();
private State mDhcpRequestingState = new DhcpRequestingState();
- private State mDhcpHaveAddressState = new DhcpHaveAddressState();
+ private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
+ private State mConfiguringInterfaceState = new ConfiguringInterfaceState();
private State mDhcpBoundState = new DhcpBoundState();
private State mDhcpRenewingState = new DhcpRenewingState();
private State mDhcpRebindingState = new DhcpRebindingState();
@@ -219,19 +224,17 @@
addState(mWaitBeforeStartState, mDhcpState);
addState(mDhcpSelectingState, mDhcpState);
addState(mDhcpRequestingState, mDhcpState);
- addState(mDhcpHaveAddressState, mDhcpState);
- addState(mDhcpBoundState, mDhcpHaveAddressState);
- addState(mWaitBeforeRenewalState, mDhcpHaveAddressState);
- addState(mDhcpRenewingState, mDhcpHaveAddressState);
- addState(mDhcpRebindingState, mDhcpHaveAddressState);
+ addState(mDhcpHaveLeaseState, mDhcpState);
+ addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
+ addState(mDhcpBoundState, mDhcpHaveLeaseState);
+ addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
+ addState(mDhcpRenewingState, mDhcpHaveLeaseState);
+ addState(mDhcpRebindingState, mDhcpHaveLeaseState);
addState(mDhcpInitRebootState, mDhcpState);
addState(mDhcpRebootingState, mDhcpState);
setInitialState(mStoppedState);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- mNMService = INetworkManagementService.Stub.asInterface(b);
-
mRandom = new Random();
// Used to schedule packet retransmissions.
@@ -321,18 +324,6 @@
closeQuietly(mPacketSock);
}
- private boolean setIpAddress(LinkAddress address) {
- InterfaceConfiguration ifcg = new InterfaceConfiguration();
- ifcg.setLinkAddress(address);
- try {
- mNMService.setInterfaceConfig(mIfaceName, ifcg);
- } catch (RemoteException|IllegalStateException e) {
- Log.e(TAG, "Error configuring IP address " + address + ": ", e);
- return false;
- }
- return true;
- }
-
class ReceiveThread extends Thread {
private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
@@ -382,7 +373,8 @@
Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
} else {
// It's safe to call getpeername here, because we only send unicast packets if we
- // have an IP address, and we connect the UDP socket in DhcpHaveAddressState#enter.
+ // have an IP address, and we connect the UDP socket before
+ // ConfiguringInterfaceState#exit.
if (DBG) Log.d(TAG, "Unicasting " + description + " to " + Os.getpeername(mUdpSock));
Os.write(mUdpSock, buf);
}
@@ -460,6 +452,7 @@
}
abstract class LoggingState extends State {
+ @Override
public void enter() {
if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
DhcpClientEvent.logStateEvent(mIfaceName, getName());
@@ -759,7 +752,7 @@
mOffer = null;
Log.d(TAG, "Confirmed lease: " + mDhcpLease);
setDhcpLeaseExpiry(packet);
- transitionTo(mDhcpBoundState);
+ transitionTo(mConfiguringInterfaceState);
}
} else if (packet instanceof DhcpNakPacket) {
// TODO: Wait a while before returning into INIT state.
@@ -776,24 +769,52 @@
}
}
- class DhcpHaveAddressState extends LoggingState {
+ class DhcpHaveLeaseState extends LoggingState {
@Override
public void enter() {
super.enter();
- if (!setIpAddress(mDhcpLease.ipAddress) ||
- (mDhcpLease.serverAddress != null &&
- !connectUdpSock((mDhcpLease.serverAddress)))) {
- notifyFailure();
- // There's likely no point in going into DhcpInitState here, we'll probably just
- // repeat the transaction, get the same IP address as before, and fail.
- transitionTo(mStoppedState);
- }
}
@Override
public void exit() {
- if (DBG) Log.d(TAG, "Clearing IP address");
- setIpAddress(new LinkAddress("0.0.0.0/0"));
+ // Tell IpManager to clear the IPv4 address. There is no need to
+ // wait for confirmation since any subsequent packets are sent from
+ // INADDR_ANY anyway (DISCOVER, REQUEST).
+ mController.sendMessage(CMD_CLEAR_LINKADDRESS);
+ }
+ }
+
+ class ConfiguringInterfaceState extends LoggingState {
+ @Override
+ public void enter() {
+ super.enter();
+ mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ super.processMessage(message);
+ switch (message.what) {
+ case EVENT_LINKADDRESS_CONFIGURED:
+ if (mDhcpLease.serverAddress != null &&
+ !connectUdpSock(mDhcpLease.serverAddress)) {
+ // There's likely no point in going into DhcpInitState here, we'll probably
+ // just repeat the transaction, get the same IP address as before, and fail.
+ //
+ // NOTE: It is observed that connectUdpSock() basically never fails, due to
+ // SO_BINDTODEVICE. Examining the local socket address shows it will happily
+ // return an IPv4 address from another interface, or even return "0.0.0.0".
+ //
+ // TODO: Consider deleting this check, following testing on several kernels.
+ notifyFailure();
+ transitionTo(mStoppedState);
+ } else {
+ transitionTo(mDhcpBoundState);
+ }
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
}
}
@@ -803,8 +824,8 @@
super.enter();
mOneshotTimeoutAlarm.cancel();
notifySuccess();
- // TODO: DhcpStateMachine only supported renewing at 50% of the lease time, and did not
- // support rebinding. Once the legacy DHCP client is gone, fix this.
+ // 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.
scheduleRenew();
}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index e39f1bc..fd98f46 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -770,6 +770,19 @@
return (delta != ProvisioningChange.LOST_PROVISIONING);
}
+ private boolean setIPv4Address(LinkAddress address) {
+ final InterfaceConfiguration ifcg = new InterfaceConfiguration();
+ ifcg.setLinkAddress(address);
+ try {
+ mNwService.setInterfaceConfig(mInterfaceName, ifcg);
+ if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
+ } catch (IllegalStateException | RemoteException e) {
+ Log.e(mTag, "IPv4 configuration failed: ", e);
+ return false;
+ }
+ return true;
+ }
+
private void clearIPv4Address() {
try {
final InterfaceConfiguration ifcg = new InterfaceConfiguration();
@@ -794,7 +807,6 @@
}
private void handleIPv4Failure() {
- // TODO: Figure out to de-dup this and the same code in DhcpClient.
clearIPv4Address();
mDhcpResults = null;
final LinkProperties newLp = assembleLinkProperties();
@@ -944,7 +956,7 @@
// If we have a StaticIpConfiguration attempt to apply it and
// handle the result accordingly.
if (mConfiguration.mStaticIpConfig != null) {
- if (applyStaticIpConfig()) {
+ if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
} else {
if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
@@ -1050,6 +1062,21 @@
}
break;
+ case DhcpClient.CMD_CLEAR_LINKADDRESS:
+ clearIPv4Address();
+ break;
+
+ case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
+ final LinkAddress ipAddress = (LinkAddress) msg.obj;
+ if (setIPv4Address(ipAddress)) {
+ mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
+ } else {
+ Log.e(mTag, "Failed to set IPv4 address!");
+ transitionTo(mStoppingState);
+ }
+ break;
+ }
+
case DhcpClient.CMD_POST_DHCP_ACTION: {
// Note that onPostDhcpAction() is likely to be
// asynchronous, and thus there is no guarantee that we
@@ -1082,20 +1109,5 @@
}
return HANDLED;
}
-
- private boolean applyStaticIpConfig() {
- final InterfaceConfiguration ifcg = new InterfaceConfiguration();
- ifcg.setLinkAddress(mConfiguration.mStaticIpConfig.ipAddress);
- ifcg.setInterfaceUp();
- try {
- mNwService.setInterfaceConfig(mInterfaceName, ifcg);
- if (DBG) Log.d(mTag, "Static IP configuration succeeded");
- } catch (IllegalStateException | RemoteException e) {
- Log.e(mTag, "Static IP configuration failed: ", e);
- return false;
- }
-
- return true;
- }
}
}