Merge "[XFRM_MSG_GETSA] Fix the class names and code locations" into main
diff --git a/OWNERS b/OWNERS
index b2176cc..988af41 100644
--- a/OWNERS
+++ b/OWNERS
@@ -3,3 +3,4 @@
file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
per-file **IpSec* = file:platform/frameworks/base:main:/services/core/java/com/android/server/vcn/OWNERS
+per-file **Xfrm* = file:platform/frameworks/base:main:/services/core/java/com/android/server/vcn/OWNERS
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 4bd7e6a..9f542f4 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -90,7 +90,6 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -1258,12 +1257,18 @@
pw.decreaseIndent();
}
- private IpPrefix longToPrefix(long ip64) {
- final ByteBuffer prefixBuffer = ByteBuffer.allocate(IPV6_ADDR_LEN);
- prefixBuffer.putLong(ip64);
+ /**
+ * Returns a /64 IpPrefix corresponding to the passed in byte array
+ *
+ * @param ip64 byte array to convert format
+ * @return the converted IpPrefix
+ */
+ @VisibleForTesting
+ public static IpPrefix bytesToPrefix(final byte[] ip64) {
IpPrefix sourcePrefix;
+ byte[] prefixBytes = Arrays.copyOf(ip64, IPV6_ADDR_LEN);
try {
- sourcePrefix = new IpPrefix(InetAddress.getByAddress(prefixBuffer.array()), 64);
+ sourcePrefix = new IpPrefix(InetAddress.getByAddress(prefixBytes), 64);
} catch (UnknownHostException e) {
// Cannot happen. InetAddress.getByAddress can only throw an exception if the byte array
// is the wrong length, but we allocate it with fixed length IPV6_ADDR_LEN.
@@ -1274,7 +1279,7 @@
private String ipv6UpstreamRuleToString(TetherUpstream6Key key, Tether6Value value) {
return String.format("%d(%s) [%s] [%s] -> %d(%s) %04x [%s] [%s]",
- key.iif, getIfName(key.iif), key.dstMac, longToPrefix(key.src64), value.oif,
+ key.iif, getIfName(key.iif), key.dstMac, bytesToPrefix(key.src64), value.oif,
getIfName(value.oif), value.ethProto, value.ethSrcMac, value.ethDstMac);
}
@@ -1570,7 +1575,7 @@
*/
@NonNull
public TetherUpstream6Key makeTetherUpstream6Key() {
- long prefix64 = ByteBuffer.wrap(sourcePrefix.getRawAddress()).getLong();
+ final byte[] prefix64 = Arrays.copyOf(sourcePrefix.getRawAddress(), 8);
return new TetherUpstream6Key(downstreamIfindex, inDstMac, prefix64);
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java b/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
index 36a1c3c..0cc3dd9 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
@@ -32,10 +32,10 @@
@Field(order = 1, type = Type.EUI48, padding = 6)
public final MacAddress dstMac; // Destination ethernet mac address (zeroed iff rawip ingress).
- @Field(order = 2, type = Type.S64)
- public final long src64; // The top 64-bits of the source ip.
+ @Field(order = 2, type = Type.ByteArray, arraysize = 8)
+ public final byte[] src64; // The top 64-bits of the source ip.
- public TetherUpstream6Key(int iif, @NonNull final MacAddress dstMac, long src64) {
+ public TetherUpstream6Key(int iif, @NonNull final MacAddress dstMac, final byte[] src64) {
Objects.requireNonNull(dstMac);
this.iif = iif;
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 996ee11..3277363 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -327,8 +327,10 @@
return mConfig;
}
});
- mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMainSM, mLog,
- TetherMainSM.EVENT_UPSTREAM_CALLBACK);
+ mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mHandler, mLog,
+ (what, obj) -> {
+ mTetherMainSM.sendMessage(TetherMainSM.EVENT_UPSTREAM_CALLBACK, what, 0, obj);
+ });
mForwardedDownstreams = new HashSet<>();
IntentFilter filter = new IntentFilter();
@@ -2879,4 +2881,9 @@
} catch (RemoteException e) { }
});
}
+
+ @VisibleForTesting
+ public TetherMainSM getTetherMainSMForTesting() {
+ return mTetherMainSM;
+ }
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index c274165..d02e8e8 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -35,7 +35,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
-import com.android.internal.util.StateMachine;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.SdkUtil.LateSdk;
import com.android.net.module.util.SharedLog;
@@ -84,9 +83,9 @@
/**
* Get a reference to the UpstreamNetworkMonitor to be used by tethering.
*/
- public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
- SharedLog log, int what) {
- return new UpstreamNetworkMonitor(ctx, target, log, what);
+ public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, Handler h,
+ SharedLog log, UpstreamNetworkMonitor.EventListener listener) {
+ return new UpstreamNetworkMonitor(ctx, h, log, listener);
}
/**
diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index ac2aa7b..7a05d74 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -44,7 +44,6 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.StateMachine;
import com.android.net.module.util.SharedLog;
import com.android.networkstack.apishim.ConnectivityManagerShimImpl;
import com.android.networkstack.apishim.common.ConnectivityManagerShim;
@@ -111,9 +110,8 @@
private final Context mContext;
private final SharedLog mLog;
- private final StateMachine mTarget;
private final Handler mHandler;
- private final int mWhat;
+ private final EventListener mEventListener;
private final HashMap<Network, UpstreamNetworkState> mNetworkMap = new HashMap<>();
private HashSet<IpPrefix> mLocalPrefixes;
private ConnectivityManager mCM;
@@ -135,12 +133,11 @@
private Network mDefaultInternetNetwork;
private boolean mPreferTestNetworks;
- public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
+ public UpstreamNetworkMonitor(Context ctx, Handler h, SharedLog log, EventListener listener) {
mContext = ctx;
- mTarget = tgt;
- mHandler = mTarget.getHandler();
+ mHandler = h;
mLog = log.forSubComponent(TAG);
- mWhat = what;
+ mEventListener = listener;
mLocalPrefixes = new HashSet<>();
mIsDefaultCellularUpstream = false;
mCM = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -374,11 +371,12 @@
network, newNc));
}
- mNetworkMap.put(network, new UpstreamNetworkState(
- prev.linkProperties, newNc, network));
+ final UpstreamNetworkState uns =
+ new UpstreamNetworkState(prev.linkProperties, newNc, network);
+ mNetworkMap.put(network, uns);
// TODO: If sufficient information is available to select a more
// preferable upstream, do so now and notify the target.
- notifyTarget(EVENT_ON_CAPABILITIES, network);
+ mEventListener.onUpstreamEvent(EVENT_ON_CAPABILITIES, uns);
}
private @Nullable UpstreamNetworkState updateLinkProperties(@NonNull Network network,
@@ -411,7 +409,7 @@
private void handleLinkProp(Network network, LinkProperties newLp) {
final UpstreamNetworkState ns = updateLinkProperties(network, newLp);
if (ns != null) {
- notifyTarget(EVENT_ON_LINKPROPERTIES, ns);
+ mEventListener.onUpstreamEvent(EVENT_ON_LINKPROPERTIES, ns);
}
}
@@ -438,7 +436,7 @@
// preferable upstream, do so now and notify the target. Likewise,
// if the current upstream network is gone, notify the target of the
// fact that we now have no upstream at all.
- notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
+ mEventListener.onUpstreamEvent(EVENT_ON_LOST, mNetworkMap.remove(network));
}
private void maybeHandleNetworkSwitch(@NonNull Network network) {
@@ -456,14 +454,14 @@
// Default network changed. Update local data and notify tethering.
Log.d(TAG, "New default Internet network: " + network);
mDefaultInternetNetwork = network;
- notifyTarget(EVENT_DEFAULT_SWITCHED, ns);
+ mEventListener.onUpstreamEvent(EVENT_DEFAULT_SWITCHED, ns);
}
private void recomputeLocalPrefixes() {
final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values());
if (!mLocalPrefixes.equals(localPrefixes)) {
mLocalPrefixes = localPrefixes;
- notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone());
+ mEventListener.onUpstreamEvent(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone());
}
}
@@ -502,12 +500,13 @@
// onLinkPropertiesChanged right after this method and mDefaultInternetNetwork will
// be updated then.
//
- // Technically, not updating here isn't necessary, because the notifications to
- // Tethering sent by notifyTarget are messages sent to a state machine running on
- // the same thread as this method, and so cannot arrive until after this method has
- // returned. However, it is not a good idea to rely on that because fact that
- // Tethering uses multiple state machines running on the same thread is a major
- // source of race conditions and something that should be fixed.
+ // Technically, mDefaultInternetNetwork could be updated here, because the
+ // Callback#onChange implementation sends messages to the state machine running
+ // on the same thread as this method. If there is new default network change,
+ // the message cannot arrive until onLinkPropertiesChanged returns.
+ // However, it is not a good idea to rely on that because fact that Tethering uses
+ // multiple state machines running on the same thread is a major source of race
+ // conditions and something that should be fixed.
//
// TODO: is it correct that this code always updates EntitlementManager?
// This code runs when the default network connects or changes capabilities, but the
@@ -551,7 +550,7 @@
mIsDefaultCellularUpstream = false;
mEntitlementMgr.notifyUpstream(false);
Log.d(TAG, "Lost default Internet network: " + network);
- notifyTarget(EVENT_DEFAULT_SWITCHED, null);
+ mEventListener.onUpstreamEvent(EVENT_DEFAULT_SWITCHED, null);
return;
}
@@ -569,14 +568,6 @@
if (cb != null) cm().unregisterNetworkCallback(cb);
}
- private void notifyTarget(int which, Network network) {
- notifyTarget(which, mNetworkMap.get(network));
- }
-
- private void notifyTarget(int which, Object obj) {
- mTarget.sendMessage(mWhat, which, 0, obj);
- }
-
private static class TypeStatePair {
public int type = TYPE_NONE;
public UpstreamNetworkState ns = null;
@@ -698,4 +689,10 @@
public void setPreferTestNetworks(boolean prefer) {
mPreferTestNetworks = prefer;
}
+
+ /** An interface to notify upstream network changes. */
+ public interface EventListener {
+ /** Notify the client of some event */
+ void onUpstreamEvent(int what, Object obj);
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index 6c1721e..01600b8 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -64,6 +64,7 @@
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
import static com.android.testutils.MiscAsserts.assertSameElements;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -166,7 +167,6 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -794,7 +794,7 @@
if (!mDeps.isAtLeastS()) return;
ArrayMap<TetherUpstream6Key, Tether6Value> expected = new ArrayMap<>();
for (IpPrefix upstreamPrefix : upstreamPrefixes) {
- long prefix64 = prefixToLong(upstreamPrefix);
+ final byte[] prefix64 = prefixToIp64(upstreamPrefix);
final TetherUpstream6Key key = new TetherUpstream6Key(DOWNSTREAM_IFACE_PARAMS.index,
DOWNSTREAM_IFACE_PARAMS.macAddr, prefix64);
final Tether6Value value = new Tether6Value(upstreamIfindex,
@@ -822,7 +822,7 @@
if (!mDeps.isAtLeastS()) return;
Set<TetherUpstream6Key> expected = new ArraySet<>();
for (IpPrefix upstreamPrefix : upstreamPrefixes) {
- long prefix64 = prefixToLong(upstreamPrefix);
+ final byte[] prefix64 = prefixToIp64(upstreamPrefix);
final TetherUpstream6Key key = new TetherUpstream6Key(DOWNSTREAM_IFACE_PARAMS.index,
DOWNSTREAM_IFACE_PARAMS.macAddr, prefix64);
expected.add(key);
@@ -1275,7 +1275,7 @@
}
@Test
- public void testRuleMakeTetherDownstream6Key() throws Exception {
+ public void testIpv6DownstreamRuleMakeTetherDownstream6Key() throws Exception {
final int mobileIfIndex = 100;
final Ipv6DownstreamRule rule = buildTestDownstreamRule(mobileIfIndex, NEIGH_A, MAC_A);
@@ -1288,7 +1288,7 @@
}
@Test
- public void testRuleMakeTether6Value() throws Exception {
+ public void testIpv6DownstreamRuleMakeTether6Value() throws Exception {
final int mobileIfIndex = 100;
final Ipv6DownstreamRule rule = buildTestDownstreamRule(mobileIfIndex, NEIGH_A, MAC_A);
@@ -1303,6 +1303,46 @@
}
@Test
+ public void testIpv6UpstreamRuleMakeTetherUpstream6Key() {
+ final byte[] bytes = new byte[]{(byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0xab, (byte) 0xcd, (byte) 0xfe, (byte) 0x00};
+ final IpPrefix prefix = new IpPrefix("2001:db8:abcd:fe00::/64");
+ final Ipv6UpstreamRule rule = buildTestUpstreamRule(UPSTREAM_IFINDEX,
+ DOWNSTREAM_IFINDEX, prefix, DOWNSTREAM_MAC);
+
+ final TetherUpstream6Key key = rule.makeTetherUpstream6Key();
+ assertEquals(DOWNSTREAM_IFINDEX, key.iif);
+ assertEquals(DOWNSTREAM_MAC, key.dstMac);
+ assertArrayEquals(bytes, key.src64);
+ // iif (4) + dstMac (6) + padding (6) + src64 (8) = 24
+ assertEquals(24, key.writeToBytes().length);
+ }
+
+ @Test
+ public void testIpv6UpstreamRuleMakeTether6Value() {
+ final IpPrefix prefix = new IpPrefix("2001:db8:abcd:fe00::/64");
+ final Ipv6UpstreamRule rule = buildTestUpstreamRule(UPSTREAM_IFINDEX,
+ DOWNSTREAM_IFINDEX, prefix, DOWNSTREAM_MAC);
+
+ final Tether6Value value = rule.makeTether6Value();
+ assertEquals(UPSTREAM_IFINDEX, value.oif);
+ assertEquals(MAC_NULL, value.ethDstMac);
+ assertEquals(MAC_NULL, value.ethSrcMac);
+ assertEquals(ETH_P_IPV6, value.ethProto);
+ assertEquals(NetworkStackConstants.ETHER_MTU, value.pmtu);
+ // oif (4) + ethDstMac (6) + ethSrcMac (6) + ethProto (2) + pmtu (2) = 20
+ assertEquals(20, value.writeToBytes().length);
+ }
+
+ @Test
+ public void testBytesToPrefix() {
+ final byte[] bytes = new byte[]{(byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0x00, (byte) 0x00, (byte) 0x12, (byte) 0x34};
+ final IpPrefix prefix = new IpPrefix("2001:db8:0:1234::/64");
+ assertEquals(prefix, BpfCoordinator.bytesToPrefix(bytes));
+ }
+
+ @Test
public void testSetDataLimit() throws Exception {
setupFunctioningNetdInterface();
@@ -2269,8 +2309,8 @@
100 /* nonzero, CT_NEW */);
}
- private static long prefixToLong(IpPrefix prefix) {
- return ByteBuffer.wrap(prefix.getRawAddress()).getLong();
+ private static byte[] prefixToIp64(IpPrefix prefix) {
+ return Arrays.copyOf(prefix.getRawAddress(), 8);
}
void checkRule4ExistInUpstreamDownstreamMap() throws Exception {
@@ -2515,7 +2555,7 @@
final Ipv6DownstreamRule rule = buildTestDownstreamRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A);
mBpfDownstream6Map.insertEntry(rule.makeTetherDownstream6Key(), rule.makeTether6Value());
- final long prefix64 = prefixToLong(UPSTREAM_PREFIX);
+ final byte[] prefix64 = prefixToIp64(UPSTREAM_PREFIX);
final TetherUpstream6Key upstream6Key = new TetherUpstream6Key(DOWNSTREAM_IFINDEX,
DOWNSTREAM_MAC, prefix64);
final Tether6Value upstream6Value = new Tether6Value(UPSTREAM_IFINDEX,
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 6eba590..5877fc5 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -187,7 +187,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.net.module.util.CollectionUtils;
@@ -310,6 +309,7 @@
private BroadcastReceiver mBroadcastReceiver;
private Tethering mTethering;
private TestTetheringEventCallback mTetheringEventCallback;
+ private Tethering.TetherMainSM mTetherMainSM;
private PhoneStateListener mPhoneStateListener;
private InterfaceConfigurationParcel mInterfaceConfiguration;
private TetheringConfiguration mConfig;
@@ -319,6 +319,7 @@
private SoftApCallback mSoftApCallback;
private SoftApCallback mLocalOnlyHotspotCallback;
private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
+ private UpstreamNetworkMonitor.EventListener mEventListener;
private TetheredInterfaceCallbackShim mTetheredInterfaceCallbackShim;
private TestConnectivityManager mCm;
@@ -432,7 +433,6 @@
}
public class MockTetheringDependencies extends TetheringDependencies {
- StateMachine mUpstreamNetworkMonitorSM;
ArrayList<IpServer> mAllDownstreams;
@Override
@@ -456,12 +456,12 @@
}
@Override
- public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
- StateMachine target, SharedLog log, int what) {
+ public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, Handler h,
+ SharedLog log, UpstreamNetworkMonitor.EventListener listener) {
// Use a real object instead of a mock so that some tests can use a real UNM and some
// can use a mock.
- mUpstreamNetworkMonitorSM = target;
- mUpstreamNetworkMonitor = spy(super.getUpstreamNetworkMonitor(ctx, target, log, what));
+ mEventListener = listener;
+ mUpstreamNetworkMonitor = spy(super.getUpstreamNetworkMonitor(ctx, h, log, listener));
return mUpstreamNetworkMonitor;
}
@@ -688,6 +688,7 @@
private void initTetheringOnTestThread() throws Exception {
mLooper = new TestLooper();
mTethering = new Tethering(mTetheringDependencies);
+ mTetherMainSM = mTethering.getTetherMainSMForTesting();
verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
verify(mNetd).registerUnsolicitedEventListener(any());
verifyDefaultNetworkRequestFiled();
@@ -1182,10 +1183,7 @@
initTetheringUpstream(upstreamState);
// Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
- mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
- Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
- UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
- 0,
+ mEventListener.onUpstreamEvent(UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
upstreamState);
mLooper.dispatchAll();
@@ -2713,14 +2711,12 @@
@Test
public void testUpstreamNetworkChanged() throws Exception {
initTetheringOnTestThread();
- final Tethering.TetherMainSM stateMachine = (Tethering.TetherMainSM)
- mTetheringDependencies.mUpstreamNetworkMonitorSM;
final InOrder inOrder = inOrder(mNotificationUpdater);
// Gain upstream.
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
initTetheringUpstream(upstreamState);
- stateMachine.chooseUpstreamType(true);
+ mTetherMainSM.chooseUpstreamType(true);
mTetheringEventCallback.expectUpstreamChanged(upstreamState.network);
inOrder.verify(mNotificationUpdater)
.onUpstreamCapabilitiesChanged(upstreamState.networkCapabilities);
@@ -2728,7 +2724,7 @@
// Set the upstream with the same network ID but different object and the same capability.
final UpstreamNetworkState upstreamState2 = buildMobileIPv4UpstreamState();
initTetheringUpstream(upstreamState2);
- stateMachine.chooseUpstreamType(true);
+ mTetherMainSM.chooseUpstreamType(true);
// Expect that no upstream change event and capabilities changed event.
mTetheringEventCallback.assertNoUpstreamChangeCallback();
inOrder.verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any());
@@ -2738,17 +2734,17 @@
assertFalse(upstreamState3.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED));
upstreamState3.networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
initTetheringUpstream(upstreamState3);
- stateMachine.chooseUpstreamType(true);
+ mTetherMainSM.chooseUpstreamType(true);
// Expect that no upstream change event and capabilities changed event.
mTetheringEventCallback.assertNoUpstreamChangeCallback();
- stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState3);
+ mTetherMainSM.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState3);
inOrder.verify(mNotificationUpdater)
.onUpstreamCapabilitiesChanged(upstreamState3.networkCapabilities);
// Lose upstream.
initTetheringUpstream(null);
- stateMachine.chooseUpstreamType(true);
+ mTetherMainSM.chooseUpstreamType(true);
mTetheringEventCallback.expectUpstreamChanged(NULL_NETWORK);
inOrder.verify(mNotificationUpdater).onUpstreamCapabilitiesChanged(null);
}
@@ -2756,17 +2752,15 @@
@Test
public void testUpstreamCapabilitiesChanged() throws Exception {
initTetheringOnTestThread();
- final Tethering.TetherMainSM stateMachine = (Tethering.TetherMainSM)
- mTetheringDependencies.mUpstreamNetworkMonitorSM;
final InOrder inOrder = inOrder(mNotificationUpdater);
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
initTetheringUpstream(upstreamState);
- stateMachine.chooseUpstreamType(true);
+ mTetherMainSM.chooseUpstreamType(true);
inOrder.verify(mNotificationUpdater)
.onUpstreamCapabilitiesChanged(upstreamState.networkCapabilities);
- stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState);
+ mTetherMainSM.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState);
inOrder.verify(mNotificationUpdater)
.onUpstreamCapabilitiesChanged(upstreamState.networkCapabilities);
@@ -2775,7 +2769,7 @@
// Expect that capability is changed with new capability VALIDATED.
assertFalse(upstreamState.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED));
upstreamState.networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
- stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState);
+ mTetherMainSM.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState);
inOrder.verify(mNotificationUpdater)
.onUpstreamCapabilitiesChanged(upstreamState.networkCapabilities);
@@ -2784,7 +2778,7 @@
final UpstreamNetworkState upstreamState2 = new UpstreamNetworkState(
upstreamState.linkProperties, upstreamState.networkCapabilities,
new Network(WIFI_NETID));
- stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState2);
+ mTetherMainSM.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState2);
inOrder.verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any());
}
@@ -2907,11 +2901,7 @@
final String iface, final int transportType) {
final UpstreamNetworkState upstream = buildV4UpstreamState(ipv4Address, network, iface,
transportType);
- mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
- Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
- UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
- 0,
- upstream);
+ mEventListener.onUpstreamEvent(UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, upstream);
mLooper.dispatchAll();
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
index e756bd3..045c0cb 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
@@ -30,10 +30,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -51,27 +53,23 @@
import android.net.NetworkRequest;
import android.os.Build;
import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import android.os.test.TestLooper;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
import com.android.net.module.util.SharedLog;
import com.android.networkstack.tethering.TestConnectivityManager.NetworkRequestInfo;
import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -84,8 +82,6 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class UpstreamNetworkMonitorTest {
- private static final int EVENT_UNM_UPDATE = 1;
-
private static final boolean INCLUDES = true;
private static final boolean EXCLUDES = false;
@@ -102,12 +98,13 @@
@Mock private EntitlementManager mEntitleMgr;
@Mock private IConnectivityManager mCS;
@Mock private SharedLog mLog;
+ @Mock private UpstreamNetworkMonitor.EventListener mListener;
- private TestStateMachine mSM;
private TestConnectivityManager mCM;
private UpstreamNetworkMonitor mUNM;
private final TestLooper mLooper = new TestLooper();
+ private InOrder mCallbackOrder;
@Before public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -117,17 +114,11 @@
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
+ mCallbackOrder = inOrder(mListener);
mCM = spy(new TestConnectivityManager(mContext, mCS));
when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))).thenReturn(mCM);
- mSM = new TestStateMachine(mLooper.getLooper());
- mUNM = new UpstreamNetworkMonitor(mContext, mSM, mLog, EVENT_UNM_UPDATE);
- }
-
- @After public void tearDown() throws Exception {
- if (mSM != null) {
- mSM.quit();
- mSM = null;
- }
+ mUNM = new UpstreamNetworkMonitor(mContext, new Handler(mLooper.getLooper()), mLog,
+ mListener);
}
@Test
@@ -603,14 +594,17 @@
mCM.makeDefaultNetwork(cellAgent);
mLooper.dispatchAll();
verifyCurrentLinkProperties(cellAgent);
- int messageIndex = mSM.messages.size() - 1;
+ verifyNotifyNetworkCapabilitiesChange(cellAgent.networkCapabilities);
+ verifyNotifyLinkPropertiesChange(cellLp);
+ verifyNotifyDefaultSwitch(cellAgent);
+ verifyNoMoreInteractions(mListener);
addLinkAddresses(cellLp, ipv6Addr1);
mCM.sendLinkProperties(cellAgent, false /* updateDefaultFirst */);
mLooper.dispatchAll();
verifyCurrentLinkProperties(cellAgent);
- verifyNotifyLinkPropertiesChange(messageIndex);
- messageIndex = mSM.messages.size() - 1;
+ verifyNotifyLinkPropertiesChange(cellLp);
+ verifyNoMoreInteractions(mListener);
removeLinkAddresses(cellLp, ipv6Addr1);
addLinkAddresses(cellLp, ipv6Addr2);
@@ -618,7 +612,8 @@
mLooper.dispatchAll();
assertEquals(cellAgent.linkProperties, mUNM.getCurrentPreferredUpstream().linkProperties);
verifyCurrentLinkProperties(cellAgent);
- verifyNotifyLinkPropertiesChange(messageIndex);
+ verifyNotifyLinkPropertiesChange(cellLp);
+ verifyNoMoreInteractions(mListener);
}
private void verifyCurrentLinkProperties(TestNetworkAgent agent) {
@@ -626,12 +621,33 @@
assertEquals(agent.linkProperties, mUNM.getCurrentPreferredUpstream().linkProperties);
}
- private void verifyNotifyLinkPropertiesChange(int lastMessageIndex) {
- assertEquals(UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
- mSM.messages.get(++lastMessageIndex).arg1);
- assertEquals(UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES,
- mSM.messages.get(++lastMessageIndex).arg1);
- assertEquals(lastMessageIndex + 1, mSM.messages.size());
+ private void verifyNotifyNetworkCapabilitiesChange(final NetworkCapabilities cap) {
+ mCallbackOrder.verify(mListener).onUpstreamEvent(
+ eq(UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES),
+ argThat(uns -> uns instanceof UpstreamNetworkState
+ && cap.equals(((UpstreamNetworkState) uns).networkCapabilities)));
+
+ }
+
+ private void verifyNotifyLinkPropertiesChange(final LinkProperties lp) {
+ mCallbackOrder.verify(mListener).onUpstreamEvent(
+ eq(UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES),
+ argThat(uns -> uns instanceof UpstreamNetworkState
+ && lp.equals(((UpstreamNetworkState) uns).linkProperties)));
+
+ mCallbackOrder.verify(mListener).onUpstreamEvent(
+ eq(UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES), any());
+ }
+
+ private void verifyNotifyDefaultSwitch(TestNetworkAgent agent) {
+ mCallbackOrder.verify(mListener).onUpstreamEvent(
+ eq(UpstreamNetworkMonitor.EVENT_DEFAULT_SWITCHED),
+ argThat(uns ->
+ uns instanceof UpstreamNetworkState
+ && agent.networkId.equals(((UpstreamNetworkState) uns).network)
+ && agent.linkProperties.equals(((UpstreamNetworkState) uns).linkProperties)
+ && agent.networkCapabilities.equals(
+ ((UpstreamNetworkState) uns).networkCapabilities)));
}
private void addLinkAddresses(LinkProperties lp, String... addrs) {
@@ -673,33 +689,6 @@
return false;
}
- public static class TestStateMachine extends StateMachine {
- public final ArrayList<Message> messages = new ArrayList<>();
- private final State mLoggingState = new LoggingState();
-
- class LoggingState extends State {
- @Override public void enter() {
- messages.clear();
- }
-
- @Override public void exit() {
- messages.clear();
- }
-
- @Override public boolean processMessage(Message msg) {
- messages.add(msg);
- return true;
- }
- }
-
- public TestStateMachine(Looper looper) {
- super("UpstreamNetworkMonitor.TestStateMachine", looper);
- addState(mLoggingState);
- setInitialState(mLoggingState);
- super.start();
- }
- }
-
static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
final Set<String> expectedSet = new HashSet<>();
Collections.addAll(expectedSet, expected);
diff --git a/framework-t/src/android/net/nsd/MDnsManager.java b/framework-t/src/android/net/nsd/MDnsManager.java
index c11e60c..c7ded25 100644
--- a/framework-t/src/android/net/nsd/MDnsManager.java
+++ b/framework-t/src/android/net/nsd/MDnsManager.java
@@ -51,7 +51,7 @@
public void startDaemon() {
try {
mMdns.startDaemon();
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
Log.e(TAG, "Start mdns failed.", e);
}
}
@@ -62,7 +62,7 @@
public void stopDaemon() {
try {
mMdns.stopDaemon();
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
Log.e(TAG, "Stop mdns failed.", e);
}
}
@@ -85,7 +85,7 @@
registrationType, port, txtRecord, interfaceIdx);
try {
mMdns.registerService(info);
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
Log.e(TAG, "Register service failed.", e);
return false;
}
@@ -105,7 +105,7 @@
registrationType, "" /* domainName */, interfaceIdx, NETID_UNSET);
try {
mMdns.discover(info);
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
Log.e(TAG, "Discover service failed.", e);
return false;
}
@@ -129,7 +129,7 @@
new byte[0] /* txtRecord */, interfaceIdx);
try {
mMdns.resolve(info);
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
Log.e(TAG, "Resolve service failed.", e);
return false;
}
@@ -149,7 +149,7 @@
"" /* address */, interfaceIdx, NETID_UNSET);
try {
mMdns.getServiceAddress(info);
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
Log.e(TAG, "Get service address failed.", e);
return false;
}
@@ -165,7 +165,7 @@
public boolean stopOperation(int id) {
try {
mMdns.stopOperation(id);
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
Log.e(TAG, "Stop operation failed.", e);
return false;
}
@@ -180,7 +180,7 @@
public void registerEventListener(@NonNull IMDnsEventListener listener) {
try {
mMdns.registerEventListener(listener);
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
Log.e(TAG, "Register listener failed.", e);
}
}
@@ -193,7 +193,7 @@
public void unregisterEventListener(@NonNull IMDnsEventListener listener) {
try {
mMdns.unregisterEventListener(listener);
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
Log.e(TAG, "Unregister listener failed.", e);
}
}
diff --git a/framework/Android.bp b/framework/Android.bp
index c88bacc..73a1ed0 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -94,7 +94,9 @@
"framework-wifi.stubs.module_lib",
],
static_libs: [
- "mdns_aidl_interface-lateststable-java",
+ // Not using the latest stable version because all functions in the latest version of
+ // mdns_aidl_interface are deprecated.
+ "mdns_aidl_interface-V1-java",
"modules-utils-backgroundthread",
"modules-utils-build",
"modules-utils-preconditions",
@@ -301,6 +303,10 @@
],
flags: [
"--show-annotation android.annotation.FlaggedApi",
+ "--show-for-stub-purposes-annotation android.annotation.SystemApi" +
+ "\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
+ "--show-for-stub-purposes-annotation android.annotation.SystemApi" +
+ "\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)",
],
aidl: {
include_dirs: [
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index f959114..efae754 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -1457,6 +1457,18 @@
return mTransportTypes == (1 << transportType);
}
+ /**
+ * Returns true iff this NC has the specified transport and no other, ignoring TRANSPORT_TEST.
+ *
+ * If this NC has the passed transport and no other, this method returns true.
+ * If this NC has the passed transport, TRANSPORT_TEST and no other, this method returns true.
+ * Otherwise, this method returns false.
+ * @hide
+ */
+ public boolean hasSingleTransportBesidesTest(@Transport int transportType) {
+ return (mTransportTypes & ~(1 << TRANSPORT_TEST)) == (1 << transportType);
+ }
+
private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
return ((this.mTransportTypes == 0)
|| ((this.mTransportTypes & nc.mTransportTypes) != 0));
diff --git a/service/Android.bp b/service/Android.bp
index 76741bc..e2dab9e 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -185,7 +185,7 @@
"androidx.annotation_annotation",
"connectivity-net-module-utils-bpf",
"connectivity_native_aidl_interface-lateststable-java",
- "dnsresolver_aidl_interface-V12-java",
+ "dnsresolver_aidl_interface-V13-java",
"modules-utils-shell-command-handler",
"net-utils-device-common",
"net-utils-device-common-ip",
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index ba9ea86..8f29078 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -1362,8 +1362,8 @@
/**
* Create a HandlerThread to use in ConnectivityService.
*/
- public HandlerThread makeHandlerThread() {
- return new HandlerThread("ConnectivityServiceThread");
+ public HandlerThread makeHandlerThread(@NonNull final String tag) {
+ return new HandlerThread(tag);
}
/**
@@ -1458,7 +1458,7 @@
public CarrierPrivilegeAuthenticator makeCarrierPrivilegeAuthenticator(
@NonNull final Context context, @NonNull final TelephonyManager tm) {
if (isAtLeastT()) {
- return new CarrierPrivilegeAuthenticator(context, this, tm);
+ return new CarrierPrivilegeAuthenticator(context, tm);
} else {
return null;
}
@@ -1706,7 +1706,7 @@
mNetd = netd;
mBpfNetMaps = mDeps.getBpfNetMaps(mContext, netd);
- mHandlerThread = mDeps.makeHandlerThread();
+ mHandlerThread = mDeps.makeHandlerThread("ConnectivityServiceThread");
mPermissionMonitor =
new PermissionMonitor(mContext, mNetd, mBpfNetMaps, mHandlerThread);
mHandlerThread.start();
@@ -2649,6 +2649,13 @@
}
}
+ private boolean canSeeAllowedUids(final int pid, final int uid, final int netOwnerUid) {
+ return Process.SYSTEM_UID == uid
+ || netOwnerUid == uid
+ || checkAnyPermissionOf(mContext, pid, uid,
+ android.Manifest.permission.NETWORK_FACTORY);
+ }
+
@VisibleForTesting
NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions(
NetworkCapabilities nc, int callerPid, int callerUid) {
@@ -2670,8 +2677,7 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)) {
newNc.setAdministratorUids(new int[0]);
}
- if (!checkAnyPermissionOf(mContext,
- callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
+ if (!canSeeAllowedUids(callerPid, callerUid, newNc.getOwnerUid())) {
newNc.setAllowedUids(new ArraySet<>());
newNc.setSubscriptionIds(Collections.emptySet());
}
@@ -3944,6 +3950,11 @@
pw.println();
dumpBpfProgramStatus(pw);
+ if (null != mCarrierPrivilegeAuthenticator) {
+ pw.println();
+ mCarrierPrivilegeAuthenticator.dump(pw);
+ }
+
pw.println();
if (!CollectionUtils.contains(args, SHORT_ARG)) {
@@ -5252,7 +5263,7 @@
private boolean hasCarrierPrivilegeForNetworkCaps(final int callingUid,
@NonNull final NetworkCapabilities caps) {
if (mCarrierPrivilegeAuthenticator != null) {
- return mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ return mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
callingUid, caps);
}
return false;
diff --git a/service/src/com/android/server/NetIdManager.java b/service/src/com/android/server/NetIdManager.java
index 61925c8..27b6b9b 100644
--- a/service/src/com/android/server/NetIdManager.java
+++ b/service/src/com/android/server/NetIdManager.java
@@ -27,6 +27,16 @@
* Class used to reserve and release net IDs.
*
* <p>Instances of this class are thread-safe.
+ *
+ * NetIds are currently 16 bits long and consume 16 bits in the fwmark.
+ * The reason they are large is that applications might get confused if the netId counter
+ * wraps - for example, Network#equals would return true for a current network
+ * and a long-disconnected network.
+ * We could in theory fix this by splitting the identifier in two, e.g., a 24-bit generation
+ * counter and an 8-bit netId. Java Network objects would be constructed from the full 32-bit
+ * number, but only the 8-bit number would be used by netd and the fwmark.
+ * We'd have to fix all code that assumes that it can take a netId or a mark and construct
+ * a Network object from it.
*/
public class NetIdManager {
// Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
diff --git a/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java b/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
index ab7b1a7..5705ebe 100644
--- a/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
+++ b/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
@@ -17,6 +17,7 @@
package com.android.server.connectivity;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static com.android.server.connectivity.ConnectivityFlags.CARRIER_SERVICE_CHANGED_USE_CALLBACK;
@@ -31,6 +32,8 @@
import android.net.NetworkCapabilities;
import android.net.NetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
+import android.net.TransportInfo;
+import android.net.wifi.WifiInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -41,12 +44,13 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.modules.utils.HandlerExecutor;
+import com.android.net.module.util.DeviceConfigUtils;
import com.android.networkstack.apishim.TelephonyManagerShimImpl;
import com.android.networkstack.apishim.common.TelephonyManagerShim;
import com.android.networkstack.apishim.common.TelephonyManagerShim.CarrierPrivilegesListenerShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
-import com.android.server.ConnectivityService;
import java.util.ArrayList;
import java.util.List;
@@ -77,13 +81,13 @@
private final boolean mUseCallbacksForServiceChanged;
public CarrierPrivilegeAuthenticator(@NonNull final Context c,
- @NonNull final ConnectivityService.Dependencies deps,
+ @NonNull final Dependencies deps,
@NonNull final TelephonyManager t,
@NonNull final TelephonyManagerShim telephonyManagerShim) {
mContext = c;
mTelephonyManager = t;
mTelephonyManagerShim = telephonyManagerShim;
- final HandlerThread thread = new HandlerThread(TAG);
+ final HandlerThread thread = deps.makeHandlerThread();
thread.start();
mHandler = new Handler(thread.getLooper());
mUseCallbacksForServiceChanged = deps.isFeatureEnabled(
@@ -109,9 +113,24 @@
}
public CarrierPrivilegeAuthenticator(@NonNull final Context c,
- @NonNull final ConnectivityService.Dependencies deps,
@NonNull final TelephonyManager t) {
- this(c, deps, t, TelephonyManagerShimImpl.newInstance(t));
+ this(c, new Dependencies(), t, TelephonyManagerShimImpl.newInstance(t));
+ }
+
+ public static class Dependencies {
+ /**
+ * Create a HandlerThread to use in CarrierPrivilegeAuthenticator.
+ */
+ public HandlerThread makeHandlerThread() {
+ return new HandlerThread(TAG);
+ }
+
+ /**
+ * @see DeviceConfigUtils#isTetheringFeatureEnabled
+ */
+ public boolean isFeatureEnabled(Context context, String name) {
+ return DeviceConfigUtils.isTetheringFeatureEnabled(context, name);
+ }
}
private void simConfigChanged() {
@@ -125,11 +144,13 @@
private class PrivilegeListener implements CarrierPrivilegesListenerShim {
public final int mLogicalSlot;
+
PrivilegeListener(final int logicalSlot) {
mLogicalSlot = logicalSlot;
}
- @Override public void onCarrierPrivilegesChanged(
+ @Override
+ public void onCarrierPrivilegesChanged(
@NonNull List<String> privilegedPackageNames,
@NonNull int[] privilegedUids) {
if (mUseCallbacksForServiceChanged) return;
@@ -193,12 +214,13 @@
*
* This returns whether the passed UID is the carrier service package for the subscription ID
* stored in the telephony network specifier in the passed network capabilities.
- * If the capabilities don't code for a cellular network, or if they don't have the
+ * If the capabilities don't code for a cellular or Wi-Fi network, or if they don't have the
* subscription ID in their specifier, this returns false.
*
- * This method can be used to check that a network request for {@link NET_CAPABILITY_CBS} is
- * allowed for the UID of a caller, which must hold carrier privilege and provide the carrier
- * config.
+ * This method can be used to check that a network request that requires the UID to be
+ * the carrier service UID is indeed called by such a UID. An example of such a network could
+ * be a network with the {@link android.net.NetworkCapabilities#NET_CAPABILITY_CBS}
+ * capability.
* It can also be used to check that a factory is entitled to grant access to a given network
* to a given UID on grounds that it is the carrier service package.
*
@@ -206,11 +228,28 @@
* @param networkCapabilities the network capabilities for which carrier privilege is checked.
* @return true if uid provides the relevant carrier config else false.
*/
- public boolean hasCarrierPrivilegeForNetworkCapabilities(int callingUid,
+ public boolean isCarrierServiceUidForNetworkCapabilities(int callingUid,
@NonNull NetworkCapabilities networkCapabilities) {
if (callingUid == Process.INVALID_UID) return false;
- if (!networkCapabilities.hasSingleTransport(TRANSPORT_CELLULAR)) return false;
- final int subId = getSubIdFromNetworkSpecifier(networkCapabilities.getNetworkSpecifier());
+ final int subId;
+ if (networkCapabilities.hasSingleTransportBesidesTest(TRANSPORT_CELLULAR)) {
+ subId = getSubIdFromTelephonySpecifier(networkCapabilities.getNetworkSpecifier());
+ } else if (networkCapabilities.hasSingleTransportBesidesTest(TRANSPORT_WIFI)) {
+ subId = getSubIdFromWifiTransportInfo(networkCapabilities.getTransportInfo());
+ } else {
+ subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ && !networkCapabilities.getSubscriptionIds().contains(subId)) {
+ // Ideally, the code above should just use networkCapabilities.getSubscriptionIds()
+ // for simplicity and future-proofing. However, this is not the historical behavior,
+ // and there is no enforcement that they do not differ, so log a terrible failure if
+ // they do not match to gain confidence this never happens.
+ // TODO : when there is confidence that this never happens, rewrite the code above
+ // with NetworkCapabilities#getSubscriptionIds.
+ Log.wtf(TAG, "NetworkCapabilities subIds are inconsistent between "
+ + "specifier/transportInfo and mSubIds : " + networkCapabilities);
+ }
if (SubscriptionManager.INVALID_SUBSCRIPTION_ID == subId) return false;
return callingUid == getCarrierServiceUidForSubId(subId);
}
@@ -239,14 +278,6 @@
}
@VisibleForTesting
- int getSubIdFromNetworkSpecifier(NetworkSpecifier specifier) {
- if (specifier instanceof TelephonyNetworkSpecifier) {
- return ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
- }
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- }
-
- @VisibleForTesting
int getUidForPackage(String pkgName) {
if (pkgName == null) {
return Process.INVALID_UID;
@@ -271,8 +302,22 @@
return getUidForPackage(getCarrierServicePackageNameForLogicalSlot(slotId));
}
- // Helper methods to avoid having to deal with UnsupportedApiLevelException.
+ @VisibleForTesting
+ int getSubIdFromTelephonySpecifier(@Nullable final NetworkSpecifier specifier) {
+ if (specifier instanceof TelephonyNetworkSpecifier) {
+ return ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ int getSubIdFromWifiTransportInfo(@Nullable final TransportInfo info) {
+ if (info instanceof WifiInfo) {
+ return ((WifiInfo) info).getSubscriptionId();
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ // Helper methods to avoid having to deal with UnsupportedApiLevelException.
private void addCarrierPrivilegesListener(@NonNull final Executor executor,
@NonNull final PrivilegeListener listener) {
try {
@@ -292,4 +337,16 @@
Log.e(TAG, "removeCarrierPrivilegesListener API is not available");
}
}
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("CarrierPrivilegeAuthenticator:");
+ synchronized (mLock) {
+ final int size = mCarrierServiceUid.size();
+ for (int i = 0; i < size; ++i) {
+ final int logicalSlot = mCarrierServiceUid.keyAt(i);
+ final int serviceUid = mCarrierServiceUid.valueAt(i);
+ pw.println("Logical slot = " + logicalSlot + " : uid = " + serviceUid);
+ }
+ }
+ }
}
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
index 5aac8f1..f8f76ef 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -33,7 +33,6 @@
public static final String NO_REMATCH_ALL_REQUESTS_ON_REGISTER =
"no_rematch_all_requests_on_register";
- @VisibleForTesting
public static final String CARRIER_SERVICE_CHANGED_USE_CALLBACK =
"carrier_service_changed_use_callback_version";
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 7cd3cc8..50cad45 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -17,11 +17,13 @@
package com.android.server.connectivity;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.transportNamesOf;
import android.annotation.NonNull;
@@ -1549,7 +1551,7 @@
* @param hasAutomotiveFeature true if this device has the automotive feature, false otherwise
* @param authenticator the carrier privilege authenticator to check for telephony constraints
*/
- public static void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc,
+ public void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc,
final int creatorUid, final boolean hasAutomotiveFeature,
@NonNull final ConnectivityService.Dependencies deps,
@Nullable final CarrierPrivilegeAuthenticator authenticator) {
@@ -1562,7 +1564,7 @@
}
}
- private static boolean areAllowedUidsAcceptableFromNetworkAgent(
+ private boolean areAllowedUidsAcceptableFromNetworkAgent(
@NonNull final NetworkCapabilities nc, final boolean hasAutomotiveFeature,
@NonNull final ConnectivityService.Dependencies deps,
@Nullable final CarrierPrivilegeAuthenticator carrierPrivilegeAuthenticator) {
@@ -1575,21 +1577,28 @@
// On a non-restricted network, access UIDs make no sense
if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) return false;
- // If this network has TRANSPORT_TEST, then the caller can do whatever they want to
- // access UIDs
- if (nc.hasTransport(TRANSPORT_TEST)) return true;
+ // If this network has TRANSPORT_TEST and nothing else, then the caller can do whatever
+ // they want to access UIDs
+ if (nc.hasSingleTransport(TRANSPORT_TEST)) return true;
- // Factories that make ethernet networks can allow UIDs for automotive devices.
- if (nc.hasSingleTransport(TRANSPORT_ETHERNET) && hasAutomotiveFeature) {
- return true;
+ if (nc.hasTransport(TRANSPORT_ETHERNET)) {
+ // Factories that make ethernet networks can allow UIDs for automotive devices.
+ if (hasAutomotiveFeature) return true;
+ // It's also admissible if the ethernet network has TRANSPORT_TEST, as long as it
+ // doesn't have NET_CAPABILITY_INTERNET so it can't become the default network.
+ if (nc.hasTransport(TRANSPORT_TEST) && !nc.hasCapability(NET_CAPABILITY_INTERNET)) {
+ return true;
+ }
+ return false;
}
- // Factories that make cell networks can allow the UID for the carrier service package.
+ // Factories that make cell/wifi networks can allow the UID for the carrier service package.
// This can only work in T where there is support for CarrierPrivilegeAuthenticator
if (null != carrierPrivilegeAuthenticator
- && nc.hasSingleTransport(TRANSPORT_CELLULAR)
+ && (nc.hasSingleTransportBesidesTest(TRANSPORT_CELLULAR)
+ || nc.hasSingleTransportBesidesTest(TRANSPORT_WIFI))
&& (1 == nc.getAllowedUidsNoCopy().size())
- && (carrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ && (carrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
nc.getAllowedUidsNoCopy().valueAt(0), nc))) {
return true;
}
diff --git a/staticlibs/native/bpf_headers/BpfRingbufTest.cpp b/staticlibs/native/bpf_headers/BpfRingbufTest.cpp
index e4de812..e81fb92 100644
--- a/staticlibs/native/bpf_headers/BpfRingbufTest.cpp
+++ b/staticlibs/native/bpf_headers/BpfRingbufTest.cpp
@@ -74,11 +74,27 @@
ASSERT_RESULT_OK(result);
EXPECT_TRUE(result.value()->isEmpty());
+ struct timespec t1, t2;
+ EXPECT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &t1));
+ EXPECT_FALSE(result.value()->wait(1000 /*ms*/)); // false because wait should timeout
+ EXPECT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &t2));
+ long long time1 = t1.tv_sec * 1000000000LL + t1.tv_nsec;
+ long long time2 = t2.tv_sec * 1000000000LL + t2.tv_nsec;
+ EXPECT_GE(time2 - time1, 1000000000 /*ns*/); // 1000 ms as ns
+
for (int i = 0; i < n; i++) {
RunProgram();
}
EXPECT_FALSE(result.value()->isEmpty());
+
+ EXPECT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &t1));
+ EXPECT_TRUE(result.value()->wait());
+ EXPECT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &t2));
+ time1 = t1.tv_sec * 1000000000LL + t1.tv_nsec;
+ time2 = t2.tv_sec * 1000000000LL + t2.tv_nsec;
+ EXPECT_LE(time2 - time1, 1000000 /*ns*/); // in x86 CF testing < 5000 ns
+
EXPECT_THAT(result.value()->ConsumeAll(callback), HasValue(n));
EXPECT_TRUE(result.value()->isEmpty());
EXPECT_EQ(output, TEST_RINGBUF_MAGIC_NUM);
diff --git a/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h b/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
index 9aff790..d716358 100644
--- a/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
+++ b/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
@@ -19,6 +19,7 @@
#include <android-base/result.h>
#include <android-base/unique_fd.h>
#include <linux/bpf.h>
+#include <poll.h>
#include <sys/mman.h>
#include <utils/Log.h>
@@ -41,6 +42,9 @@
bool isEmpty(void);
+ // returns !isEmpty() for convenience
+ bool wait(int timeout_ms = -1);
+
protected:
// Non-initializing constructor, used by Create.
BpfRingbufBase(size_t value_size) : mValueSize(value_size) {}
@@ -200,12 +204,21 @@
}
inline bool BpfRingbufBase::isEmpty(void) {
- uint32_t prod_pos = mProducerPos->load(std::memory_order_acquire);
- // Only userspace writes to mConsumerPos, so no need to use std::memory_order_acquire
+ uint32_t prod_pos = mProducerPos->load(std::memory_order_relaxed);
uint64_t cons_pos = mConsumerPos->load(std::memory_order_relaxed);
return (cons_pos & 0xFFFFFFFF) == prod_pos;
}
+inline bool BpfRingbufBase::wait(int timeout_ms) {
+ // possible optimization: if (!isEmpty()) return true;
+ struct pollfd pfd = { // 1-element array
+ .fd = mRingFd.get(),
+ .events = POLLIN,
+ };
+ (void)poll(&pfd, 1, timeout_ms); // 'best effort' poll
+ return !isEmpty();
+}
+
inline base::Result<int> BpfRingbufBase::ConsumeAll(
const std::function<void(const void*)>& callback) {
int64_t count = 0;
diff --git a/staticlibs/netd/Android.bp b/staticlibs/netd/Android.bp
index 56565ed..637a938 100644
--- a/staticlibs/netd/Android.bp
+++ b/staticlibs/netd/Android.bp
@@ -225,19 +225,6 @@
}
-java_library {
- name: "mdns_aidl_interface-lateststable-java",
- sdk_version: "module_current",
- min_sdk_version: "30",
- static_libs: [
- "mdns_aidl_interface-V1-java",
- ],
- apex_available: [
- "//apex_available:platform",
- "com.android.tethering",
- ],
-}
-
aidl_interface {
name: "mdns_aidl_interface",
local_include_dir: "binder",
diff --git a/staticlibs/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt b/staticlibs/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt
index d75d9ca..df6067d 100644
--- a/staticlibs/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt
+++ b/staticlibs/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt
@@ -61,8 +61,8 @@
commonError)
}
assertTrue(tm.isDataConnectivityPossible,
- "The device is not setup with a SIM card that supports data connectivity. " +
- commonError)
+ "The device has a SIM card, but it does not supports data connectivity. " +
+ "Check the data plan, and verify that mobile data is working. " + commonError)
connectUtil.ensureCellularValidated()
}
}
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt b/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
index 1ba83ca..10accd4 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
@@ -17,9 +17,9 @@
package com.android.testutils
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
-import java.lang.IllegalStateException
import java.lang.reflect.Modifier
import org.junit.runner.Description
import org.junit.runner.Runner
@@ -110,10 +110,19 @@
notifier.fireTestStarted(leakMonitorDesc)
val threadCountsAfterTest = getAllThreadNameCounts()
- if (threadCountsBeforeTest != threadCountsAfterTest) {
+ // TODO : move CompareOrUpdateResult to its own util instead of LinkProperties.
+ val threadsDiff = CompareOrUpdateResult(
+ threadCountsBeforeTest.entries,
+ threadCountsAfterTest.entries
+ ) { it.key }
+ // Ignore removed threads, which typically are generated by previous tests.
+ // Because this is in the threadsDiff.updated member, for sure there is a
+ // corresponding key in threadCountsBeforeTest.
+ val increasedThreads = threadsDiff.updated
+ .filter { threadCountsBeforeTest[it.key]!! < it.value }
+ if (threadsDiff.added.isNotEmpty() || increasedThreads.isNotEmpty()) {
notifier.fireTestFailure(Failure(leakMonitorDesc,
- IllegalStateException("Expected threads: $threadCountsBeforeTest " +
- "but got: $threadCountsAfterTest")))
+ IllegalStateException("Unexpected thread changes: $threadsDiff")))
}
notifier.fireTestFinished(leakMonitorDesc)
}
@@ -121,9 +130,13 @@
private fun getAllThreadNameCounts(): Map<String, Int> {
// Get the counts of threads in the group per name.
// Filter system thread groups.
+ // Also ignore threads with 1 count, this effectively filtered out threads created by the
+ // test runner or other system components. e.g. hwuiTask*, queued-work-looper,
+ // SurfaceSyncGroupTimer, RenderThread, Time-limited test, etc.
return Thread.getAllStackTraces().keys
.filter { it.threadGroup?.name != "system" }
.groupingBy { it.name }.eachCount()
+ .filter { it.value != 1 }
}
override fun getDescription(): Description {
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index b86de25..9310888 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -62,10 +62,13 @@
// uncomment when b/13249961 is fixed
// sdk_version: "current",
platform_apis: true,
- data: [":ConnectivityTestPreparer"],
per_testcase_directory: true,
host_required: ["net-tests-utils-host-common"],
test_config_template: "AndroidTestTemplate.xml",
+ data: [
+ ":ConnectivityTestPreparer",
+ ":CtsCarrierServicePackage",
+ ]
}
// Networking CTS tests for development and release. These tests always target the platform SDK
@@ -154,3 +157,17 @@
package_name: "android.net.cts.maxtargetsdk30", // CTS package names must be unique.
instrumentation_target_package: "android.net.cts.maxtargetsdk30",
}
+
+android_test_helper_app {
+ name: "CtsCarrierServicePackage",
+ defaults: ["cts_defaults"],
+ package_name: "android.net.cts.carrierservicepackage",
+ manifest: "carrierservicepackage/AndroidManifest.xml",
+ srcs: ["carrierservicepackage/src/**/*.java"],
+ min_sdk_version: "30",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+}
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index 68e36ff..098cc0a 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -36,6 +36,7 @@
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- This test also uses signature permissions through adopting the shell identity.
The permissions acquired that way include (probably not exhaustive) :
@@ -46,6 +47,7 @@
android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
+
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
@@ -54,4 +56,3 @@
</instrumentation>
</manifest>
-
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 8efa99f..38f26d8 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -27,6 +27,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="{MODULE}.apk" />
+ <option name="test-file-name" value="CtsCarrierServicePackage.apk" />
</target_preparer>
<target_preparer class="com.android.testutils.ConnectivityTestTargetPreparer">
</target_preparer>
diff --git a/tests/cts/net/carrierservicepackage/AndroidManifest.xml b/tests/cts/net/carrierservicepackage/AndroidManifest.xml
new file mode 100644
index 0000000..c2a45eb
--- /dev/null
+++ b/tests/cts/net/carrierservicepackage/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:versionCode="1"
+ android:versionName="1.0.0"
+ package="android.net.cts.carrierservicepackage">
+ <uses-sdk android:minSdkVersion="30"
+ android:targetSdkVersion="33" />
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <application android:allowBackup="false"
+ android:directBootAware="true">
+ <service android:name=".DummyCarrierConfigService"
+ android:permission="android.permission.BIND_CARRIER_SERVICES"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.carrier.CarrierService"/>
+ </intent-filter>
+ </service>
+ </application>
+
+</manifest>
diff --git a/tests/cts/net/carrierservicepackage/src/android/net/cts/carrierservicepackage/DummyCarrierConfigService.java b/tests/cts/net/carrierservicepackage/src/android/net/cts/carrierservicepackage/DummyCarrierConfigService.java
new file mode 100644
index 0000000..ca2015b
--- /dev/null
+++ b/tests/cts/net/carrierservicepackage/src/android/net/cts/carrierservicepackage/DummyCarrierConfigService.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts.carrierservicepackage;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.PersistableBundle;
+import android.service.carrier.CarrierIdentifier;
+import android.service.carrier.CarrierService;
+
+public class DummyCarrierConfigService extends CarrierService {
+ private static final String TAG = "DummyCarrierConfigService";
+
+ public DummyCarrierConfigService() {}
+
+ @Override
+ public PersistableBundle onLoadConfig(CarrierIdentifier id) {
+ return new PersistableBundle(); // Do nothing
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return super.onBind(intent);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ return super.onUnbind(intent);
+ }
+}
+
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 0a143c5..778f0c5 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -796,14 +796,15 @@
// Make sure that the NC is null if the package doesn't hold ACCESS_NETWORK_STATE.
assertNull(redactNc(nc, groundedUid, groundedPkg));
- // Uids, ssid, underlying networks & subscriptionIds will be redacted if the given uid
+ // Uids, ssid & underlying networks will be redacted if the given uid
// doesn't hold the associated permissions. The wifi transport info is also suitably
// redacted.
final NetworkCapabilities redactedNormal = redactNc(nc, normalUid, normalPkg);
assertNull(redactedNormal.getUids());
assertNull(redactedNormal.getSsid());
assertNull(redactedNormal.getUnderlyingNetworks());
- assertEquals(0, redactedNormal.getSubscriptionIds().size());
+ // Owner UID is allowed to see the subscription IDs.
+ assertEquals(2, redactedNormal.getSubscriptionIds().size());
assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS,
((WifiInfo) redactedNormal.getTransportInfo()).getBSSID());
assertEquals(rssi, ((WifiInfo) redactedNormal.getTransportInfo()).getRssi());
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 392cba9..225408c 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -15,9 +15,12 @@
*/
package android.net.cts
+import android.Manifest.permission.MODIFY_PHONE_STATE
import android.Manifest.permission.NETWORK_SETTINGS
+import android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
import android.app.Instrumentation
import android.content.Context
+import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.EthernetNetworkSpecifier
import android.net.INetworkAgent
@@ -44,7 +47,9 @@
import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
+import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
import android.net.NetworkCapabilities.TRANSPORT_TEST
import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkCapabilities.TRANSPORT_WIFI
@@ -53,6 +58,7 @@
import android.net.NetworkReleasedException
import android.net.NetworkRequest
import android.net.NetworkScore
+import android.net.NetworkSpecifier
import android.net.QosCallback
import android.net.QosCallback.QosCallbackRegistrationException
import android.net.QosCallbackException
@@ -61,6 +67,7 @@
import android.net.QosSocketInfo
import android.net.RouteInfo
import android.net.SocketKeepalive
+import android.net.TelephonyNetworkSpecifier
import android.net.TestNetworkInterface
import android.net.TestNetworkManager
import android.net.Uri
@@ -69,21 +76,31 @@
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnError
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionAvailable
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionLost
+import android.net.wifi.WifiInfo
import android.os.Build
+import android.os.ConditionVariable
import android.os.Handler
import android.os.HandlerThread
import android.os.Message
+import android.os.PersistableBundle
import android.os.Process
import android.os.SystemClock
import android.platform.test.annotations.AppModeFull
import android.system.OsConstants.IPPROTO_TCP
import android.system.OsConstants.IPPROTO_UDP
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
+import android.telephony.TelephonyManager.CarrierPrivilegesCallback
import android.telephony.data.EpsBearerQosSessionAttributes
+import android.util.ArraySet
import android.util.DebugUtils.valueToString
+import android.util.Log
import androidx.test.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.compatibility.common.util.ThrowingSupplier
+import com.android.compatibility.common.util.UiccUtil
import com.android.modules.utils.build.SdkLevel
import com.android.net.module.util.ArrayTrackRecord
import com.android.testutils.CompatUtil
@@ -112,23 +129,8 @@
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
import com.android.testutils.TestableNetworkCallback
import com.android.testutils.assertThrows
-import java.io.Closeable
-import java.io.IOException
-import java.net.DatagramSocket
-import java.net.InetAddress
-import java.net.InetSocketAddress
-import java.net.Socket
-import java.time.Duration
-import java.util.Arrays
-import java.util.UUID
-import java.util.concurrent.Executors
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-import kotlin.test.assertFalse
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import kotlin.test.assertTrue
-import kotlin.test.fail
+import com.android.testutils.runAsShell
+import com.android.testutils.tryTest
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -140,7 +142,26 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.timeout
import org.mockito.Mockito.verify
+import java.io.Closeable
+import java.io.IOException
+import java.net.DatagramSocket
+import java.net.InetAddress
+import java.net.InetSocketAddress
+import java.net.Socket
+import java.security.MessageDigest
+import java.time.Duration
+import java.util.Arrays
+import java.util.UUID
+import java.util.concurrent.Executors
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
+import kotlin.test.fail
+private const val TAG = "NetworkAgentTest"
// This test doesn't really have a constraint on how fast the methods should return. If it's
// going to fail, it will simply wait forever, so setting a high timeout lowers the flake ratio
// without affecting the run time of successful runs. Thus, set a very high timeout.
@@ -261,17 +282,18 @@
callbacksToCleanUp.add(callback)
}
- private fun makeTestNetworkRequest(specifier: String? = null): NetworkRequest {
- return NetworkRequest.Builder()
- .clearCapabilities()
- .addTransportType(TRANSPORT_TEST)
- .also {
- if (specifier != null) {
- it.setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier(specifier))
- }
- }
- .build()
- }
+ private fun String?.asEthSpecifier(): NetworkSpecifier? =
+ if (null == this) null else CompatUtil.makeEthernetNetworkSpecifier(this)
+ private fun makeTestNetworkRequest(specifier: NetworkSpecifier? = null) =
+ NetworkRequest.Builder().run {
+ clearCapabilities()
+ addTransportType(TRANSPORT_TEST)
+ if (specifier != null) setNetworkSpecifier(specifier)
+ build()
+ }
+
+ private fun makeTestNetworkRequest(specifier: String?) =
+ makeTestNetworkRequest(specifier.asEthSpecifier())
private fun makeTestNetworkCapabilities(
specifier: String? = null,
@@ -322,7 +344,7 @@
): Pair<TestableNetworkAgent, TestableNetworkCallback> {
val callback = TestableNetworkCallback()
// Ensure this NetworkAgent is never unneeded by filing a request with its specifier.
- requestNetwork(makeTestNetworkRequest(specifier = specifier), callback)
+ requestNetwork(makeTestNetworkRequest(specifier), callback)
val nc = makeTestNetworkCapabilities(specifier, transports)
val agent = createNetworkAgent(context, initialConfig = initialConfig, initialNc = nc)
agent.setTeardownDelayMillis(0)
@@ -543,6 +565,231 @@
.addTransportType(TRANSPORT_TEST)
.setAllowedUids(uids.toSet()).build()
+ /**
+ * Get the single element from this ArraySet, or fail() if doesn't contain exactly 1 element.
+ */
+ fun <T> ArraySet<T>.getSingleElement(): T {
+ if (size != 1) fail("Expected exactly one element, contained $size")
+ return iterator().next()
+ }
+
+ private fun doTestAllowedUids(
+ subId: Int,
+ transport: Int,
+ uid: Int,
+ expectUidsPresent: Boolean
+ ) {
+ doTestAllowedUids(subId, intArrayOf(transport), uid, expectUidsPresent)
+ }
+
+ private fun doTestAllowedUids(
+ subId: Int,
+ transports: IntArray,
+ uid: Int,
+ expectUidsPresent: Boolean
+ ) {
+ val callback = TestableNetworkCallback(DEFAULT_TIMEOUT_MS)
+ val specifier = when {
+ transports.size != 1 -> null
+ TRANSPORT_ETHERNET in transports -> EthernetNetworkSpecifier("testInterface")
+ TRANSPORT_CELLULAR in transports -> TelephonyNetworkSpecifier(subId)
+ else -> null
+ }
+ val agent = createNetworkAgent(initialNc = NetworkCapabilities.Builder().run {
+ addTransportType(TRANSPORT_TEST)
+ transports.forEach { addTransportType(it) }
+ addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ setNetworkSpecifier(specifier)
+ if (TRANSPORT_WIFI in transports && SdkLevel.isAtLeastV()) {
+ // setSubscriptionId only exists in V+
+ setTransportInfo(WifiInfo.Builder().setSubscriptionId(subId).build())
+ }
+ setAllowedUids(setOf(uid))
+ setOwnerUid(Process.myUid())
+ setAdministratorUids(intArrayOf(Process.myUid()))
+ build()
+ })
+ runWithShellPermissionIdentity {
+ agent.register()
+ }
+ agent.markConnected()
+
+ registerNetworkCallback(makeTestNetworkRequest(specifier), callback)
+ callback.expect<Available>(agent.network!!)
+ callback.expect<CapabilitiesChanged>(agent.network!!) {
+ if (expectUidsPresent) {
+ it.caps.allowedUidsNoCopy.getSingleElement() == uid
+ } else {
+ it.caps.allowedUidsNoCopy.isEmpty()
+ }
+ }
+ agent.unregister()
+ // callback will be unregistered in tearDown()
+ }
+
+ private fun setHoldCarrierPrivilege(hold: Boolean, subId: Int) {
+ fun getCertHash(): String {
+ val pkgInfo = realContext.packageManager.getPackageInfo(realContext.opPackageName,
+ PackageManager.GET_SIGNATURES)
+ val digest = MessageDigest.getInstance("SHA-256")
+ val certHash = digest.digest(pkgInfo.signatures!![0]!!.toByteArray())
+ return UiccUtil.bytesToHexString(certHash)!!
+ }
+
+ val tm = realContext.getSystemService(TelephonyManager::class.java)!!
+ val ccm = realContext.getSystemService(CarrierConfigManager::class.java)!!
+
+ val cv = ConditionVariable()
+ val cpb = PrivilegeWaiterCallback(cv)
+ tryTest {
+ val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
+ }
+ // Wait for the callback to be registered
+ assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't register CarrierPrivilegesCallback")
+ if (cpb.hasPrivilege == hold) {
+ if (hold) {
+ Log.w(TAG, "Package ${realContext.opPackageName} already is privileged")
+ } else {
+ Log.w(TAG, "Package ${realContext.opPackageName} already isn't privileged")
+ }
+ return@tryTest
+ }
+ cv.close()
+ runAsShell(MODIFY_PHONE_STATE) {
+ val carrierConfigs = if (hold) {
+ PersistableBundle().also {
+ it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
+ arrayOf(getCertHash()))
+ }
+ } else {
+ null
+ }
+ ccm.overrideConfig(subId, carrierConfigs)
+ }
+ assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't change carrier privilege")
+ } cleanup {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ tm.unregisterCarrierPrivilegesCallback(cpb)
+ }
+ }
+ }
+
+ private fun acquireCarrierPrivilege(subId: Int) = setHoldCarrierPrivilege(true, subId)
+ private fun dropCarrierPrivilege(subId: Int) = setHoldCarrierPrivilege(false, subId)
+
+ private fun setCarrierServicePackageOverride(subId: Int, pkg: String?) {
+ val tm = realContext.getSystemService(TelephonyManager::class.java)!!
+
+ val cv = ConditionVariable()
+ val cpb = CarrierServiceChangedWaiterCallback(cv)
+ tryTest {
+ val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
+ }
+ // Wait for the callback to be registered
+ assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't register CarrierPrivilegesCallback")
+ if (cpb.pkgName == pkg) {
+ Log.w(TAG, "Carrier service package was already $pkg")
+ return@tryTest
+ }
+ cv.close()
+ runAsShell(MODIFY_PHONE_STATE) {
+ if (null == pkg) {
+ // There is a bug is clear-carrier-service-package-override where not adding
+ // the -s argument will use the wrong slot index : b/299604822
+ runShellCommand("cmd phone clear-carrier-service-package-override" +
+ " -s $subId")
+ } else {
+ // -s could set the subId, but this test works with the default subId.
+ runShellCommand("cmd phone set-carrier-service-package-override $pkg")
+ }
+ }
+ assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't modify carrier service package")
+ } cleanup {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ tm.unregisterCarrierPrivilegesCallback(cpb)
+ }
+ }
+ }
+
+ private fun String.execute() = runShellCommand(this).trim()
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S)
+ fun testAllowedUids() {
+ // Use a different package than this one to make sure that a package that doesn't hold
+ // carrier service permission can be set as an allowed UID.
+ val servicePackage = "android.net.cts.carrierservicepackage"
+ val uid = try {
+ realContext.packageManager.getApplicationInfo(servicePackage, 0).uid
+ } catch (e: PackageManager.NameNotFoundException) {
+ fail("$servicePackage could not be installed, please check the SuiteApkInstaller" +
+ " installed CtsCarrierServicePackage.apk", e)
+ }
+
+ val tm = realContext.getSystemService(TelephonyManager::class.java)!!
+ val defaultSubId = SubscriptionManager.getDefaultSubscriptionId()
+ tryTest {
+ // This process is not the carrier service UID, so allowedUids should be ignored in all
+ // the following cases.
+ doTestAllowedUids(defaultSubId, TRANSPORT_CELLULAR, uid, expectUidsPresent = false)
+ doTestAllowedUids(defaultSubId, TRANSPORT_WIFI, uid, expectUidsPresent = false)
+ doTestAllowedUids(defaultSubId, TRANSPORT_BLUETOOTH, uid, expectUidsPresent = false)
+
+ // The tools to set the carrier service package override do not exist before U,
+ // so there is no way to test the rest of this test on < U.
+ if (!SdkLevel.isAtLeastU()) return@tryTest
+ // Acquiring carrier privilege is necessary to override the carrier service package.
+ val defaultSlotIndex = SubscriptionManager.getSlotIndex(defaultSubId)
+ acquireCarrierPrivilege(defaultSubId)
+ setCarrierServicePackageOverride(defaultSubId, servicePackage)
+ val actualServicePackage: String? = runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ tm.getCarrierServicePackageNameForLogicalSlot(defaultSlotIndex)
+ }
+ assertEquals(servicePackage, actualServicePackage)
+
+ // Wait for CarrierServiceAuthenticator to have seen the update of the service package
+ val timeout = SystemClock.elapsedRealtime() + DEFAULT_TIMEOUT_MS
+ while (true) {
+ if (SystemClock.elapsedRealtime() > timeout) {
+ fail("Couldn't make $servicePackage the service package for $defaultSubId: " +
+ "dumpsys connectivity".execute().split("\n")
+ .filter { it.contains("Logical slot = $defaultSlotIndex.*") })
+ }
+ if ("dumpsys connectivity"
+ .execute()
+ .split("\n")
+ .filter { it.contains("Logical slot = $defaultSlotIndex : uid = $uid") }
+ .isNotEmpty()) {
+ // Found the configuration
+ break
+ }
+ Thread.sleep(500)
+ }
+
+ // Cell and WiFi are allowed to set UIDs, but not Bluetooth or agents with multiple
+ // transports.
+ doTestAllowedUids(defaultSubId, TRANSPORT_CELLULAR, uid, expectUidsPresent = true)
+ if (SdkLevel.isAtLeastV()) {
+ // Cannot be tested before V because WifiInfo.Builder#setSubscriptionId doesn't
+ // exist
+ doTestAllowedUids(defaultSubId, TRANSPORT_WIFI, uid, expectUidsPresent = true)
+ }
+ doTestAllowedUids(defaultSubId, TRANSPORT_BLUETOOTH, uid, expectUidsPresent = false)
+ doTestAllowedUids(defaultSubId, intArrayOf(TRANSPORT_CELLULAR, TRANSPORT_WIFI), uid,
+ expectUidsPresent = false)
+ } cleanupStep {
+ if (SdkLevel.isAtLeastU()) setCarrierServicePackageOverride(defaultSubId, null)
+ } cleanup {
+ if (SdkLevel.isAtLeastU()) dropCarrierPrivilege(defaultSubId)
+ }
+ }
+
@Test
fun testRejectedUpdates() {
val callback = TestableNetworkCallback(DEFAULT_TIMEOUT_MS)
@@ -1497,3 +1744,25 @@
doTestNativeNetworkCreation(expectCreatedImmediately = true, intArrayOf(TRANSPORT_VPN))
}
}
+
+// Subclasses of CarrierPrivilegesCallback can't be inline, or they'll be compiled as
+// inner classes of the test class and will fail resolution on R as the test harness
+// uses reflection to list all methods and classes
+class PrivilegeWaiterCallback(private val cv: ConditionVariable) :
+ CarrierPrivilegesCallback {
+ var hasPrivilege = false
+ override fun onCarrierPrivilegesChanged(p: MutableSet<String>, uids: MutableSet<Int>) {
+ hasPrivilege = uids.contains(Process.myUid())
+ cv.open()
+ }
+}
+
+class CarrierServiceChangedWaiterCallback(private val cv: ConditionVariable) :
+ CarrierPrivilegesCallback {
+ var pkgName: String? = null
+ override fun onCarrierPrivilegesChanged(p: MutableSet<String>, u: MutableSet<Int>) {}
+ override fun onCarrierServiceChanged(pkgName: String?, uid: Int) {
+ this.pkgName = pkgName
+ cv.open()
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 9c44a3e..e1ea2b9 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -80,6 +80,7 @@
import com.android.net.module.util.PacketBuilder
import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
@@ -843,6 +844,8 @@
checkConnectSocketToMdnsd(shouldFail = false)
}
+ // Native mdns powered by Netd is removed after U.
+ @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test @CtsNetTestCasesMaxTargetSdk30("Socket is started with the service up to target SDK 30")
fun testManagerCreatesLegacySocket() {
nsdManager // Ensure the lazy-init member is initialized, so NsdManager is created
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index b8cf08e..9fad766 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -79,7 +79,6 @@
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
-import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF;
@@ -1659,8 +1658,7 @@
waitForIdle();
}
- public void startLegacyVpnPrivileged(VpnProfile profile,
- @Nullable Network underlying, @NonNull LinkProperties egress) {
+ public void startLegacyVpnPrivileged(VpnProfile profile) {
switch (profile.type) {
case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
@@ -1992,7 +1990,7 @@
}
@Override
- public HandlerThread makeHandlerThread() {
+ public HandlerThread makeHandlerThread(@NonNull final String tag) {
return mCsHandlerThread;
}
@@ -10252,7 +10250,7 @@
b.expectBroadcast();
// Simulate LockdownVpnTracker attempting to start the VPN since it received the
// systemDefault callback.
- mMockVpn.startLegacyVpnPrivileged(profile, mCellAgent.getNetwork(), cellLp);
+ mMockVpn.startLegacyVpnPrivileged(profile);
if (expectSetVpnDefaultForUids) {
// setVpnDefaultForUids() releases the original network request and creates a VPN
// request so LOST callback is received.
@@ -10323,7 +10321,7 @@
// callback with different network.
final ExpectedBroadcast b6 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
mMockVpn.stopVpnRunnerPrivileged();
- mMockVpn.startLegacyVpnPrivileged(profile, mWiFiAgent.getNetwork(), wifiLp);
+ mMockVpn.startLegacyVpnPrivileged(profile);
// VPN network is disconnected (to restart)
callback.expect(LOST, mMockVpn);
defaultCallback.expect(LOST, mMockVpn);
@@ -17386,6 +17384,7 @@
mCm.requestNetwork(new NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(TRANSPORT_TEST)
+ .addTransportType(TRANSPORT_CELLULAR)
.build(),
cb);
@@ -17531,7 +17530,7 @@
// In this test TEST_PACKAGE_UID will be the UID of the carrier service UID.
doReturn(true).when(mCarrierPrivilegeAuthenticator)
- .hasCarrierPrivilegeForNetworkCapabilities(eq(TEST_PACKAGE_UID), any());
+ .isCarrierServiceUidForNetworkCapabilities(eq(TEST_PACKAGE_UID), any());
// Simulate a restricted telephony network. The telephony factory is entitled to set
// the access UID to the service package on any of its restricted networks.
@@ -17596,17 +17595,18 @@
// TODO : fix the builder
ncb.setNetworkSpecifier(null);
ncb.removeTransportType(TRANSPORT_CELLULAR);
- ncb.addTransportType(TRANSPORT_WIFI);
+ ncb.addTransportType(TRANSPORT_BLUETOOTH);
// Wifi does not get to set access UID, even to the correct UID
mCm.requestNetwork(new NetworkRequest.Builder()
- .addTransportType(TRANSPORT_WIFI)
+ .addTransportType(TRANSPORT_BLUETOOTH)
.removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
.build(), cb);
- mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(), ncb.build());
- mWiFiAgent.connect(true);
- cb.expectAvailableThenValidatedCallbacks(mWiFiAgent);
+ final TestNetworkAgentWrapper bluetoothAgent = new TestNetworkAgentWrapper(
+ TRANSPORT_BLUETOOTH, new LinkProperties(), ncb.build());
+ bluetoothAgent.connect(true);
+ cb.expectAvailableThenValidatedCallbacks(bluetoothAgent);
ncb.setAllowedUids(serviceUidSet);
- mWiFiAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
+ bluetoothAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
cb.assertNoCallback(TEST_CALLBACK_TIMEOUT_MS);
mCm.unregisterNetworkCallback(cb);
}
diff --git a/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java b/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java
index 8113626..f07593e 100644
--- a/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java
@@ -45,6 +45,7 @@
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
import android.os.Build;
+import android.os.HandlerThread;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -52,10 +53,11 @@
import com.android.networkstack.apishim.TelephonyManagerShimImpl;
import com.android.networkstack.apishim.common.TelephonyManagerShim.CarrierPrivilegesListenerShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
-import com.android.server.ConnectivityService;
+import com.android.server.connectivity.CarrierPrivilegeAuthenticator.Dependencies;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
+import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -86,10 +88,11 @@
private final int mCarrierConfigPkgUid = 12345;
private final String mTestPkg = "com.android.server.connectivity.test";
private final BroadcastReceiver mMultiSimBroadcastReceiver;
+ @NonNull private final HandlerThread mHandlerThread;
public class TestCarrierPrivilegeAuthenticator extends CarrierPrivilegeAuthenticator {
TestCarrierPrivilegeAuthenticator(@NonNull final Context c,
- @NonNull final ConnectivityService.Dependencies deps,
+ @NonNull final Dependencies deps,
@NonNull final TelephonyManager t) {
super(c, deps, t, mTelephonyManagerShim);
}
@@ -100,6 +103,11 @@
}
}
+ @After
+ public void tearDown() {
+ mHandlerThread.quit();
+ }
+
/** Parameters to test both using callbacks or the old broadcast */
@Parameterized.Parameters
public static Collection<Boolean> shouldUseCallbacks() {
@@ -111,9 +119,11 @@
mTelephonyManager = mock(TelephonyManager.class);
mTelephonyManagerShim = mock(TelephonyManagerShimImpl.class);
mPackageManager = mock(PackageManager.class);
- final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
+ mHandlerThread = new HandlerThread(CarrierPrivilegeAuthenticatorTest.class.getSimpleName());
+ final Dependencies deps = mock(Dependencies.class);
doReturn(useCallbacks).when(deps).isFeatureEnabled(any() /* context */,
eq(CARRIER_SERVICE_CHANGED_USE_CALLBACK));
+ doReturn(mHandlerThread).when(deps).makeHandlerThread();
doReturn(SUBSCRIPTION_COUNT).when(mTelephonyManager).getActiveModemCount();
doReturn(mTestPkg).when(mTelephonyManagerShim)
.getCarrierServicePackageNameForLogicalSlot(anyInt());
@@ -164,9 +174,9 @@
.addTransportType(TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(0));
- assertTrue(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ assertTrue(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
mCarrierConfigPkgUid, ncBuilder.build()));
- assertFalse(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ assertFalse(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
mCarrierConfigPkgUid + 1, ncBuilder.build()));
}
@@ -203,9 +213,9 @@
.addTransportType(TRANSPORT_CELLULAR)
.setNetworkSpecifier(specifier)
.build();
- assertTrue(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ assertTrue(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
mCarrierConfigPkgUid, nc));
- assertFalse(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ assertFalse(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
mCarrierConfigPkgUid + 1, nc));
}
@@ -225,9 +235,9 @@
listener.onCarrierPrivilegesChanged(Collections.emptyList(), new int[] {});
listener.onCarrierServiceChanged(null, applicationInfo.uid);
- assertFalse(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ assertFalse(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
mCarrierConfigPkgUid, nc));
- assertTrue(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ assertTrue(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
mCarrierConfigPkgUid + 1, nc));
}
@@ -238,11 +248,11 @@
final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder();
ncBuilder.addTransportType(TRANSPORT_CELLULAR);
- assertFalse(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ assertFalse(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
mCarrierConfigPkgUid, ncBuilder.build()));
ncBuilder.setNetworkSpecifier(new TelephonyNetworkSpecifier(0));
- assertTrue(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ assertTrue(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
mCarrierConfigPkgUid, ncBuilder.build()));
// The builder for NetworkCapabilities doesn't allow removing the transport as long as a
@@ -251,7 +261,7 @@
ncBuilder.removeTransportType(TRANSPORT_CELLULAR);
ncBuilder.addTransportType(TRANSPORT_WIFI);
ncBuilder.setNetworkSpecifier(new TelephonyNetworkSpecifier(0));
- assertFalse(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
+ assertFalse(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
mCarrierConfigPkgUid, ncBuilder.build()));
}
}
diff --git a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
index 545ed16..afb9abd 100644
--- a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
@@ -140,7 +140,8 @@
assertResolverOptionsEquals(actual.resolverOptions, expected.resolverOptions);
assertContainsExactly(actual.transportTypes, expected.transportTypes);
assertEquals(actual.meteredNetwork, expected.meteredNetwork);
- assertFieldCountEquals(17, ResolverParamsParcel.class);
+ assertEquals(actual.dohParams, expected.dohParams);
+ assertFieldCountEquals(18, ResolverParamsParcel.class);
}
@Before
@@ -381,6 +382,7 @@
expectedParams.transportTypes = TEST_TRANSPORT_TYPES;
expectedParams.resolverOptions = null;
expectedParams.meteredNetwork = true;
+ expectedParams.dohParams = null;
assertResolverParamsEquals(actualParams, expectedParams);
}
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 109c132..ea2228e 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -2039,16 +2039,7 @@
private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
setMockedUsers(PRIMARY_USER);
-
- // Dummy egress interface
- final LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(EGRESS_IFACE);
-
- final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
- InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE);
- lp.addRoute(defaultRoute);
-
- vpn.startLegacyVpn(vpnProfile, EGRESS_NETWORK, lp);
+ vpn.startLegacyVpn(vpnProfile);
return vpn;
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index 4f5cfc0..f4c62c5 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -176,7 +176,7 @@
override fun getClatCoordinator(netd: INetd?) = this@CSTest.clatCoordinator
override fun getNetworkStack() = this@CSTest.networkStack
- override fun makeHandlerThread() = csHandlerThread
+ override fun makeHandlerThread(tag: String) = csHandlerThread
override fun makeProxyTracker(context: Context, connServiceHandler: Handler) = proxyTracker
override fun makeCarrierPrivilegeAuthenticator(
diff --git a/thread/service/Android.bp b/thread/service/Android.bp
index bd265e6..35ae3c2 100644
--- a/thread/service/Android.bp
+++ b/thread/service/Android.bp
@@ -43,6 +43,9 @@
"ot-daemon-aidl-java",
],
apex_available: ["com.android.tethering"],
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
}
cc_library_shared {
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 33516aa..60c97bf 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -14,6 +14,10 @@
package com.android.server.thread;
+import static android.net.MulticastRoutingConfig.CONFIG_FORWARD_NONE;
+import static android.net.MulticastRoutingConfig.FORWARD_NONE;
+import static android.net.MulticastRoutingConfig.FORWARD_SELECTED;
+import static android.net.MulticastRoutingConfig.FORWARD_WITH_MIN_SCOPE;
import static android.net.thread.ActiveOperationalDataset.CHANNEL_PAGE_24_GHZ;
import static android.net.thread.ActiveOperationalDataset.LENGTH_EXTENDED_PAN_ID;
import static android.net.thread.ActiveOperationalDataset.LENGTH_MESH_LOCAL_PREFIX_BITS;
@@ -51,13 +55,20 @@
import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.LocalNetworkConfig;
+import android.net.MulticastRoutingConfig;
+import android.net.LocalNetworkInfo;
+import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
+import android.net.NetworkRequest;
import android.net.NetworkScore;
+import android.net.RouteInfo;
import android.net.thread.ActiveOperationalDataset;
import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
import android.net.thread.IActiveOperationalDatasetReceiver;
@@ -85,8 +96,10 @@
import com.android.server.thread.openthread.IOtStatusReceiver;
import com.android.server.thread.openthread.Ipv6AddressInfo;
import com.android.server.thread.openthread.OtDaemonState;
+import com.android.server.thread.openthread.BorderRouterConfigurationParcel;
import java.io.IOException;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SecureRandom;
@@ -131,6 +144,14 @@
private IOtDaemon mOtDaemon;
private NetworkAgent mNetworkAgent;
+ private MulticastRoutingConfig mUpstreamMulticastRoutingConfig = CONFIG_FORWARD_NONE;
+ private MulticastRoutingConfig mDownstreamMulticastRoutingConfig = CONFIG_FORWARD_NONE;
+ private Network mUpstreamNetwork;
+ private final NetworkRequest mUpstreamNetworkRequest;
+ private final HashMap<Network, String> mNetworkToInterface;
+ private final LocalNetworkConfig mLocalNetworkConfig;
+
+ private BorderRouterConfigurationParcel mBorderRouterConfig;
@VisibleForTesting
ThreadNetworkControllerService(
@@ -147,6 +168,18 @@
mOtDaemonSupplier = otDaemonSupplier;
mConnectivityManager = connectivityManager;
mTunIfController = tunIfController;
+ mUpstreamNetworkRequest =
+ new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
+ .build();
+ mLocalNetworkConfig =
+ new LocalNetworkConfig.Builder()
+ .setUpstreamSelector(mUpstreamNetworkRequest)
+ .build();
+ mNetworkToInterface = new HashMap<Network, String>();
+ mBorderRouterConfig = new BorderRouterConfigurationParcel();
}
public static ThreadNetworkControllerService newInstance(Context context) {
@@ -167,19 +200,24 @@
private static NetworkCapabilities newNetworkCapabilities() {
return new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_THREAD)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
.build();
}
- private static InetAddress addressInfoToInetAddress(Ipv6AddressInfo addressInfo) {
+ private static Inet6Address bytesToInet6Address(byte[] addressBytes) {
try {
- return InetAddress.getByAddress(addressInfo.address);
+ return (Inet6Address) Inet6Address.getByAddress(addressBytes);
} catch (UnknownHostException e) {
- // This is impossible unless the Thread daemon is critically broken
+ // This is unlikely to happen unless the Thread daemon is critically broken
return null;
}
}
+ private static InetAddress addressInfoToInetAddress(Ipv6AddressInfo addressInfo) {
+ return bytesToInet6Address(addressInfo.address);
+ }
+
private static LinkAddress newLinkAddress(Ipv6AddressInfo addressInfo) {
long deprecationTimeMillis =
addressInfo.isPreferred
@@ -244,11 +282,77 @@
mLinkProperties.setInterfaceName(TUN_IF_NAME);
mLinkProperties.setMtu(TunInterfaceController.MTU);
mConnectivityManager.registerNetworkProvider(mNetworkProvider);
+ requestUpstreamNetwork();
initializeOtDaemon();
});
}
+ private void requestUpstreamNetwork() {
+ mConnectivityManager.registerNetworkCallback(
+ mUpstreamNetworkRequest,
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ Log.i(TAG, "onAvailable: " + network);
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ Log.i(TAG, "onLost: " + network);
+ }
+
+ @Override
+ public void onLinkPropertiesChanged(
+ @NonNull Network network, @NonNull LinkProperties linkProperties) {
+ Log.i(
+ TAG,
+ String.format(
+ "onLinkPropertiesChanged: {network: %s, interface: %s}",
+ network, linkProperties.getInterfaceName()));
+ mNetworkToInterface.put(network, linkProperties.getInterfaceName());
+ if (network.equals(mUpstreamNetwork)) {
+ enableBorderRouting(mNetworkToInterface.get(mUpstreamNetwork));
+ }
+ }
+ },
+ mHandler);
+ }
+
+ private final class ThreadNetworkCallback extends ConnectivityManager.NetworkCallback {
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ Log.i(TAG, "onAvailable: Thread network Available");
+ }
+
+ @Override
+ public void onLocalNetworkInfoChanged(
+ @NonNull Network network, @NonNull LocalNetworkInfo localNetworkInfo) {
+ Log.i(TAG, "onLocalNetworkInfoChanged: " + localNetworkInfo);
+ if (localNetworkInfo.getUpstreamNetwork() == null) {
+ mUpstreamNetwork = null;
+ return;
+ }
+ if (!localNetworkInfo.getUpstreamNetwork().equals(mUpstreamNetwork)) {
+ mUpstreamNetwork = localNetworkInfo.getUpstreamNetwork();
+ if (mNetworkToInterface.containsKey(mUpstreamNetwork)) {
+ enableBorderRouting(mNetworkToInterface.get(mUpstreamNetwork));
+ }
+ }
+ }
+ }
+
+ private void requestThreadNetwork() {
+ mConnectivityManager.registerNetworkCallback(
+ new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_THREAD)
+ .removeForbiddenCapability(NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK)
+ .build(),
+ new ThreadNetworkCallback(),
+ mHandler);
+ }
+
private void registerThreadNetwork() {
if (mNetworkAgent != null) {
return;
@@ -258,6 +362,7 @@
new NetworkScore.Builder()
.setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_LOCAL_NETWORK)
.build();
+ requestThreadNetwork();
mNetworkAgent =
new NetworkAgent(
mContext,
@@ -265,6 +370,7 @@
TAG,
netCaps,
mLinkProperties,
+ mLocalNetworkConfig,
score,
new NetworkAgentConfig.Builder().build(),
mNetworkProvider) {};
@@ -304,10 +410,19 @@
}
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
@@ -557,6 +672,39 @@
}
}
+ private void enableBorderRouting(String infraIfName) {
+ if (mBorderRouterConfig.isBorderRoutingEnabled
+ && infraIfName.equals(mBorderRouterConfig.infraInterfaceName)) {
+ return;
+ }
+ Log.i(TAG, "enableBorderRouting on AIL: " + infraIfName);
+ try {
+ mBorderRouterConfig.infraInterfaceName = infraIfName;
+ mBorderRouterConfig.infraInterfaceIcmp6Socket =
+ InfraInterfaceController.createIcmp6Socket(infraIfName);
+ mBorderRouterConfig.isBorderRoutingEnabled = true;
+
+ mOtDaemon.configureBorderRouter(
+ mBorderRouterConfig,
+ new IOtStatusReceiver.Stub() {
+ @Override
+ public void onSuccess() {
+ Log.i(TAG, "configure border router successfully");
+ }
+
+ @Override
+ public void onError(int i, String s) {
+ Log.w(
+ TAG,
+ String.format(
+ "failed to configure border router: %d %s", i, s));
+ }
+ });
+ } catch (Exception e) {
+ Log.w(TAG, "enableBorderRouting failed: " + e);
+ }
+ }
+
private void handleThreadInterfaceStateChanged(boolean isUp) {
try {
mTunIfController.setInterfaceUp(isUp);
@@ -597,6 +745,100 @@
updateNetworkLinkProperties(linkAddress, isAdded);
}
+ private boolean isMulticastForwardingEnabled() {
+ return !(mUpstreamMulticastRoutingConfig.getForwardingMode() == FORWARD_NONE
+ && mDownstreamMulticastRoutingConfig.getForwardingMode() == FORWARD_NONE);
+ }
+
+ private void sendLocalNetworkConfig() {
+ if (mNetworkAgent == null) {
+ return;
+ }
+ final LocalNetworkConfig.Builder configBuilder = new LocalNetworkConfig.Builder();
+ LocalNetworkConfig localNetworkConfig =
+ configBuilder
+ .setUpstreamMulticastRoutingConfig(mUpstreamMulticastRoutingConfig)
+ .setDownstreamMulticastRoutingConfig(mDownstreamMulticastRoutingConfig)
+ .setUpstreamSelector(mUpstreamNetworkRequest)
+ .build();
+ mNetworkAgent.sendLocalNetworkConfig(localNetworkConfig);
+ Log.d(
+ TAG,
+ "Sent localNetworkConfig with upstreamConfig "
+ + mUpstreamMulticastRoutingConfig
+ + " downstreamConfig"
+ + mDownstreamMulticastRoutingConfig);
+ }
+
+ private void handleMulticastForwardingStateChanged(boolean isEnabled) {
+ if (isMulticastForwardingEnabled() == isEnabled) {
+ return;
+ }
+ if (isEnabled) {
+ // When multicast forwarding is enabled, setup upstream forwarding to any address
+ // with minimal scope 4
+ // setup downstream forwarding with addresses subscribed from Thread network
+ mUpstreamMulticastRoutingConfig =
+ new MulticastRoutingConfig.Builder(FORWARD_WITH_MIN_SCOPE, 4).build();
+ mDownstreamMulticastRoutingConfig =
+ new MulticastRoutingConfig.Builder(FORWARD_SELECTED).build();
+ } else {
+ // When multicast forwarding is disabled, set both upstream and downstream
+ // forwarding config to FORWARD_NONE.
+ mUpstreamMulticastRoutingConfig = CONFIG_FORWARD_NONE;
+ mDownstreamMulticastRoutingConfig = CONFIG_FORWARD_NONE;
+ }
+ sendLocalNetworkConfig();
+ Log.d(
+ TAG,
+ "Sent updated localNetworkConfig with multicast forwarding "
+ + (isEnabled ? "enabled" : "disabled"));
+ }
+
+ private void handleMulticastForwardingAddressChanged(byte[] addressBytes, boolean isAdded) {
+ Inet6Address address = bytesToInet6Address(addressBytes);
+ MulticastRoutingConfig newDownstreamConfig;
+ MulticastRoutingConfig.Builder builder;
+
+ if (mDownstreamMulticastRoutingConfig.getForwardingMode() !=
+ MulticastRoutingConfig.FORWARD_SELECTED) {
+ Log.e(
+ TAG,
+ "Ignore multicast listening address updates when downstream multicast "
+ + "forwarding mode is not FORWARD_SELECTED");
+ // Don't update the address set if downstream multicast forwarding is disabled.
+ return;
+ }
+ if (isAdded ==
+ mDownstreamMulticastRoutingConfig.getListeningAddresses().contains(address)) {
+ return;
+ }
+
+ builder = new MulticastRoutingConfig.Builder(FORWARD_SELECTED);
+ for (Inet6Address listeningAddress :
+ mDownstreamMulticastRoutingConfig.getListeningAddresses()) {
+ builder.addListeningAddress(listeningAddress);
+ }
+
+ if (isAdded) {
+ builder.addListeningAddress(address);
+ } else {
+ builder.clearListeningAddress(address);
+ }
+
+ newDownstreamConfig = builder.build();
+ if (!newDownstreamConfig.equals(mDownstreamMulticastRoutingConfig)) {
+ Log.d(
+ TAG,
+ "Multicast listening address "
+ + address.getHostAddress()
+ + " is "
+ + (isAdded ? "added" : "removed"));
+ mDownstreamMulticastRoutingConfig = newDownstreamConfig;
+ sendLocalNetworkConfig();
+ }
+ }
+
private static final class CallbackMetadata {
private static long gId = 0;
@@ -728,6 +970,7 @@
onInterfaceStateChanged(newState.isInterfaceUp);
onDeviceRoleChanged(newState.deviceRole, listenerId);
onPartitionIdChanged(newState.partitionId, listenerId);
+ onMulticastForwardingStateChanged(newState.multicastForwardingEnabled);
mState = newState;
ActiveOperationalDataset newActiveDataset;
@@ -836,9 +1079,19 @@
}
}
+ private void onMulticastForwardingStateChanged(boolean isEnabled) {
+ checkOnHandlerThread();
+ handleMulticastForwardingStateChanged(isEnabled);
+ }
+
@Override
public void onAddressChanged(Ipv6AddressInfo addressInfo, boolean isAdded) {
mHandler.post(() -> handleAddressChanged(addressInfo, isAdded));
}
+
+ @Override
+ public void onMulticastForwardingAddressChanged(byte[] address, boolean isAdded) {
+ mHandler.post(() -> handleMulticastForwardingAddressChanged(address, isAdded));
+ }
}
}
diff --git a/thread/service/proguard.flags b/thread/service/proguard.flags
new file mode 100644
index 0000000..5028982
--- /dev/null
+++ b/thread/service/proguard.flags
@@ -0,0 +1,4 @@
+# Ensure the callback methods are not stripped
+-keepclassmembers class **.ThreadNetworkControllerService$ThreadNetworkCallback {
+ *;
+}