diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java index 847a224e90..b5f72301f1 100644 --- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java +++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java @@ -66,7 +66,6 @@ import android.annotation.RequiresPermission; import android.annotation.TargetApi; import android.content.Context; import android.net.ConnectivityManager; -import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LocalNetworkConfig; @@ -79,7 +78,6 @@ import android.net.NetworkCapabilities; import android.net.NetworkProvider; import android.net.NetworkRequest; import android.net.NetworkScore; -import android.net.RouteInfo; import android.net.TestNetworkSpecifier; import android.net.thread.ActiveOperationalDataset; import android.net.thread.ActiveOperationalDataset.SecurityPolicy; @@ -151,7 +149,6 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub private final ConnectivityManager mConnectivityManager; private final TunInterfaceController mTunIfController; private final InfraInterfaceController mInfraIfController; - private final LinkProperties mLinkProperties = new LinkProperties(); private final OtDaemonCallbackProxy mOtDaemonCallbackProxy = new OtDaemonCallbackProxy(); // TODO(b/308310823): read supported channel from Thread dameon @@ -295,12 +292,16 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub return mOtDaemon; } - // TODO(b/309792480): restarts the OT daemon service private void onOtDaemonDied() { - Log.w(TAG, "OT daemon became dead, clean up..."); + checkOnHandlerThread(); + Log.w(TAG, "OT daemon is dead, clean up and restart it..."); + OperationReceiverWrapper.onOtDaemonDied(); mOtDaemonCallbackProxy.onOtDaemonDied(); + mTunIfController.onOtDaemonDied(); + mOtDaemon = null; + initializeOtDaemon(); } public void initialize() { @@ -313,8 +314,6 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub throw new IllegalStateException( "Failed to create Thread tunnel interface", e); } - mLinkProperties.setInterfaceName(TUN_IF_NAME); - mLinkProperties.setMtu(TunInterfaceController.MTU); mConnectivityManager.registerNetworkProvider(mNetworkProvider); requestUpstreamNetwork(); requestThreadNetwork(); @@ -465,7 +464,7 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub mHandler.getLooper(), TAG, netCaps, - mLinkProperties, + mTunIfController.getLinkProperties(), newLocalNetworkConfig(), score, new NetworkAgentConfig.Builder().build(), @@ -496,46 +495,6 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub mNetworkAgent = null; } - private void updateTunInterfaceAddress(LinkAddress linkAddress, boolean isAdded) { - try { - if (isAdded) { - mTunIfController.addAddress(linkAddress); - } else { - mTunIfController.removeAddress(linkAddress); - } - } catch (IOException e) { - Log.e( - TAG, - String.format( - "Failed to %s Thread tun interface address %s", - (isAdded ? "add" : "remove"), linkAddress), - e); - } - } - - private void updateNetworkLinkProperties(LinkAddress linkAddress, boolean isAdded) { - RouteInfo routeInfo = - new RouteInfo( - new IpPrefix(linkAddress.getAddress(), 64), - null, - TUN_IF_NAME, - RouteInfo.RTN_UNICAST, - TunInterfaceController.MTU); - if (isAdded) { - mLinkProperties.addLinkAddress(linkAddress); - mLinkProperties.addRoute(routeInfo); - } else { - mLinkProperties.removeLinkAddress(linkAddress); - mLinkProperties.removeRoute(routeInfo); - } - - // The Thread daemon can send link property updates before the networkAgent is - // registered - if (mNetworkAgent != null) { - mNetworkAgent.sendLinkProperties(mLinkProperties); - } - } - @Override public int getThreadVersion() { return THREAD_VERSION_1_3; @@ -872,7 +831,7 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub private void handleThreadInterfaceStateChanged(boolean isUp) { try { mTunIfController.setInterfaceUp(isUp); - Log.d(TAG, "Thread network interface becomes " + (isUp ? "up" : "down")); + Log.i(TAG, "Thread TUN interface becomes " + (isUp ? "up" : "down")); } catch (IOException e) { Log.e(TAG, "Failed to handle Thread interface state changes", e); } @@ -880,13 +839,13 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub private void handleDeviceRoleChanged(@DeviceRole int deviceRole) { if (ThreadNetworkController.isAttached(deviceRole)) { - Log.d(TAG, "Attached to the Thread network"); + Log.i(TAG, "Attached to the Thread network"); // This is an idempotent method which can be called for multiple times when the device // is already attached (e.g. going from Child to Router) registerThreadNetwork(); } else { - Log.d(TAG, "Detached from the Thread network"); + Log.i(TAG, "Detached from the Thread network"); // This is an idempotent method which can be called for multiple times when the device // is already detached or stopped @@ -903,10 +862,17 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub } LinkAddress linkAddress = newLinkAddress(addressInfo); - Log.d(TAG, (isAdded ? "Adding" : "Removing") + " address " + linkAddress); + if (isAdded) { + mTunIfController.addAddress(linkAddress); + } else { + mTunIfController.removeAddress(linkAddress); + } - updateTunInterfaceAddress(linkAddress, isAdded); - updateNetworkLinkProperties(linkAddress, isAdded); + // The OT daemon can send link property updates before the networkAgent is + // registered + if (mNetworkAgent != null) { + mNetworkAgent.sendLinkProperties(mTunIfController.getLinkProperties()); + } } private boolean isMulticastForwardingEnabled() { diff --git a/thread/service/java/com/android/server/thread/TunInterfaceController.java b/thread/service/java/com/android/server/thread/TunInterfaceController.java index 7223b2af9e..b29a54f03a 100644 --- a/thread/service/java/com/android/server/thread/TunInterfaceController.java +++ b/thread/service/java/com/android/server/thread/TunInterfaceController.java @@ -17,7 +17,10 @@ package com.android.server.thread; import android.annotation.Nullable; +import android.net.IpPrefix; import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.RouteInfo; import android.net.util.SocketUtils; import android.os.ParcelFileDescriptor; import android.os.SystemClock; @@ -31,6 +34,7 @@ import com.android.net.module.util.netlink.RtNetlinkAddressMessage; import java.io.FileDescriptor; import java.io.IOException; +import java.io.InterruptedIOException; /** Controller for virtual/tunnel network interfaces. */ public class TunInterfaceController { @@ -43,13 +47,21 @@ public class TunInterfaceController { } private final String mIfName; + private final LinkProperties mLinkProperties = new LinkProperties(); private ParcelFileDescriptor mParcelTunFd; private FileDescriptor mNetlinkSocket; private static int sNetlinkSeqNo = 0; /** Creates a new {@link TunInterfaceController} instance for given interface. */ public TunInterfaceController(String interfaceName) { - this.mIfName = interfaceName; + mIfName = interfaceName; + mLinkProperties.setInterfaceName(mIfName); + mLinkProperties.setMtu(MTU); + } + + /** Returns link properties of the Thread TUN interface. */ + public LinkProperties getLinkProperties() { + return mLinkProperties; } /** @@ -87,13 +99,18 @@ public class TunInterfaceController { /** Sets the interface up or down according to {@code isUp}. */ public void setInterfaceUp(boolean isUp) throws IOException { + if (!isUp) { + for (LinkAddress address : mLinkProperties.getAllLinkAddresses()) { + removeAddress(address); + } + } nativeSetInterfaceUp(mIfName, isUp); } private native void nativeSetInterfaceUp(String interfaceName, boolean isUp) throws IOException; /** Adds a new address to the interface. */ - public void addAddress(LinkAddress address) throws IOException { + public void addAddress(LinkAddress address) { Log.d(TAG, "Adding address " + address + " with flags: " + address.getFlags()); long validLifetimeSeconds; @@ -121,7 +138,7 @@ public class TunInterfaceController { byte[] message = RtNetlinkAddressMessage.newRtmNewAddressMessage( - sNetlinkSeqNo, + sNetlinkSeqNo++, address.getAddress(), (short) address.getPrefixLength(), address.getFlags(), @@ -131,13 +148,51 @@ public class TunInterfaceController { preferredLifetimeSeconds); try { Os.write(mNetlinkSocket, message, 0, message.length); - } catch (ErrnoException e) { - throw new IOException("Failed to send netlink message", e); + } catch (ErrnoException | InterruptedIOException e) { + Log.e(TAG, "Failed to add address " + address, e); + return; } + mLinkProperties.addLinkAddress(address); + mLinkProperties.addRoute(getRouteForAddress(address)); } /** Removes an address from the interface. */ - public void removeAddress(LinkAddress address) throws IOException { - // TODO(b/263222068): remove address with netlink + public void removeAddress(LinkAddress address) { + Log.d(TAG, "Removing address " + address); + byte[] message = + RtNetlinkAddressMessage.newRtmDelAddressMessage( + sNetlinkSeqNo++, + address.getAddress(), + (short) address.getPrefixLength(), + Os.if_nametoindex(mIfName)); + + // Intentionally update the mLinkProperties before send netlink message because the + // address is already removed from ot-daemon and apps can't reach to the address even + // when the netlink request below fails + mLinkProperties.removeLinkAddress(address); + mLinkProperties.removeRoute(getRouteForAddress(address)); + try { + Os.write(mNetlinkSocket, message, 0, message.length); + } catch (ErrnoException | InterruptedIOException e) { + Log.e(TAG, "Failed to remove address " + address, e); + } + } + + private RouteInfo getRouteForAddress(LinkAddress linkAddress) { + return new RouteInfo( + new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength()), + null, + mIfName, + RouteInfo.RTN_UNICAST, + MTU); + } + + /** Called by {@link ThreadNetworkControllerService} to do clean up when ot-daemon is dead. */ + public void onOtDaemonDied() { + try { + setInterfaceUp(false); + } catch (IOException e) { + Log.e(TAG, "Failed to set Thread TUN interface down"); + } } } diff --git a/thread/tests/integration/Android.bp b/thread/tests/integration/Android.bp index 5989db589b..6ba192d882 100644 --- a/thread/tests/integration/Android.bp +++ b/thread/tests/integration/Android.bp @@ -24,12 +24,14 @@ java_defaults { min_sdk_version: "30", static_libs: [ "androidx.test.rules", + "compatibility-device-util-axt", "guava", "mockito-target-minus-junit4", "net-tests-utils", "net-utils-device-common", "net-utils-device-common-bpf", "testables", + "truth", ], libs: [ "android.test.runner", diff --git a/thread/tests/integration/AndroidTest.xml b/thread/tests/integration/AndroidTest.xml index ec9b5f39ed..152c1c39d4 100644 --- a/thread/tests/integration/AndroidTest.xml +++ b/thread/tests/integration/AndroidTest.xml @@ -31,6 +31,8 @@