Merge "Fix listeners not getting called when AVD falls back on UI thread" into nyc-mr1-dev
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index e0d8373..927f8f9 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 @@
                         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 @@
                         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 @@
                 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 @@
                         } 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 e94b584..9173feb 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 java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.LinkedList;
 
 
 /**
@@ -45,10 +46,28 @@
     private static final boolean VDBG = false;
 
     private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+    private final LinkedList<TetherInterfaceStateMachine> mActiveDownstreams;
     private NetworkState mUpstreamNetworkState;
 
     public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> 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 @@
 
     private void stopIPv6TetheringOnAllInterfaces() {
         for (TetherInterfaceStateMachine sm : mNotifyList) {
-            sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE,
-                    0, 0, null);
+            stopIPv6TetheringOn(sm);
         }
     }
 
@@ -98,28 +116,32 @@
 
     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 @@
                 ns.networkCapabilities,
                 ns.linkProperties);
     }
+
+    private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
+        sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+    }
 }