summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2017-06-09 07:34:32 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-06-09 07:34:32 +0000
commit0c0c3442e3d8fd73792e7b8082daa14870874629 (patch)
tree99ba426ca60671ef3eca2e51b6ae331c1d9b4935
parent8e5d9d7c7bc0d0b2410848cf935c756051fb6fcb (diff)
parent3b31ff1943ee21dbf96fddf6ba8ffb00093a9c59 (diff)
downloadethernet-0c0c3442e3d8fd73792e7b8082daa14870874629.tar.gz
Make Ethernet more robust. am: 3c1204c1f3
am: 3b31ff1943 Change-Id: I4dd010c8e49ac2d603f7ed617209bdc3759680fd
-rw-r--r--java/com/android/server/ethernet/EthernetNetworkFactory.java454
1 files changed, 220 insertions, 234 deletions
diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java
index 0b091f2..7ff3f6c 100644
--- a/java/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -34,7 +34,6 @@ import android.net.NetworkInfo.DetailedState;
import android.net.StaticIpConfiguration;
import android.net.ip.IpManager;
import android.net.ip.IpManager.ProvisioningConfiguration;
-import android.net.ip.IpManager.WaitForProvisioningCallback;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -50,6 +49,8 @@ import com.android.server.net.BaseNetworkObserver;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
/**
@@ -84,6 +85,9 @@ class EthernetNetworkFactory {
/** To set link state and configure IP addresses. */
private INetworkManagementService mNMService;
+ /** All code runs here, including start(). */
+ private Handler mHandler;
+
/* To communicate with ConnectivityManager */
private NetworkCapabilities mNetworkCapabilities;
private NetworkAgent mNetworkAgent;
@@ -96,19 +100,18 @@ class EthernetNetworkFactory {
/** To notify Ethernet status. */
private final RemoteCallbackList<IEthernetServiceListener> mListeners;
- /** Data members. All accesses to these must be synchronized(this). */
- private static String mIface = "";
+ /** Data members. All accesses to these must be on the handler thread. */
+ private String mIface = "";
private String mHwAddr;
- private static boolean mLinkUp;
+ private boolean mLinkUp;
private NetworkInfo mNetworkInfo;
private LinkProperties mLinkProperties;
private IpManager mIpManager;
- private Thread mIpProvisioningThread;
+ private boolean mNetworkRequested = false;
EthernetNetworkFactory(RemoteCallbackList<IEthernetServiceListener> listeners) {
- mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, "");
- mLinkProperties = new LinkProperties();
initNetworkCapabilities();
+ clearInfo();
mListeners = listeners;
}
@@ -118,26 +121,40 @@ class EthernetNetworkFactory {
}
protected void startNetwork() {
- onRequestNetwork();
+ if (!mNetworkRequested) {
+ mNetworkRequested = true;
+ maybeStartIpManager();
+ }
}
+
protected void stopNetwork() {
+ mNetworkRequested = false;
+ stopIpManager();
}
}
- private void stopIpManagerLocked() {
+ private void clearInfo() {
+ mLinkProperties = new LinkProperties();
+ mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, "");
+ mNetworkInfo.setExtraInfo(mHwAddr);
+ mNetworkInfo.setIsAvailable(isTrackingInterface());
+ }
+
+ private void stopIpManager() {
if (mIpManager != null) {
mIpManager.shutdown();
mIpManager = null;
}
- }
-
- private void stopIpProvisioningThreadLocked() {
- stopIpManagerLocked();
-
- if (mIpProvisioningThread != null) {
- mIpProvisioningThread.interrupt();
- mIpProvisioningThread = null;
+ // ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object
+ // with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here:
+ // that sets the state to IDLE, and ConnectivityService will still think we're connected.
+ //
+ mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
+ if (mNetworkAgent != null) {
+ updateAgent();
+ mNetworkAgent = null;
}
+ clearInfo();
}
/**
@@ -150,36 +167,36 @@ class EthernetNetworkFactory {
}
Log.d(TAG, "updateInterface: " + iface + " link " + (up ? "up" : "down"));
- synchronized(this) {
- mLinkUp = up;
- mNetworkInfo.setIsAvailable(up);
- if (!up) {
- // Tell the agent we're disconnected. It will call disconnect().
- mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
- stopIpProvisioningThreadLocked();
- }
- updateAgent();
- // set our score lower than any network could go
- // so we get dropped. TODO - just unregister the factory
- // when link goes down.
- mFactory.setScoreFilter(up ? NETWORK_SCORE : -1);
+ mLinkUp = up;
+ if (up) {
+ maybeStartIpManager();
+ } else {
+ stopIpManager();
}
}
private class InterfaceObserver extends BaseNetworkObserver {
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
- updateInterfaceState(iface, up);
+ mHandler.post(() -> {
+ updateInterfaceState(iface, up);
+ });
}
@Override
public void interfaceAdded(String iface) {
- maybeTrackInterface(iface);
+ mHandler.post(() -> {
+ maybeTrackInterface(iface);
+ });
}
@Override
public void interfaceRemoved(String iface) {
- stopTrackingInterface(iface);
+ mHandler.post(() -> {
+ if (stopTrackingInterface(iface)) {
+ trackFirstAvailableInterface();
+ }
+ });
}
}
@@ -195,15 +212,13 @@ class EthernetNetworkFactory {
return;
}
- synchronized (this) {
- if (!isTrackingInterface()) {
- setInterfaceInfoLocked(iface, config.getHardwareAddress());
- mNetworkInfo.setIsAvailable(true);
- mNetworkInfo.setExtraInfo(mHwAddr);
- } else {
- Log.e(TAG, "Interface unexpectedly changed from " + iface + " to " + mIface);
- mNMService.setInterfaceDown(iface);
- }
+ if (!isTrackingInterface()) {
+ setInterfaceInfo(iface, config.getHardwareAddress());
+ mNetworkInfo.setIsAvailable(true);
+ mNetworkInfo.setExtraInfo(mHwAddr);
+ } else {
+ Log.e(TAG, "Interface unexpectedly changed from " + iface + " to " + mIface);
+ mNMService.setInterfaceDown(iface);
}
} catch (RemoteException e) {
Log.e(TAG, "Error upping interface " + mIface + ": " + e);
@@ -221,23 +236,14 @@ class EthernetNetworkFactory {
return true;
}
- private void stopTrackingInterface(String iface) {
+ private boolean stopTrackingInterface(String iface) {
if (!iface.equals(mIface))
- return;
+ return false;
Log.d(TAG, "Stopped tracking interface " + iface);
- // TODO: Unify this codepath with stop().
- synchronized (this) {
- stopIpProvisioningThreadLocked();
- setInterfaceInfoLocked("", null);
- mNetworkInfo.setExtraInfo(null);
- mLinkUp = false;
- mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
- updateAgent();
- mNetworkAgent = null;
- mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, "");
- mLinkProperties = new LinkProperties();
- }
+ setInterfaceInfo("", null);
+ stopIpManager();
+ return true;
}
private boolean setStaticIpAddress(StaticIpConfiguration staticConfig) {
@@ -260,156 +266,127 @@ class EthernetNetworkFactory {
}
public void updateAgent() {
- synchronized (EthernetNetworkFactory.this) {
- if (mNetworkAgent == null) return;
- if (DBG) {
- Log.i(TAG, "Updating mNetworkAgent with: " +
- mNetworkCapabilities + ", " +
- mNetworkInfo + ", " +
- mLinkProperties);
- }
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
- mNetworkAgent.sendLinkProperties(mLinkProperties);
- // never set the network score below 0.
- mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0);
+ if (mNetworkAgent == null) return;
+ if (DBG) {
+ Log.i(TAG, "Updating mNetworkAgent with: " +
+ mNetworkCapabilities + ", " +
+ mNetworkInfo + ", " +
+ mLinkProperties);
}
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ mNetworkAgent.sendLinkProperties(mLinkProperties);
+ // never set the network score below 0.
+ mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0);
}
- /* Called by the NetworkFactory on the handler thread. */
- public void onRequestNetwork() {
- synchronized(EthernetNetworkFactory.this) {
- if (mIpProvisioningThread != null) {
- return;
+ void onIpLayerStarted(LinkProperties linkProperties) {
+ if (mNetworkAgent != null) {
+ Log.e(TAG, "Already have a NetworkAgent - aborting new request");
+ stopIpManager();
+ return;
+ }
+ mLinkProperties = linkProperties;
+ mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
+
+ // Create our NetworkAgent.
+ mNetworkAgent = new NetworkAgent(mHandler.getLooper(), mContext,
+ NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties,
+ NETWORK_SCORE) {
+ public void unwanted() {
+ if (this == mNetworkAgent) {
+ stopIpManager();
+ } else if (mNetworkAgent != null) {
+ Log.d(TAG, "Ignoring unwanted as we have a more modern " +
+ "instance");
+ } // Otherwise, we've already called stopIpManager.
}
+ };
+ }
+
+ void onIpLayerStopped(LinkProperties linkProperties) {
+ // This cannot happen due to provisioning timeout, because our timeout is 0. It can only
+ // happen if we're provisioned and we lose provisioning.
+ stopIpManager();
+ maybeStartIpManager();
+ }
+
+ void updateLinkProperties(LinkProperties linkProperties) {
+ mLinkProperties = linkProperties;
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendLinkProperties(linkProperties);
}
+ }
- final Thread ipProvisioningThread = new Thread(new Runnable() {
- public void run() {
- if (DBG) {
- Log.d(TAG, String.format("starting ipProvisioningThread(%s): mNetworkInfo=%s",
- mIface, mNetworkInfo));
- }
+ public void maybeStartIpManager() {
+ if (mNetworkRequested && mIpManager == null && isTrackingInterface()) {
+ startIpManager();
+ }
+ }
- LinkProperties linkProperties;
+ public void startIpManager() {
+ if (DBG) {
+ Log.d(TAG, String.format("starting IpManager(%s): mNetworkInfo=%s", mIface,
+ mNetworkInfo));
+ }
- IpConfiguration config = mEthernetManager.getConfiguration();
+ LinkProperties linkProperties;
- if (config.getIpAssignment() == IpAssignment.STATIC) {
- if (!setStaticIpAddress(config.getStaticIpConfiguration())) {
- // We've already logged an error.
- return;
- }
- linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface);
- } else {
- mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
- WaitForProvisioningCallback ipmCallback = new WaitForProvisioningCallback() {
- @Override
- public void onLinkPropertiesChange(LinkProperties newLp) {
- synchronized(EthernetNetworkFactory.this) {
- if (mNetworkAgent != null && mNetworkInfo.isConnected()) {
- mLinkProperties = newLp;
- mNetworkAgent.sendLinkProperties(newLp);
- }
- }
- }
- };
-
- synchronized(EthernetNetworkFactory.this) {
- stopIpManagerLocked();
- mIpManager = new IpManager(mContext, mIface, ipmCallback);
-
- if (config.getProxySettings() == ProxySettings.STATIC ||
- config.getProxySettings() == ProxySettings.PAC) {
- mIpManager.setHttpProxy(config.getHttpProxy());
- }
-
- final String tcpBufferSizes = mContext.getResources().getString(
- com.android.internal.R.string.config_ethernet_tcp_buffers);
- if (!TextUtils.isEmpty(tcpBufferSizes)) {
- mIpManager.setTcpBufferSizes(tcpBufferSizes);
- }
-
- final ProvisioningConfiguration provisioningConfiguration =
- mIpManager.buildProvisioningConfiguration()
- .withProvisioningTimeoutMs(0)
- .build();
- mIpManager.startProvisioning(provisioningConfiguration);
- }
+ IpConfiguration config = mEthernetManager.getConfiguration();
- linkProperties = ipmCallback.waitForProvisioning();
- if (linkProperties == null) {
- Log.e(TAG, "IP provisioning error");
- // set our score lower than any network could go
- // so we get dropped.
- mFactory.setScoreFilter(-1);
- synchronized(EthernetNetworkFactory.this) {
- stopIpManagerLocked();
- }
- return;
- }
+ if (config.getIpAssignment() == IpAssignment.STATIC) {
+ if (!setStaticIpAddress(config.getStaticIpConfiguration())) {
+ // We've already logged an error.
+ return;
+ }
+ linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface);
+ } else {
+ mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
+ IpManager.Callback ipmCallback = new IpManager.Callback() {
+ @Override
+ public void onProvisioningSuccess(LinkProperties newLp) {
+ mHandler.post(() -> onIpLayerStarted(newLp));
}
- synchronized(EthernetNetworkFactory.this) {
- if (mNetworkAgent != null) {
- Log.e(TAG, "Already have a NetworkAgent - aborting new request");
- stopIpManagerLocked();
- mIpProvisioningThread = null;
- return;
- }
- mLinkProperties = linkProperties;
- mNetworkInfo.setIsAvailable(true);
- mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
-
- // Create our NetworkAgent.
- mNetworkAgent = new NetworkAgent(mFactory.getLooper(), mContext,
- NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties,
- NETWORK_SCORE) {
- public void unwanted() {
- synchronized(EthernetNetworkFactory.this) {
- if (this == mNetworkAgent) {
- stopIpManagerLocked();
-
- mLinkProperties.clear();
- mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null,
- mHwAddr);
- updateAgent();
- mNetworkAgent = null;
- try {
- mNMService.clearInterfaceAddresses(mIface);
- } catch (Exception e) {
- Log.e(TAG, "Failed to clear addresses or disable ipv6" + e);
- }
- } else {
- Log.d(TAG, "Ignoring unwanted as we have a more modern " +
- "instance");
- }
- }
- };
- };
-
- mIpProvisioningThread = null;
+ @Override
+ public void onProvisioningFailure(LinkProperties newLp) {
+ mHandler.post(() -> onIpLayerStopped(newLp));
}
- if (DBG) {
- Log.d(TAG, String.format("exiting ipProvisioningThread(%s): mNetworkInfo=%s",
- mIface, mNetworkInfo));
+ @Override
+ public void onLinkPropertiesChange(LinkProperties newLp) {
+ mHandler.post(() -> updateLinkProperties(newLp));
}
+ };
+
+ stopIpManager();
+ mIpManager = new IpManager(mContext, mIface, ipmCallback);
+
+ if (config.getProxySettings() == ProxySettings.STATIC ||
+ config.getProxySettings() == ProxySettings.PAC) {
+ mIpManager.setHttpProxy(config.getHttpProxy());
}
- });
- synchronized(EthernetNetworkFactory.this) {
- if (mIpProvisioningThread == null) {
- mIpProvisioningThread = ipProvisioningThread;
- mIpProvisioningThread.start();
+ final String tcpBufferSizes = mContext.getResources().getString(
+ com.android.internal.R.string.config_ethernet_tcp_buffers);
+ if (!TextUtils.isEmpty(tcpBufferSizes)) {
+ mIpManager.setTcpBufferSizes(tcpBufferSizes);
}
+
+ final ProvisioningConfiguration provisioningConfiguration =
+ mIpManager.buildProvisioningConfiguration()
+ .withProvisioningTimeoutMs(0)
+ .build();
+ mIpManager.startProvisioning(provisioningConfiguration);
}
}
/**
* Begin monitoring connectivity
*/
- public synchronized void start(Context context, Handler target) {
+ public void start(Context context, Handler handler) {
+ mHandler = handler;
+
// The services we use.
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNMService = INetworkManagementService.Stub.asInterface(b);
@@ -420,9 +397,9 @@ class EthernetNetworkFactory {
com.android.internal.R.string.config_ethernet_iface_regex);
// Create and register our NetworkFactory.
- mFactory = new LocalNetworkFactory(NETWORK_TYPE, context, target.getLooper());
+ mFactory = new LocalNetworkFactory(NETWORK_TYPE, context, mHandler.getLooper());
mFactory.setCapabilityFilter(mNetworkCapabilities);
- mFactory.setScoreFilter(-1); // this set high when we have an iface
+ mFactory.setScoreFilter(NETWORK_SCORE);
mFactory.register();
mContext = context;
@@ -437,23 +414,22 @@ class EthernetNetworkFactory {
// If an Ethernet interface is already connected, start tracking that.
// Otherwise, the first Ethernet interface to appear will be tracked.
+ mHandler.post(() -> trackFirstAvailableInterface());
+ }
+
+ public void trackFirstAvailableInterface() {
try {
final String[] ifaces = mNMService.listInterfaces();
for (String iface : ifaces) {
- synchronized(this) {
- if (maybeTrackInterface(iface)) {
- // We have our interface. Track it.
- // Note: if the interface already has link (e.g., if we
- // crashed and got restarted while it was running),
- // we need to fake a link up notification so we start
- // configuring it. Since we're already holding the lock,
- // any real link up/down notification will only arrive
- // after we've done this.
- if (mNMService.getInterfaceConfig(iface).hasFlag("running")) {
- updateInterfaceState(iface, true);
- }
- break;
+ if (maybeTrackInterface(iface)) {
+ // We have our interface. Track it.
+ // Note: if the interface already has link (e.g., if we crashed and got
+ // restarted while it was running), we need to fake a link up notification so we
+ // start configuring it.
+ if (mNMService.getInterfaceConfig(iface).hasFlag("running")) {
+ updateInterfaceState(iface, true);
}
+ break;
}
}
} catch (RemoteException|IllegalStateException e) {
@@ -461,21 +437,9 @@ class EthernetNetworkFactory {
}
}
- public synchronized void stop() {
- stopIpProvisioningThreadLocked();
- // ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object
- // with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here:
- // that sets the state to IDLE, and ConnectivityService will still think we're connected.
- //
- // TODO: stop using explicit comparisons to DISCONNECTED / SUSPENDED in ConnectivityService,
- // and instead use isConnectedOrConnecting().
- mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
- mLinkUp = false;
- updateAgent();
- mLinkProperties = new LinkProperties();
- mNetworkAgent = null;
- setInterfaceInfoLocked("", null);
- mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, "");
+ public void stop() {
+ stopIpManager();
+ setInterfaceInfo("", null);
mFactory.unregister();
}
@@ -489,20 +453,22 @@ class EthernetNetworkFactory {
mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100 * 1000);
}
- public synchronized boolean isTrackingInterface() {
+ public boolean isTrackingInterface() {
return !TextUtils.isEmpty(mIface);
}
/**
* Set interface information and notify listeners if availability is changed.
- * This should be called with the lock held.
*/
- private void setInterfaceInfoLocked(String iface, String hwAddr) {
+ private void setInterfaceInfo(String iface, String hwAddr) {
boolean oldAvailable = isTrackingInterface();
mIface = iface;
mHwAddr = hwAddr;
boolean available = isTrackingInterface();
+ mNetworkInfo.setExtraInfo(mHwAddr);
+ mNetworkInfo.setIsAvailable(available);
+
if (oldAvailable != available) {
int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
@@ -516,26 +482,46 @@ class EthernetNetworkFactory {
}
}
- synchronized void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
- if (isTrackingInterface()) {
- pw.println("Tracking interface: " + mIface);
- pw.increaseIndent();
- pw.println("MAC address: " + mHwAddr);
- pw.println("Link state: " + (mLinkUp ? "up" : "down"));
- pw.decreaseIndent();
- } else {
- pw.println("Not tracking any interface");
- }
+ private void postAndWaitForRunnable(Runnable r) throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ mHandler.post(() -> {
+ try {
+ r.run();
+ } finally {
+ latch.countDown();
+ }
+ });
+ latch.await();
+ }
- pw.println();
- pw.println("NetworkInfo: " + mNetworkInfo);
- pw.println("LinkProperties: " + mLinkProperties);
- pw.println("NetworkAgent: " + mNetworkAgent);
- if (mIpManager != null) {
- pw.println("IpManager:");
- pw.increaseIndent();
- mIpManager.dump(fd, pw, args);
- pw.decreaseIndent();
+
+ void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
+ try {
+ postAndWaitForRunnable(() -> {
+ pw.println("Network Requested: " + mNetworkRequested);
+ if (isTrackingInterface()) {
+ pw.println("Tracking interface: " + mIface);
+ pw.increaseIndent();
+ pw.println("MAC address: " + mHwAddr);
+ pw.println("Link state: " + (mLinkUp ? "up" : "down"));
+ pw.decreaseIndent();
+ } else {
+ pw.println("Not tracking any interface");
+ }
+
+ pw.println();
+ pw.println("NetworkInfo: " + mNetworkInfo);
+ pw.println("LinkProperties: " + mLinkProperties);
+ pw.println("NetworkAgent: " + mNetworkAgent);
+ if (mIpManager != null) {
+ pw.println("IpManager:");
+ pw.increaseIndent();
+ mIpManager.dump(fd, pw, args);
+ pw.decreaseIndent();
+ }
+ });
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("dump() interrupted");
}
}
}