Initial refactoring to group IP-related elements into an IpManager
Bug: 17345682 Change-Id: I88f3f4bd32d18cd8d4f1404493648c8bcc1deeec
This commit is contained in:
461
services/net/java/android/net/ip/IpManager.java
Normal file
461
services/net/java/android/net/ip/IpManager.java
Normal file
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net.ip;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.DhcpResults;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.LinkProperties.ProvisioningChange;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.StaticIpConfiguration;
|
||||
import android.os.INetworkManagementService;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.State;
|
||||
import com.android.internal.util.StateMachine;
|
||||
import com.android.server.net.NetlinkTracker;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
|
||||
|
||||
/**
|
||||
* IpManager
|
||||
*
|
||||
* This class provides the interface to IP-layer provisioning and maintenance
|
||||
* functionality that can be used by transport layers like Wi-Fi, Ethernet,
|
||||
* et cetera.
|
||||
*
|
||||
* [ Lifetime ]
|
||||
* IpManager is designed to be instantiated as soon as the interface name is
|
||||
* known and can be as long-lived as the class containing it (i.e. declaring
|
||||
* it "private final" is okay).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class IpManager extends StateMachine {
|
||||
private static final String TAG = IpManager.class.getSimpleName();
|
||||
private static final boolean DBG = true;
|
||||
private static final boolean VDBG = false;
|
||||
|
||||
/**
|
||||
* Callbacks for both configuration of IpManager and for handling
|
||||
* events as desired.
|
||||
*/
|
||||
public static class Callback {
|
||||
/**
|
||||
* Configuration callbacks.
|
||||
*
|
||||
* Override methods as desired in order to control which features
|
||||
* IpManager will use at run time.
|
||||
*/
|
||||
|
||||
// An IpReachabilityMonitor will always be started, if only for logging.
|
||||
// This method is checked before probing neighbors and before calling
|
||||
// onProvisioningLost() (see below).
|
||||
public boolean usingIpReachabilityMonitor() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event callbacks.
|
||||
*
|
||||
* Override methods as desired in order to handle event callbacks
|
||||
* as IpManager invokes them.
|
||||
*/
|
||||
|
||||
// TODO: Kill with fire once DHCP and static configuration are moved
|
||||
// out of WifiStateMachine.
|
||||
public void onIPv4ProvisioningSuccess(DhcpResults dhcpResults, int reason) {}
|
||||
public void onIPv4ProvisioningFailure(int reason) {}
|
||||
|
||||
public void onProvisioningSuccess(LinkProperties newLp) {}
|
||||
public void onProvisioningFailure(LinkProperties newLp) {}
|
||||
|
||||
// Invoked on LinkProperties changes.
|
||||
public void onLinkPropertiesChange(LinkProperties newLp) {}
|
||||
|
||||
// Called when the internal IpReachabilityMonitor (if enabled) has
|
||||
// detected the loss of a critical number of required neighbors.
|
||||
public void onReachabilityLost(String logMsg) {}
|
||||
}
|
||||
|
||||
private static final int CMD_STOP = 1;
|
||||
private static final int CMD_START = 2;
|
||||
private static final int CMD_CONFIRM = 3;
|
||||
private static final int CMD_UPDATE_DHCPV4_RESULTS = 4;
|
||||
// Sent by NetlinkTracker to communicate netlink events.
|
||||
private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 5;
|
||||
|
||||
private static final int MAX_LOG_RECORDS = 1000;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final State mStoppedState = new StoppedState();
|
||||
private final State mStartedState = new StartedState();
|
||||
|
||||
private final Context mContext;
|
||||
private final String mInterfaceName;
|
||||
@VisibleForTesting
|
||||
protected final Callback mCallback;
|
||||
private final INetworkManagementService mNwService;
|
||||
private final NetlinkTracker mNetlinkTracker;
|
||||
|
||||
private int mInterfaceIndex;
|
||||
|
||||
/**
|
||||
* Non-final member variables accessed only from within our StateMachine.
|
||||
*/
|
||||
private IpReachabilityMonitor mIpReachabilityMonitor;
|
||||
private DhcpResults mDhcpResults;
|
||||
private StaticIpConfiguration mStaticIpConfig;
|
||||
|
||||
/**
|
||||
* Member variables accessed both from within the StateMachine thread
|
||||
* and via accessors from other threads.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
private LinkProperties mLinkProperties;
|
||||
|
||||
public IpManager(Context context, String ifName, Callback callback)
|
||||
throws IllegalArgumentException {
|
||||
super(TAG + "." + ifName);
|
||||
|
||||
mContext = context;
|
||||
mInterfaceName = ifName;
|
||||
|
||||
mCallback = callback;
|
||||
|
||||
mNwService = INetworkManagementService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
|
||||
|
||||
mNetlinkTracker = new NetlinkTracker(
|
||||
mInterfaceName,
|
||||
new NetlinkTracker.Callback() {
|
||||
@Override
|
||||
public void update() {
|
||||
sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED);
|
||||
}
|
||||
});
|
||||
try {
|
||||
mNwService.registerObserver(mNetlinkTracker);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Couldn't register NetlinkTracker: " + e.toString());
|
||||
}
|
||||
|
||||
resetLinkProperties();
|
||||
|
||||
// Super simple StateMachine.
|
||||
addState(mStoppedState);
|
||||
addState(mStartedState);
|
||||
setInitialState(mStoppedState);
|
||||
setLogRecSize(MAX_LOG_RECORDS);
|
||||
super.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* A special constructor for use in testing that bypasses some of the more
|
||||
* complicated setup bits.
|
||||
*
|
||||
* TODO: Figure out how to delete this yet preserve testability.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected IpManager(String ifName, Callback callback) {
|
||||
super(TAG + ".test-" + ifName);
|
||||
mInterfaceName = ifName;
|
||||
mCallback = callback;
|
||||
|
||||
mContext = null;
|
||||
mNwService = null;
|
||||
mNetlinkTracker = null;
|
||||
}
|
||||
|
||||
public void startProvisioning(StaticIpConfiguration staticIpConfig) {
|
||||
getInterfaceIndex();
|
||||
|
||||
sendMessage(CMD_START, staticIpConfig);
|
||||
}
|
||||
|
||||
public void startProvisioning() {
|
||||
getInterfaceIndex();
|
||||
|
||||
sendMessage(CMD_START);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
sendMessage(CMD_STOP);
|
||||
}
|
||||
|
||||
public void confirmConfiguration() {
|
||||
sendMessage(CMD_CONFIRM);
|
||||
}
|
||||
|
||||
public LinkProperties getLinkProperties() {
|
||||
synchronized (mLock) {
|
||||
return new LinkProperties(mLinkProperties);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Kill with fire once DHCPv4/static config is moved into IpManager.
|
||||
public void updateWithDhcpResults(DhcpResults dhcpResults, int reason) {
|
||||
sendMessage(CMD_UPDATE_DHCPV4_RESULTS, reason, 0, dhcpResults);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internals.
|
||||
*/
|
||||
|
||||
private void getInterfaceIndex() {
|
||||
try {
|
||||
mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex();
|
||||
} catch (SocketException | NullPointerException e) {
|
||||
// TODO: throw new IllegalStateException.
|
||||
Log.e(TAG, "ALERT: Failed to get interface index: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
// This needs to be called with care to ensure that our LinkProperties
|
||||
// are in sync with the actual LinkProperties of the interface. For example,
|
||||
// we should only call this if we know for sure that there are no IP addresses
|
||||
// assigned to the interface, etc.
|
||||
private void resetLinkProperties() {
|
||||
mNetlinkTracker.clearLinkProperties();
|
||||
mDhcpResults = null;
|
||||
mStaticIpConfig = null;
|
||||
|
||||
synchronized (mLock) {
|
||||
mLinkProperties = new LinkProperties();
|
||||
mLinkProperties.setInterfaceName(mInterfaceName);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
mLinkProperties = new LinkProperties(newLp);
|
||||
}
|
||||
|
||||
if (DBG) {
|
||||
switch (delta) {
|
||||
case GAINED_PROVISIONING:
|
||||
case LOST_PROVISIONING:
|
||||
Log.d(TAG, "provisioning: " + delta);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
private LinkProperties assembleLinkProperties() {
|
||||
// [1] Create a new LinkProperties object to populate.
|
||||
LinkProperties newLp = new LinkProperties();
|
||||
newLp.setInterfaceName(mInterfaceName);
|
||||
|
||||
// [2] Pull in data from netlink:
|
||||
// - IPv4 addresses
|
||||
// - IPv6 addresses
|
||||
// - IPv6 routes
|
||||
// - IPv6 DNS servers
|
||||
LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
|
||||
newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
|
||||
for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
|
||||
newLp.addRoute(route);
|
||||
}
|
||||
for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
|
||||
// Only add likely reachable DNS servers.
|
||||
// TODO: investigate deleting this.
|
||||
if (newLp.isReachable(dns)) {
|
||||
newLp.addDnsServer(dns);
|
||||
}
|
||||
}
|
||||
|
||||
// [3] Add in data from DHCPv4, if available.
|
||||
//
|
||||
// mDhcpResults is never shared with any other owner so we don't have
|
||||
// to worry about concurrent modification.
|
||||
if (mDhcpResults != null) {
|
||||
for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
|
||||
newLp.addRoute(route);
|
||||
}
|
||||
for (InetAddress dns : mDhcpResults.dnsServers) {
|
||||
// Only add likely reachable DNS servers.
|
||||
// TODO: investigate deleting this.
|
||||
if (newLp.isReachable(dns)) {
|
||||
newLp.addDnsServer(dns);
|
||||
}
|
||||
}
|
||||
newLp.setDomains(mDhcpResults.domains);
|
||||
}
|
||||
|
||||
if (VDBG) {
|
||||
Log.d(TAG, "newLp{" + newLp + "}");
|
||||
}
|
||||
|
||||
return newLp;
|
||||
}
|
||||
|
||||
class StoppedState extends State {
|
||||
@Override
|
||||
public void enter() {
|
||||
try {
|
||||
mNwService.disableIpv6(mInterfaceName);
|
||||
mNwService.clearInterfaceAddresses(mInterfaceName);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to clear addresses or disable IPv6" + e);
|
||||
}
|
||||
|
||||
resetLinkProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case CMD_STOP:
|
||||
break;
|
||||
|
||||
case CMD_START:
|
||||
mStaticIpConfig = (StaticIpConfiguration) msg.obj;
|
||||
transitionTo(mStartedState);
|
||||
break;
|
||||
|
||||
case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
|
||||
setLinkProperties(assembleLinkProperties());
|
||||
break;
|
||||
|
||||
default:
|
||||
return NOT_HANDLED;
|
||||
}
|
||||
return HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
class StartedState extends State {
|
||||
@Override
|
||||
public void enter() {
|
||||
// Set privacy extensions.
|
||||
try {
|
||||
mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
|
||||
mNwService.enableIpv6(mInterfaceName);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(TAG, "Unable to change interface settings: " + re);
|
||||
} catch (IllegalStateException ie) {
|
||||
Log.e(TAG, "Unable to change interface settings: " + ie);
|
||||
}
|
||||
|
||||
mIpReachabilityMonitor = new IpReachabilityMonitor(
|
||||
mContext,
|
||||
mInterfaceName,
|
||||
new IpReachabilityMonitor.Callback() {
|
||||
@Override
|
||||
public void notifyLost(InetAddress ip, String logMsg) {
|
||||
if (mCallback.usingIpReachabilityMonitor()) {
|
||||
mCallback.onReachabilityLost(logMsg);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Check mStaticIpConfig and handle accordingly.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit() {
|
||||
mIpReachabilityMonitor.stop();
|
||||
mIpReachabilityMonitor = null;
|
||||
|
||||
resetLinkProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case CMD_STOP:
|
||||
transitionTo(mStoppedState);
|
||||
break;
|
||||
|
||||
case CMD_START:
|
||||
// TODO: Defer this message to be delivered after a state transition
|
||||
// to StoppedState. That way, receiving CMD_START in StartedState
|
||||
// effects a restart.
|
||||
Log.e(TAG, "ALERT: START received in StartedState.");
|
||||
break;
|
||||
|
||||
case CMD_CONFIRM:
|
||||
if (mCallback.usingIpReachabilityMonitor()) {
|
||||
mIpReachabilityMonitor.probeAll();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_UPDATE_DHCPV4_RESULTS:
|
||||
final DhcpResults dhcpResults = (DhcpResults) msg.obj;
|
||||
final int reason = msg.arg1;
|
||||
if (dhcpResults != null) {
|
||||
mDhcpResults = new DhcpResults(dhcpResults);
|
||||
setLinkProperties(assembleLinkProperties());
|
||||
mCallback.onIPv4ProvisioningSuccess(dhcpResults, reason);
|
||||
} else {
|
||||
mDhcpResults = null;
|
||||
setLinkProperties(assembleLinkProperties());
|
||||
mCallback.onIPv4ProvisioningFailure(reason);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
|
||||
final LinkProperties newLp = assembleLinkProperties();
|
||||
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;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return NOT_HANDLED;
|
||||
}
|
||||
return HANDLED;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user