diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index e0d8373fc445a..927f8f98284fc 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1616,6 +1616,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); if (mNotifyList.indexOf(who) < 0) { mNotifyList.add(who); + mIPv6TetheringCoordinator.addActiveDownstream(who); } transitionTo(mTetherModeAliveState); break; @@ -1623,6 +1624,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); mNotifyList.remove(who); + mIPv6TetheringCoordinator.removeActiveDownstream(who); break; default: retValue = false; @@ -1661,17 +1663,19 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering maybeLogMessage(this, message.what); boolean retValue = true; switch (message.what) { - case CMD_TETHER_MODE_REQUESTED: + case CMD_TETHER_MODE_REQUESTED: { TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); if (mNotifyList.indexOf(who) < 0) { mNotifyList.add(who); + mIPv6TetheringCoordinator.addActiveDownstream(who); } who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, mCurrentUpstreamIface); break; - case CMD_TETHER_MODE_UNREQUESTED: - who = (TetherInterfaceStateMachine)message.obj; + } + case CMD_TETHER_MODE_UNREQUESTED: { + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); if (mNotifyList.remove(who)) { if (DBG) Log.d(TAG, "TetherModeAlive removing notifyee " + who); @@ -1689,7 +1693,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } else { Log.e(TAG, "TetherModeAliveState UNREQUESTED has unknown who: " + who); } + mIPv6TetheringCoordinator.removeActiveDownstream(who); break; + } case CMD_UPSTREAM_CHANGED: // need to try DUN immediately if Wifi goes down mTryCell = true; diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java index e94b584ac48aa..9173febd0ef3f 100644 --- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java +++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java @@ -29,6 +29,7 @@ import android.util.Log; import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.LinkedList; /** @@ -45,10 +46,28 @@ public class IPv6TetheringCoordinator { private static final boolean VDBG = false; private final ArrayList mNotifyList; + private final LinkedList mActiveDownstreams; private NetworkState mUpstreamNetworkState; public IPv6TetheringCoordinator(ArrayList notifyList) { mNotifyList = notifyList; + mActiveDownstreams = new LinkedList<>(); + } + + public void addActiveDownstream(TetherInterfaceStateMachine downstream) { + if (mActiveDownstreams.indexOf(downstream) == -1) { + // Adding a new downstream appends it to the list. Adding a + // downstream a second time without first removing it has no effect. + mActiveDownstreams.offer(downstream); + updateIPv6TetheringInterfaces(); + } + } + + public void removeActiveDownstream(TetherInterfaceStateMachine downstream) { + stopIPv6TetheringOn(downstream); + if (mActiveDownstreams.remove(downstream)) { + updateIPv6TetheringInterfaces(); + } } public void updateUpstreamNetworkState(NetworkState ns) { @@ -72,8 +91,7 @@ public class IPv6TetheringCoordinator { private void stopIPv6TetheringOnAllInterfaces() { for (TetherInterfaceStateMachine sm : mNotifyList) { - sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, - 0, 0, null); + stopIPv6TetheringOn(sm); } } @@ -98,28 +116,32 @@ public class IPv6TetheringCoordinator { private void updateIPv6TetheringInterfaces() { for (TetherInterfaceStateMachine sm : mNotifyList) { - final LinkProperties lp = getInterfaceIPv6LinkProperties(sm.interfaceType()); + final LinkProperties lp = getInterfaceIPv6LinkProperties(sm); sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp); break; } } - private LinkProperties getInterfaceIPv6LinkProperties(int interfaceType) { + private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) { if (mUpstreamNetworkState == null) return null; + if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) { + // TODO: Figure out IPv6 support on PAN interfaces. + return null; + } + // NOTE: Here, in future, we would have policies to decide how to divvy // up the available dedicated prefixes among downstream interfaces. // At this time we have no such mechanism--we only support tethering - // IPv6 toward Wi-Fi interfaces. + // IPv6 toward the oldest (first requested) active downstream. - switch (interfaceType) { - case ConnectivityManager.TETHERING_WIFI: - final LinkProperties lp = getIPv6OnlyLinkProperties( - mUpstreamNetworkState.linkProperties); - if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) { - return lp; - } - break; + final TetherInterfaceStateMachine currentActive = mActiveDownstreams.peek(); + if (currentActive != null && currentActive == sm) { + final LinkProperties lp = getIPv6OnlyLinkProperties( + mUpstreamNetworkState.linkProperties); + if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) { + return lp; + } } return null; @@ -250,4 +272,8 @@ public class IPv6TetheringCoordinator { ns.networkCapabilities, ns.linkProperties); } + + private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) { + sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + } }