Migrate to TetherOffloadRuleParcel in IpServer

The netd tethering offload IPCs are changing from taking a list
of primitives to taking a TetherOffloadRuleParcel. Modify their
only caller.

Bug: 140541991
Test: atest IpServerTest
Change-Id: I83718c80ef9d31199c87021b4dd5821717fd5ba5
This commit is contained in:
Lorenzo Colitti
2020-04-03 22:05:14 +09:00
parent cfd82d4361
commit beb28405b1
2 changed files with 95 additions and 34 deletions

View File

@@ -34,6 +34,7 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
import android.net.TetherOffloadRuleParcel;
import android.net.TetheredClient;
import android.net.TetheringManager;
import android.net.TetheringRequestParcel;
@@ -280,6 +281,19 @@ public class IpServer extends StateMachine {
return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
dstMac);
}
// Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream()
// would be error-prone due to generated stable AIDL classes not having a copy constructor.
public TetherOffloadRuleParcel toTetherOffloadRuleParcel() {
final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel();
parcel.inputInterfaceIndex = upstreamIfindex;
parcel.outputInterfaceIndex = downstreamIfindex;
parcel.destination = address.getAddress();
parcel.prefixLength = 128;
parcel.srcL2Address = srcMac.toByteArray();
parcel.dstL2Address = dstMac.toByteArray();
return parcel;
}
}
private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules =
new LinkedHashMap<>();
@@ -824,9 +838,7 @@ public class IpServer extends StateMachine {
private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
try {
mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex,
rule.address.getAddress(), mInterfaceParams.macAddr.toByteArray(),
rule.dstMac.toByteArray());
mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
mIpv6ForwardingRules.put(rule.address, rule);
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Could not add IPv6 downstream rule: ", e);
@@ -835,7 +847,7 @@ public class IpServer extends StateMachine {
private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
try {
mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress());
mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
if (removeFromMap) {
mIpv6ForwardingRules.remove(rule.address);
}

View File

@@ -43,7 +43,6 @@ import static org.junit.Assert.assertTrue;
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;
@@ -66,6 +65,7 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
import android.net.TetherOffloadRuleParcel;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer;
import android.net.dhcp.IDhcpServerCallbacks;
@@ -85,6 +85,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
@@ -92,6 +93,7 @@ import org.mockito.MockitoAnnotations;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -514,6 +516,65 @@ public class IpServerTest {
mLooper.dispatchAll();
}
/**
* Custom ArgumentMatcher for TetherOffloadRuleParcel. This is needed because generated stable
* AIDL classes don't have equals(), so we cannot just use eq(). A custom assert, such as:
*
* private void checkFooCalled(StableParcelable p, ...) {
* ArgumentCaptor<FooParam> captor = ArgumentCaptor.forClass(FooParam.class);
* verify(mMock).foo(captor.capture());
* Foo foo = captor.getValue();
* assertFooMatchesExpectations(foo);
* }
*
* almost works, but not quite. This is because if the code under test calls foo() twice, the
* first call to checkFooCalled() matches both the calls, putting both calls into the captor,
* and then fails with TooManyActualInvocations. It also makes it harder to use other mockito
* features such as never(), inOrder(), etc.
*
* This approach isn't great because if the match fails, the error message is unhelpful
* (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does
* work.
*
* See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the
* TooManyActualInvocations problem described above by forcing the caller of the custom assert
* method to specify all expected invocations in one call. This is useful when the stable
* parcelable class being asserted on has a corresponding Java object (eg., RouteInfo and
* RouteInfoParcelable), and the caller can just pass in a list of them. It not useful here
* because there is no such object.
*/
private static class TetherOffloadRuleParcelMatcher implements
ArgumentMatcher<TetherOffloadRuleParcel> {
public final int upstreamIfindex;
public final InetAddress dst;
public final MacAddress dstMac;
TetherOffloadRuleParcelMatcher(int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
this.upstreamIfindex = upstreamIfindex;
this.dst = dst;
this.dstMac = dstMac;
}
public boolean matches(TetherOffloadRuleParcel parcel) {
return upstreamIfindex == parcel.inputInterfaceIndex
&& (TEST_IFACE_PARAMS.index == parcel.outputInterfaceIndex)
&& Arrays.equals(dst.getAddress(), parcel.destination)
&& (128 == parcel.prefixLength)
&& Arrays.equals(TEST_IFACE_PARAMS.macAddr.toByteArray(), parcel.srcL2Address)
&& Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address);
}
public String toString() {
return String.format("TetherOffloadRuleParcelMatcher(%d, %s, %s",
upstreamIfindex, dst.getHostAddress(), dstMac);
}
}
private TetherOffloadRuleParcel matches(
int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac));
}
@Test
public void addRemoveipv6ForwardingRules() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */);
@@ -537,13 +598,11 @@ public class IpServerTest {
// Events on this interface are received and sent to netd.
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
reset(mNetd);
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).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
reset(mNetd);
// Link-local and multicast neighbors are ignored.
@@ -554,12 +613,12 @@ public class IpServerTest {
// A neighbor that is no longer valid causes the rule to be removed.
recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA);
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
reset(mNetd);
// A neighbor that is deleted causes the rule to be removed.
recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
reset(mNetd);
// Upstream changes result in deleting and re-adding the rules.
@@ -571,22 +630,16 @@ public class IpServerTest {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(UPSTREAM_IFACE2);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp);
inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
eq(neighA.getAddress()));
inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
eq(neighB.getAddress()));
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA));
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB));
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
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()));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB));
reset(mNetd);
// If the upstream is IPv4-only, no rules are added.
@@ -599,31 +652,27 @@ public class IpServerTest {
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());
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
// If upstream IPv6 connectivity is lost, rules are removed.
reset(mNetd);
dispatchTetherConnectionChanged(UPSTREAM_IFACE, null);
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
// 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()));
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
reset(mNetd);
mIpServer.stop();
mLooper.dispatchAll();
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
reset(mNetd);
}