Merge "Tethering: add isTetheringSupported with callerPkg parameter" into rvc-dev
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 3acc766..38f8609 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -810,7 +810,7 @@
                     rule.dstMac.toByteArray());
             mIpv6ForwardingRules.put(rule.address, rule);
         } catch (RemoteException | ServiceSpecificException e) {
-            Log.e(TAG, "Could not add IPv6 downstream rule: " + e);
+            mLog.e("Could not add IPv6 downstream rule: ", e);
         }
     }
 
@@ -821,10 +821,17 @@
                 mIpv6ForwardingRules.remove(rule.address);
             }
         } catch (RemoteException | ServiceSpecificException e) {
-            Log.e(TAG, "Could not remove IPv6 downstream rule: " + e);
+            mLog.e("Could not remove IPv6 downstream rule: ", e);
         }
     }
 
+    private void clearIpv6ForwardingRules() {
+        for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) {
+            removeIpv6ForwardingRule(rule, false /*removeFromMap*/);
+        }
+        mIpv6ForwardingRules.clear();
+    }
+
     // Convenience method to replace a rule with the same rule on a new upstream interface.
     // Allows replacing the rules in one iteration pass without ConcurrentModificationExceptions.
     // Relies on the fact that rules are in a map indexed by IP address.
@@ -837,6 +844,12 @@
     // changes or if a neighbor event is received.
     private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex,
             NeighborEvent e) {
+        // If we no longer have an upstream, clear forwarding rules and do nothing else.
+        if (upstreamIfindex == 0) {
+            clearIpv6ForwardingRules();
+            return;
+        }
+
         // If the upstream interface has changed, remove all rules and re-add them with the new
         // upstream interface.
         if (prevUpstreamIfindex != upstreamIfindex) {
@@ -846,13 +859,14 @@
         }
 
         // If we're here to process a NeighborEvent, do so now.
+        // mInterfaceParams must be non-null or the event would not have arrived.
         if (e == null) return;
         if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress()
                 || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) {
             return;
         }
 
-        Ipv6ForwardingRule rule = new Ipv6ForwardingRule(mLastIPv6UpstreamIfindex,
+        Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex,
                 mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr,
                 e.macAddr);
         if (e.isValid()) {
@@ -1095,6 +1109,7 @@
 
             for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
             mUpstreamIfaceSet = null;
+            clearIpv6ForwardingRules();
         }
 
         private void cleanupUpstreamInterface(String upstreamIface) {
diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index ca74430..864f35c 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -220,6 +220,7 @@
     private final UserRestrictionActionListener mTetheringRestriction;
     private final ActiveDataSubIdListener mActiveDataSubIdListener;
     private final ConnectedClientsTracker mConnectedClientsTracker;
+    private final TetheringThreadExecutor mExecutor;
     private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
     // All the usage of mTetheringEventCallback should run in the same thread.
     private ITetheringEventCallback mTetheringEventCallback = null;
@@ -296,8 +297,8 @@
         final UserManager userManager = (UserManager) mContext.getSystemService(
                 Context.USER_SERVICE);
         mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
-        final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler);
-        mActiveDataSubIdListener = new ActiveDataSubIdListener(executor);
+        mExecutor = new TetheringThreadExecutor(mHandler);
+        mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
 
         // Load tethering configuration.
         updateConfiguration();
@@ -315,9 +316,7 @@
 
         final WifiManager wifiManager = getWifiManager();
         if (wifiManager != null) {
-            wifiManager.registerSoftApCallback(
-                  mHandler::post /* executor */,
-                  new TetheringSoftApCallback());
+            wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
         }
     }
 
@@ -606,14 +605,17 @@
                 Context.ETHERNET_SERVICE);
         synchronized (mPublicSync) {
             if (enable) {
+                if (mEthernetCallback != null) return TETHER_ERROR_NO_ERROR;
+
                 mEthernetCallback = new EthernetCallback();
-                mEthernetIfaceRequest = em.requestTetheredInterface(mEthernetCallback);
+                mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
             } else {
-                if (mConfiguredEthernetIface != null) {
-                    stopEthernetTetheringLocked();
+                stopEthernetTetheringLocked();
+                if (mEthernetCallback != null) {
                     mEthernetIfaceRequest.release();
+                    mEthernetCallback = null;
+                    mEthernetIfaceRequest = null;
                 }
-                mEthernetCallback = null;
             }
         }
         return TETHER_ERROR_NO_ERROR;
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 33b3558..948266d 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -43,6 +43,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -546,9 +547,9 @@
         reset(mNetd);
 
         // Link-local and multicast neighbors are ignored.
-        recvNewNeigh(notMyIfindex, neighLL, NUD_REACHABLE, macA);
+        recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA);
         verifyNoMoreInteractions(mNetd);
-        recvNewNeigh(notMyIfindex, neighMC, NUD_REACHABLE, macA);
+        recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA);
         verifyNoMoreInteractions(mNetd);
 
         // A neighbor that is no longer valid causes the rule to be removed.
@@ -578,6 +579,52 @@
                 eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
         inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
                 eq(neighB.getAddress()));
+        reset(mNetd);
+
+        // When the upstream is lost, rules are removed.
+        dispatchTetherConnectionChanged(null, null);
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2),
+                eq(neighA.getAddress()));
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2),
+                eq(neighB.getAddress()));
+        reset(mNetd);
+
+        // If the upstream is IPv4-only, no rules are added.
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE);
+        reset(mNetd);
+        recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+        verifyNoMoreInteractions(mNetd);
+
+        // Rules can be added again once upstream IPv6 connectivity is available.
+        lp.setInterfaceName(UPSTREAM_IFACE);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+        recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+                eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+        verify(mNetd, never()).tetherRuleAddDownstreamIpv6(anyInt(), anyInt(),
+                eq(neighA.getAddress()), any(), any());
+
+        // If upstream IPv6 connectivity is lost, rules are removed.
+        reset(mNetd);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, null);
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+
+        // When the interface goes down, rules are removed.
+        lp.setInterfaceName(UPSTREAM_IFACE);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+        recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+        recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+                eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
+        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+                eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+        reset(mNetd);
+
+        mIpServer.stop();
+        mLooper.dispatchAll();
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+        reset(mNetd);
     }
 
     private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
@@ -624,16 +671,15 @@
      * @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream.
      */
     private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp) {
-        mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
-                new InterfaceSet(upstreamIface));
-        if (v6lp != null) {
-            mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp);
-        }
+        dispatchTetherConnectionChanged(upstreamIface);
+        mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp);
         mLooper.dispatchAll();
     }
 
     private void dispatchTetherConnectionChanged(String upstreamIface) {
-        dispatchTetherConnectionChanged(upstreamIface, null);
+        final InterfaceSet ifs = (upstreamIface != null) ? new InterfaceSet(upstreamIface) : null;
+        mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifs);
+        mLooper.dispatchAll();
     }
 
     private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) {
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index f2074bd..72c3435 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -28,6 +28,7 @@
 import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
 import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
 import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
 import static android.net.TetheringManager.TETHERING_NCM;
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
@@ -75,6 +76,8 @@
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
+import android.net.EthernetManager;
+import android.net.EthernetManager.TetheredInterfaceRequest;
 import android.net.INetd;
 import android.net.ITetheringEventCallback;
 import android.net.InetAddresses;
@@ -180,6 +183,7 @@
     @Mock private UserManager mUserManager;
     @Mock private NetworkRequest mNetworkRequest;
     @Mock private ConnectivityManager mCm;
+    @Mock private EthernetManager mEm;
 
     private final MockIpServerDependencies mIpServerDependencies =
             spy(new MockIpServerDependencies());
@@ -232,6 +236,7 @@
             if (Context.USER_SERVICE.equals(name)) return mUserManager;
             if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
             if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
+            if (Context.ETHERNET_SERVICE.equals(name)) return mEm;
             return super.getSystemService(name);
         }
 
@@ -1316,6 +1321,24 @@
         assertEquals(fakeSubId, newConfig.activeDataSubId);
     }
 
+    @Test
+    public void testNoDuplicatedEthernetRequest() throws Exception {
+        final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
+        when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
+        mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null);
+        mLooper.dispatchAll();
+        verify(mEm, times(1)).requestTetheredInterface(any(), any());
+        mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null);
+        mLooper.dispatchAll();
+        verifyNoMoreInteractions(mEm);
+        mTethering.stopTethering(TETHERING_ETHERNET);
+        mLooper.dispatchAll();
+        verify(mockRequest, times(1)).release();
+        mTethering.stopTethering(TETHERING_ETHERNET);
+        mLooper.dispatchAll();
+        verifyNoMoreInteractions(mEm);
+    }
+
     private void workingWifiP2pGroupOwner(
             boolean emulateInterfaceStatusChanged) throws Exception {
         if (emulateInterfaceStatusChanged) {