Merge "Adjust owners for incremental NsdManager changes" into main
diff --git a/Cronet/tests/mts/jarjar_excludes.txt b/Cronet/tests/mts/jarjar_excludes.txt
index e8fd39b..a0ce5c2 100644
--- a/Cronet/tests/mts/jarjar_excludes.txt
+++ b/Cronet/tests/mts/jarjar_excludes.txt
@@ -1,3 +1,5 @@
+# Exclude some test prefixes, as they can't be found after being jarjared.
+com\.android\.testutils\..+
# jarjar-gen can't handle some kotlin object expression, exclude packages that include them
androidx\..+
kotlin\.test\..+
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 76e4af8..d33453c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -140,6 +140,9 @@
},
{
"name": "FrameworksNetTests"
+ },
+ {
+ "name": "NetHttpCoverageTests"
}
],
"mainline-presubmit": [
diff --git a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
index 898b124..4c9460b 100644
--- a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
@@ -17,6 +17,7 @@
package com.android.networkstack.tethering.apishim.api30;
import android.net.INetd;
+import android.net.IpPrefix;
import android.net.MacAddress;
import android.net.TetherStatsParcel;
import android.os.RemoteException;
@@ -81,14 +82,14 @@
@Override
public boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
- @NonNull MacAddress inDstMac, @NonNull MacAddress outSrcMac,
- @NonNull MacAddress outDstMac, int mtu) {
+ @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac,
+ @NonNull MacAddress outSrcMac, @NonNull MacAddress outDstMac, int mtu) {
return true;
}
@Override
- public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex,
- int upstreamIfindex, @NonNull MacAddress inDstMac) {
+ public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
+ @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac) {
return true;
}
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 3cad1c6..119fbc6 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -18,6 +18,9 @@
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
+import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
+
+import android.net.IpPrefix;
import android.net.MacAddress;
import android.system.ErrnoException;
import android.system.Os;
@@ -48,6 +51,9 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
/**
* Bpf coordinator class for API shims.
@@ -196,13 +202,23 @@
return true;
}
+ @NonNull
+ private TetherUpstream6Key makeUpstream6Key(int downstreamIfindex, @NonNull MacAddress inDstMac,
+ @NonNull IpPrefix sourcePrefix) {
+ byte[] prefixBytes = Arrays.copyOf(sourcePrefix.getRawAddress(), 8);
+ long prefix64 = ByteBuffer.wrap(prefixBytes).order(ByteOrder.BIG_ENDIAN).getLong();
+ return new TetherUpstream6Key(downstreamIfindex, inDstMac, prefix64);
+ }
+
@Override
public boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
- @NonNull MacAddress inDstMac, @NonNull MacAddress outSrcMac,
- @NonNull MacAddress outDstMac, int mtu) {
+ @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac,
+ @NonNull MacAddress outSrcMac, @NonNull MacAddress outDstMac, int mtu) {
if (!isInitialized()) return false;
+ // RFC7421_PREFIX_LENGTH = 64 which is the most commonly used IPv6 subnet prefix length.
+ if (sourcePrefix.getPrefixLength() != RFC7421_PREFIX_LENGTH) return false;
- final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfindex, inDstMac);
+ final TetherUpstream6Key key = makeUpstream6Key(downstreamIfindex, inDstMac, sourcePrefix);
final Tether6Value value = new Tether6Value(upstreamIfindex, outSrcMac,
outDstMac, OsConstants.ETH_P_IPV6, mtu);
try {
@@ -216,10 +232,12 @@
@Override
public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
- @NonNull MacAddress inDstMac) {
+ @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac) {
if (!isInitialized()) return false;
+ // RFC7421_PREFIX_LENGTH = 64 which is the most commonly used IPv6 subnet prefix length.
+ if (sourcePrefix.getPrefixLength() != RFC7421_PREFIX_LENGTH) return false;
- final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfindex, inDstMac);
+ final TetherUpstream6Key key = makeUpstream6Key(downstreamIfindex, inDstMac, sourcePrefix);
try {
mBpfUpstream6Map.deleteEntry(key);
} catch (ErrnoException e) {
diff --git a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
index 51cecfe..25fa8bc 100644
--- a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
+++ b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
@@ -16,6 +16,7 @@
package com.android.networkstack.tethering.apishim.common;
+import android.net.IpPrefix;
import android.net.MacAddress;
import android.util.SparseArray;
@@ -79,25 +80,27 @@
* @param downstreamIfindex the downstream interface index
* @param upstreamIfindex the upstream interface index
+ * @param sourcePrefix the source IPv6 prefix
* @param inDstMac the destination MAC address to use for XDP
* @param outSrcMac the source MAC address to use for packets
* @param outDstMac the destination MAC address to use for packets
* @return true if operation succeeded or was a no-op, false otherwise
*/
public abstract boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
- @NonNull MacAddress inDstMac, @NonNull MacAddress outSrcMac,
- @NonNull MacAddress outDstMac, int mtu);
+ @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac,
+ @NonNull MacAddress outSrcMac, @NonNull MacAddress outDstMac, int mtu);
/**
* Stops IPv6 forwarding between the specified interfaces.
* @param downstreamIfindex the downstream interface index
* @param upstreamIfindex the upstream interface index
+ * @param sourcePrefix the valid source IPv6 prefix
* @param inDstMac the destination MAC address to use for XDP
* @return true if operation succeeded or was a no-op, false otherwise
*/
- public abstract boolean stopUpstreamIpv6Forwarding(int downstreamIfindex,
- int upstreamIfindex, @NonNull MacAddress inDstMac);
+ public abstract boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
+ @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac);
/**
* Return BPF tethering offload statistics.
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 6affb62..56b5c2e 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -28,11 +28,11 @@
import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.asByte;
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
+import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
import static com.android.networkstack.tethering.UpstreamNetworkState.isVcnInterface;
import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_IPSERVER;
diff --git a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 18c2171..50d6c4b 100644
--- a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -16,7 +16,6 @@
package android.net.ip;
-import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.IPPROTO_ICMPV6;
import static android.system.OsConstants.SOCK_RAW;
@@ -30,6 +29,7 @@
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
+import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
import static com.android.net.module.util.NetworkStackConstants.TAG_SYSTEM_NEIGHBOR;
import static com.android.networkstack.tethering.util.TetheringUtils.getAllNodesForScopeId;
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 976f5df..f22ccbd 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -37,6 +37,7 @@
import android.app.usage.NetworkStatsManager;
import android.net.INetd;
+import android.net.IpPrefix;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.NetworkStats;
@@ -119,6 +120,7 @@
private static final int DUMP_TIMEOUT_MS = 10_000;
private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString(
"00:00:00:00:00:00");
+ private static final IpPrefix IPV6_ZERO_PREFIX64 = new IpPrefix("::/64");
private static final String TETHER_DOWNSTREAM4_MAP_PATH = makeMapPath(DOWNSTREAM, 4);
private static final String TETHER_UPSTREAM4_MAP_PATH = makeMapPath(UPSTREAM, 4);
private static final String TETHER_DOWNSTREAM6_FS_PATH = makeMapPath(DOWNSTREAM, 6);
@@ -615,8 +617,9 @@
final int upstream = rule.upstreamIfindex;
// TODO: support upstream forwarding on non-point-to-point interfaces.
// TODO: get the MTU from LinkProperties and update the rules when it changes.
- if (!mBpfCoordinatorShim.startUpstreamIpv6Forwarding(downstream, upstream, rule.srcMac,
- NULL_MAC_ADDRESS, NULL_MAC_ADDRESS, NetworkStackConstants.ETHER_MTU)) {
+ if (!mBpfCoordinatorShim.startUpstreamIpv6Forwarding(downstream, upstream,
+ IPV6_ZERO_PREFIX64, rule.srcMac, NULL_MAC_ADDRESS, NULL_MAC_ADDRESS,
+ NetworkStackConstants.ETHER_MTU)) {
mLog.e("Failed to enable upstream IPv6 forwarding from "
+ getIfName(downstream) + " to " + getIfName(upstream));
}
@@ -657,7 +660,7 @@
final int downstream = rule.downstreamIfindex;
final int upstream = rule.upstreamIfindex;
if (!mBpfCoordinatorShim.stopUpstreamIpv6Forwarding(downstream, upstream,
- rule.srcMac)) {
+ IPV6_ZERO_PREFIX64, rule.srcMac)) {
mLog.e("Failed to disable upstream IPv6 forwarding from "
+ getIfName(downstream) + " to " + getIfName(upstream));
}
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index de15c5b..53c80ae 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -283,13 +283,16 @@
private int initWithHandles(NativeHandle h1, NativeHandle h2) {
if (h1 == null || h2 == null) {
+ // Set mIOffload to null has two purposes:
+ // 1. NativeHandles can be closed after initWithHandles() fails
+ // 2. Prevent mIOffload.stopOffload() to be called in stopOffload()
+ mIOffload = null;
mLog.e("Failed to create socket.");
return OFFLOAD_HAL_VERSION_NONE;
}
requestSocketDump(h1);
if (!mIOffload.initOffload(h1, h2, mOffloadHalCallback)) {
- mIOffload.stopOffload();
mLog.e("Failed to initialize offload.");
return OFFLOAD_HAL_VERSION_NONE;
}
@@ -329,9 +332,9 @@
mOffloadHalCallback = offloadCb;
final int version = initWithHandles(h1, h2);
- // Explicitly close FDs for HIDL. AIDL will pass the original FDs to the service,
- // they shouldn't be closed here.
- if (version < OFFLOAD_HAL_VERSION_AIDL) {
+ // Explicitly close FDs for HIDL or when mIOffload is null (cleared in initWithHandles).
+ // AIDL will pass the original FDs to the service, they shouldn't be closed here.
+ if (mIOffload == null || mIOffload.getVersion() < OFFLOAD_HAL_VERSION_AIDL) {
maybeCloseFdInNativeHandles(h1, h2);
}
return version;
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java b/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
index 5893885..36a1c3c 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
@@ -29,13 +29,17 @@
@Field(order = 0, type = Type.S32)
public final int iif; // The input interface index.
- @Field(order = 1, type = Type.EUI48, padding = 2)
+ @Field(order = 1, type = Type.EUI48, padding = 6)
public final MacAddress dstMac; // Destination ethernet mac address (zeroed iff rawip ingress).
- public TetherUpstream6Key(int iif, @NonNull final MacAddress dstMac) {
+ @Field(order = 2, type = Type.S64)
+ public final long src64; // The top 64-bits of the source ip.
+
+ public TetherUpstream6Key(int iif, @NonNull final MacAddress dstMac, long src64) {
Objects.requireNonNull(dstMac);
this.iif = iif;
this.dstMac = dstMac;
+ this.src64 = src64;
}
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index b0aa668..fe70820 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -112,8 +112,8 @@
* config_tether_upstream_automatic when set to true.
*
* This flag is enabled if !=0 and less than the module APEX version: see
- * {@link DeviceConfigUtils#isFeatureEnabled}. It is also ignored after R, as later devices
- * should just set config_tether_upstream_automatic to true instead.
+ * {@link DeviceConfigUtils#isTetheringFeatureEnabled}. It is also ignored after R, as later
+ * devices should just set config_tether_upstream_automatic to true instead.
*/
public static final String TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION =
"tether_force_upstream_automatic_version";
@@ -181,7 +181,7 @@
public static class Dependencies {
boolean isFeatureEnabled(@NonNull Context context, @NonNull String namespace,
@NonNull String name, @NonNull String moduleName, boolean defaultEnabled) {
- return DeviceConfigUtils.isFeatureEnabled(context, namespace, name,
+ return DeviceConfigUtils.isTetheringFeatureEnabled(context, namespace, name,
moduleName, defaultEnabled);
}
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 46e50ef..464778f 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -985,7 +985,7 @@
throws Exception {
if (!mBpfDeps.isAtLeastS()) return;
final TetherUpstream6Key key = new TetherUpstream6Key(TEST_IFACE_PARAMS.index,
- TEST_IFACE_PARAMS.macAddr);
+ TEST_IFACE_PARAMS.macAddr, 0);
final Tether6Value value = new Tether6Value(upstreamIfindex,
MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
@@ -996,7 +996,7 @@
throws Exception {
if (!mBpfDeps.isAtLeastS()) return;
final TetherUpstream6Key key = new TetherUpstream6Key(TEST_IFACE_PARAMS.index,
- TEST_IFACE_PARAMS.macAddr);
+ TEST_IFACE_PARAMS.macAddr, 0);
verifyWithOrder(inOrder, mBpfUpstream6Map).deleteEntry(key);
}
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 4f32f3c..8bc4c18 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -641,7 +641,7 @@
private void verifyStartUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int downstreamIfIndex,
MacAddress downstreamMac, int upstreamIfindex) throws Exception {
if (!mDeps.isAtLeastS()) return;
- final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex, downstreamMac);
+ final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex, downstreamMac, 0);
final Tether6Value value = new Tether6Value(upstreamIfindex,
MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
@@ -652,7 +652,7 @@
MacAddress downstreamMac)
throws Exception {
if (!mDeps.isAtLeastS()) return;
- final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex, downstreamMac);
+ final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex, downstreamMac, 0);
verifyWithOrder(inOrder, mBpfUpstream6Map).deleteEntry(key);
}
@@ -2192,7 +2192,7 @@
mBpfDownstream6Map.insertEntry(rule.makeTetherDownstream6Key(), rule.makeTether6Value());
final TetherUpstream6Key upstream6Key = new TetherUpstream6Key(DOWNSTREAM_IFINDEX,
- DOWNSTREAM_MAC);
+ DOWNSTREAM_MAC, 0);
final Tether6Value upstream6Value = new Tether6Value(UPSTREAM_IFINDEX,
MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
index b1f875b..4413d26 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -20,6 +20,8 @@
import static android.system.OsConstants.AF_UNIX;
import static android.system.OsConstants.SOCK_STREAM;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_DESTROY;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_NEW;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_AIDL;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1;
@@ -34,6 +36,7 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -57,7 +60,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.FileDescriptor;
@@ -75,8 +77,9 @@
private OffloadHardwareInterface mOffloadHw;
private OffloadHalCallback mOffloadHalCallback;
- @Mock private IOffloadHal mIOffload;
- @Mock private NativeHandle mNativeHandle;
+ private IOffloadHal mIOffload;
+ private NativeHandle mNativeHandle1;
+ private NativeHandle mNativeHandle2;
// Random values to test Netlink message.
private static final short TEST_TYPE = 184;
@@ -97,7 +100,9 @@
@Override
public NativeHandle createConntrackSocket(final int groups) {
- return mNativeHandle;
+ return groups == (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)
+ ? mNativeHandle1
+ : mNativeHandle2;
}
}
@@ -105,45 +110,89 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mOffloadHalCallback = new OffloadHalCallback();
- when(mIOffload.initOffload(any(NativeHandle.class), any(NativeHandle.class),
- any(OffloadHalCallback.class))).thenReturn(true);
+ mIOffload = mock(IOffloadHal.class);
}
- private void startOffloadHardwareInterface(int offloadHalVersion)
+ private void startOffloadHardwareInterface(int offloadHalVersion, boolean isHalInitSuccess)
throws Exception {
+ startOffloadHardwareInterface(offloadHalVersion, isHalInitSuccess, mock(NativeHandle.class),
+ mock(NativeHandle.class));
+ }
+
+ private void startOffloadHardwareInterface(int offloadHalVersion, boolean isHalInitSuccess,
+ NativeHandle handle1, NativeHandle handle2) throws Exception {
final SharedLog log = new SharedLog("test");
final Handler handler = new Handler(mTestLooper.getLooper());
- final int num = offloadHalVersion != OFFLOAD_HAL_VERSION_NONE ? 1 : 0;
+ final boolean hasNullHandle = handle1 == null || handle2 == null;
+ // If offloadHalVersion is OFFLOAD_HAL_VERSION_NONE or it has null NativeHandle arguments,
+ // mIOffload.initOffload() shouldn't be called.
+ final int initNum = (offloadHalVersion != OFFLOAD_HAL_VERSION_NONE && !hasNullHandle)
+ ? 1
+ : 0;
+ // If it is HIDL or has null NativeHandle argument, NativeHandles should be closed.
+ final int handleCloseNum = (hasNullHandle
+ || offloadHalVersion == OFFLOAD_HAL_VERSION_HIDL_1_0
+ || offloadHalVersion == OFFLOAD_HAL_VERSION_HIDL_1_1) ? 1 : 0;
+ mNativeHandle1 = handle1;
+ mNativeHandle2 = handle2;
+ when(mIOffload.initOffload(any(NativeHandle.class), any(NativeHandle.class),
+ any(OffloadHalCallback.class))).thenReturn(isHalInitSuccess);
mOffloadHw = new OffloadHardwareInterface(handler, log,
new MyDependencies(handler, log, offloadHalVersion));
- assertEquals(offloadHalVersion, mOffloadHw.initOffload(mOffloadHalCallback));
- verify(mIOffload, times(num)).initOffload(any(NativeHandle.class), any(NativeHandle.class),
- eq(mOffloadHalCallback));
+ assertEquals(isHalInitSuccess && !hasNullHandle
+ ? offloadHalVersion
+ : OFFLOAD_HAL_VERSION_NONE,
+ mOffloadHw.initOffload(mOffloadHalCallback));
+ verify(mIOffload, times(initNum)).initOffload(any(NativeHandle.class),
+ any(NativeHandle.class), eq(mOffloadHalCallback));
+ if (mNativeHandle1 != null) verify(mNativeHandle1, times(handleCloseNum)).close();
+ if (mNativeHandle2 != null) verify(mNativeHandle2, times(handleCloseNum)).close();
}
@Test
public void testInitFailureWithNoHal() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_NONE);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_NONE, true);
}
@Test
public void testInitSuccessWithAidl() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_AIDL);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_AIDL, true);
}
@Test
public void testInitSuccessWithHidl_1_0() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
}
@Test
public void testInitSuccessWithHidl_1_1() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1, true);
+ }
+
+ @Test
+ public void testInitFailWithAidl() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_AIDL, false);
+ }
+
+ @Test
+ public void testInitFailWithHidl_1_0() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, false);
+ }
+
+ @Test
+ public void testInitFailWithHidl_1_1() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1, false);
+ }
+
+ @Test
+ public void testInitFailDueToNullHandles() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_AIDL, true, mock(NativeHandle.class),
+ null);
}
@Test
public void testGetForwardedStats() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
ForwardedStats stats = new ForwardedStats(12345, 56780);
when(mIOffload.getForwardedStats(anyString())).thenReturn(stats);
assertEquals(mOffloadHw.getForwardedStats(RMNET0), stats);
@@ -152,7 +201,7 @@
@Test
public void testSetLocalPrefixes() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
final ArrayList<String> localPrefixes = new ArrayList<>();
localPrefixes.add("127.0.0.0/8");
localPrefixes.add("fe80::/64");
@@ -165,7 +214,7 @@
@Test
public void testSetDataLimit() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
final long limit = 12345;
when(mIOffload.setDataLimit(anyString(), anyLong())).thenReturn(true);
assertTrue(mOffloadHw.setDataLimit(RMNET0, limit));
@@ -177,7 +226,7 @@
@Test
public void testSetDataWarningAndLimitFailureWithHidl_1_0() throws Exception {
// Verify V1.0 control HAL would reject the function call with exception.
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
final long warning = 12345;
final long limit = 67890;
assertThrows(UnsupportedOperationException.class,
@@ -187,7 +236,7 @@
@Test
public void testSetDataWarningAndLimit() throws Exception {
// Verify V1.1 control HAL could receive this function call.
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1, true);
final long warning = 12345;
final long limit = 67890;
when(mIOffload.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true);
@@ -199,7 +248,7 @@
@Test
public void testSetUpstreamParameters() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
final String v4addr = "192.168.10.1";
final String v4gateway = "192.168.10.255";
final ArrayList<String> v6gws = new ArrayList<>(0);
@@ -220,7 +269,7 @@
@Test
public void testUpdateDownstream() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
final String ifName = "wlan1";
final String prefix = "192.168.43.0/24";
when(mIOffload.addDownstream(anyString(), anyString())).thenReturn(true);
@@ -237,7 +286,7 @@
@Test
public void testSendIpv4NfGenMsg() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
FileDescriptor writeSocket = new FileDescriptor();
FileDescriptor readSocket = new FileDescriptor();
try {
@@ -246,9 +295,9 @@
fail();
return;
}
- when(mNativeHandle.getFileDescriptor()).thenReturn(writeSocket);
+ when(mNativeHandle1.getFileDescriptor()).thenReturn(writeSocket);
- mOffloadHw.sendIpv4NfGenMsg(mNativeHandle, TEST_TYPE, TEST_FLAGS);
+ mOffloadHw.sendIpv4NfGenMsg(mNativeHandle1, TEST_TYPE, TEST_FLAGS);
ByteBuffer buffer = ByteBuffer.allocate(9823); // Arbitrary value > expectedLen.
buffer.order(ByteOrder.nativeOrder());
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 9c6904d..770507e 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -68,6 +68,7 @@
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
+import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
import static com.android.networkstack.tethering.TestConnectivityManager.BROADCAST_FIRST;
@@ -157,7 +158,6 @@
import android.net.ip.DadProxy;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
-import android.net.util.NetworkConstants;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
@@ -559,7 +559,7 @@
prop.addDnsServer(InetAddresses.parseNumericAddress("2001:db8::2"));
prop.addLinkAddress(
new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
- NetworkConstants.RFC7421_PREFIX_LENGTH));
+ RFC7421_PREFIX_LENGTH));
prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
InetAddresses.parseNumericAddress("2001:db8::1"),
interfaceName, RTN_UNICAST));
diff --git a/bpf_progs/netd.h b/bpf_progs/netd.h
index be604f9..dcf6d6a 100644
--- a/bpf_progs/netd.h
+++ b/bpf_progs/netd.h
@@ -55,21 +55,22 @@
} StatsValue;
STRUCT_SIZE(StatsValue, 4 * 8); // 32
+#ifdef __cplusplus
+static inline StatsValue& operator+=(StatsValue& lhs, const StatsValue& rhs) {
+ lhs.rxPackets += rhs.rxPackets;
+ lhs.rxBytes += rhs.rxBytes;
+ lhs.txPackets += rhs.txPackets;
+ lhs.txBytes += rhs.txBytes;
+ return lhs;
+}
+#endif
+
typedef struct {
char name[IFNAMSIZ];
} IfaceValue;
STRUCT_SIZE(IfaceValue, 16);
typedef struct {
- uint64_t rxBytes;
- uint64_t rxPackets;
- uint64_t txBytes;
- uint64_t txPackets;
- uint64_t tcpRxPackets;
- uint64_t tcpTxPackets;
-} Stats;
-
-typedef struct {
uint64_t timestampNs;
uint32_t ifindex;
uint32_t length;
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 8645dd7..c752779 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -194,6 +194,7 @@
TetherUpstream6Key ku = {
.iif = skb->ifindex,
+ .src64 = 0,
};
if (is_ethernet) __builtin_memcpy(downstream ? kd.dstMac : ku.dstMac, eth->h_dest, ETH_ALEN);
diff --git a/bpf_progs/offload.h b/bpf_progs/offload.h
index 9dae6c9..1e28f01 100644
--- a/bpf_progs/offload.h
+++ b/bpf_progs/offload.h
@@ -135,10 +135,10 @@
typedef struct {
uint32_t iif; // The input interface index
uint8_t dstMac[ETH_ALEN]; // destination ethernet mac address (zeroed iff rawip ingress)
- uint8_t zero[2]; // zero pad for 8 byte alignment
- // TODO: extend this to include src ip /64 subnet
+ uint8_t zero[6]; // zero pad for 8 byte alignment
+ uint64_t src64; // Top 64-bits of the src ip
} TetherUpstream6Key;
-STRUCT_SIZE(TetherUpstream6Key, 12);
+STRUCT_SIZE(TetherUpstream6Key, 4 + 6 + 6 + 8); // 24
typedef struct {
uint32_t iif; // The input interface index
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
index ffa2857..9520ef6 100644
--- a/framework-t/Android.bp
+++ b/framework-t/Android.bp
@@ -82,6 +82,17 @@
visibility: ["//packages/modules/Connectivity:__subpackages__"],
}
+// The filegroup lists files that are necessary for verifying building mdns as a standalone,
+// for use with service-connectivity-mdns-standalone-build-test
+filegroup {
+ name: "framework-connectivity-t-mdns-standalone-build-sources",
+ srcs: [
+ "src/android/net/nsd/OffloadEngine.java",
+ "src/android/net/nsd/OffloadServiceInfo.java",
+ ],
+ visibility: ["//packages/modules/Connectivity:__subpackages__"],
+}
+
java_library {
name: "framework-connectivity-t-pre-jarjar",
defaults: ["framework-connectivity-t-defaults"],
diff --git a/framework-t/api/OWNERS b/framework-t/api/OWNERS
index de0f905..af583c3 100644
--- a/framework-t/api/OWNERS
+++ b/framework-t/api/OWNERS
@@ -1 +1,2 @@
file:platform/packages/modules/Connectivity:master:/nearby/OWNERS
+file:platform/packages/modules/Connectivity:master:/remoteauth/OWNERS
diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
index 87b0a64..64762b4 100644
--- a/framework-t/api/system-current.txt
+++ b/framework-t/api/system-current.txt
@@ -348,3 +348,43 @@
}
+package android.net.nsd {
+
+ public final class NsdManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void registerOffloadEngine(@NonNull String, long, long, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.OffloadEngine);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void unregisterOffloadEngine(@NonNull android.net.nsd.OffloadEngine);
+ }
+
+ public interface OffloadEngine {
+ method public void onOffloadServiceRemoved(@NonNull android.net.nsd.OffloadServiceInfo);
+ method public void onOffloadServiceUpdated(@NonNull android.net.nsd.OffloadServiceInfo);
+ field public static final int OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK = 1; // 0x1
+ field public static final int OFFLOAD_TYPE_FILTER_QUERIES = 2; // 0x2
+ field public static final int OFFLOAD_TYPE_FILTER_REPLIES = 4; // 0x4
+ field public static final int OFFLOAD_TYPE_REPLY = 1; // 0x1
+ }
+
+ public final class OffloadServiceInfo implements android.os.Parcelable {
+ ctor public OffloadServiceInfo(@NonNull android.net.nsd.OffloadServiceInfo.Key, @NonNull java.util.List<java.lang.String>, @NonNull String, @Nullable byte[], @IntRange(from=0, to=java.lang.Integer.MAX_VALUE) int, long);
+ method public int describeContents();
+ method @NonNull public String getHostname();
+ method @NonNull public android.net.nsd.OffloadServiceInfo.Key getKey();
+ method @Nullable public byte[] getOffloadPayload();
+ method public long getOffloadType();
+ method public int getPriority();
+ method @NonNull public java.util.List<java.lang.String> getSubtypes();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.nsd.OffloadServiceInfo> CREATOR;
+ }
+
+ public static final class OffloadServiceInfo.Key implements android.os.Parcelable {
+ ctor public OffloadServiceInfo.Key(@NonNull String, @NonNull String);
+ method public int describeContents();
+ method @NonNull public String getServiceName();
+ method @NonNull public String getServiceType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.nsd.OffloadServiceInfo.Key> CREATOR;
+ }
+
+}
+
diff --git a/framework-t/src/android/net/TrafficStats.java b/framework-t/src/android/net/TrafficStats.java
index dc4ac55..a69b38d 100644
--- a/framework-t/src/android/net/TrafficStats.java
+++ b/framework-t/src/android/net/TrafficStats.java
@@ -683,33 +683,13 @@
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static long getMobileTcpRxPackets() {
- long total = 0;
- for (String iface : getMobileIfaces()) {
- long stat = UNSUPPORTED;
- try {
- stat = getStatsService().getIfaceStats(iface, TYPE_TCP_RX_PACKETS);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- total += addIfSupported(stat);
- }
- return total;
+ return UNSUPPORTED;
}
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static long getMobileTcpTxPackets() {
- long total = 0;
- for (String iface : getMobileIfaces()) {
- long stat = UNSUPPORTED;
- try {
- stat = getStatsService().getIfaceStats(iface, TYPE_TCP_TX_PACKETS);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- total += addIfSupported(stat);
- }
- return total;
+ return UNSUPPORTED;
}
/**
@@ -1141,8 +1121,4 @@
public static final int TYPE_TX_BYTES = 2;
/** {@hide} */
public static final int TYPE_TX_PACKETS = 3;
- /** {@hide} */
- public static final int TYPE_TCP_RX_PACKETS = 4;
- /** {@hide} */
- public static final int TYPE_TCP_TX_PACKETS = 5;
}
diff --git a/framework-t/src/android/net/nsd/INsdServiceConnector.aidl b/framework-t/src/android/net/nsd/INsdServiceConnector.aidl
index 5533154..e671db1 100644
--- a/framework-t/src/android/net/nsd/INsdServiceConnector.aidl
+++ b/framework-t/src/android/net/nsd/INsdServiceConnector.aidl
@@ -17,6 +17,7 @@
package android.net.nsd;
import android.net.nsd.INsdManagerCallback;
+import android.net.nsd.IOffloadEngine;
import android.net.nsd.NsdServiceInfo;
import android.os.Messenger;
@@ -35,4 +36,6 @@
void stopResolution(int listenerKey);
void registerServiceInfoCallback(int listenerKey, in NsdServiceInfo serviceInfo);
void unregisterServiceInfoCallback(int listenerKey);
+ void registerOffloadEngine(String ifaceName, in IOffloadEngine cb, long offloadCapabilities, long offloadType);
+ void unregisterOffloadEngine(in IOffloadEngine cb);
}
\ No newline at end of file
diff --git a/framework-t/src/android/net/nsd/IOffloadEngine.aidl b/framework-t/src/android/net/nsd/IOffloadEngine.aidl
new file mode 100644
index 0000000..2af733d
--- /dev/null
+++ b/framework-t/src/android/net/nsd/IOffloadEngine.aidl
@@ -0,0 +1,29 @@
+/**
+ * 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.nsd;
+
+import android.net.nsd.OffloadServiceInfo;
+
+/**
+ * Callbacks from NsdService to inform providers of packet offload.
+ *
+ * @hide
+ */
+oneway interface IOffloadEngine {
+ void onOffloadServiceUpdated(in OffloadServiceInfo info);
+ void onOffloadServiceRemoved(in OffloadServiceInfo info);
+}
diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index 2930cbd..ef0e34b 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -16,6 +16,9 @@
package android.net.nsd;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.NETWORK_STACK;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND;
import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER;
@@ -25,6 +28,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.compat.CompatChanges;
import android.content.Context;
@@ -45,9 +49,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.CollectionUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -246,6 +252,10 @@
public static final int UNREGISTER_SERVICE_CALLBACK = 31;
/** @hide */
public static final int UNREGISTER_SERVICE_CALLBACK_SUCCEEDED = 32;
+ /** @hide */
+ public static final int REGISTER_OFFLOAD_ENGINE = 33;
+ /** @hide */
+ public static final int UNREGISTER_OFFLOAD_ENGINE = 34;
/** Dns based service discovery protocol */
public static final int PROTOCOL_DNS_SD = 0x0001;
@@ -313,8 +323,107 @@
private final ArrayMap<Integer, PerNetworkDiscoveryTracker>
mPerNetworkDiscoveryMap = new ArrayMap<>();
+ @GuardedBy("mOffloadEngines")
+ private final ArrayList<OffloadEngineProxy> mOffloadEngines = new ArrayList<>();
private final ServiceHandler mHandler;
+ private static class OffloadEngineProxy extends IOffloadEngine.Stub {
+ private final Executor mExecutor;
+ private final OffloadEngine mEngine;
+
+ private OffloadEngineProxy(@NonNull Executor executor, @NonNull OffloadEngine appCb) {
+ mExecutor = executor;
+ mEngine = appCb;
+ }
+
+ @Override
+ public void onOffloadServiceUpdated(OffloadServiceInfo info) {
+ mExecutor.execute(() -> mEngine.onOffloadServiceUpdated(info));
+ }
+
+ @Override
+ public void onOffloadServiceRemoved(OffloadServiceInfo info) {
+ mExecutor.execute(() -> mEngine.onOffloadServiceRemoved(info));
+ }
+ }
+
+ /**
+ * Registers an OffloadEngine with NsdManager.
+ *
+ * A caller can register itself as an OffloadEngine if it supports mDns hardware offload.
+ * The caller must implement the {@link OffloadEngine} interface and update hardware offload
+ * state property when the {@link OffloadEngine#onOffloadServiceUpdated} and
+ * {@link OffloadEngine#onOffloadServiceRemoved} callback are called. Multiple engines may be
+ * registered for the same interface, and that the same engine cannot be registered twice.
+ *
+ * @param ifaceName indicates which network interface the hardware offload runs on
+ * @param offloadType the type of offload that the offload engine support
+ * @param offloadCapability the capabilities of the offload engine
+ * @param executor the executor on which to receive the offload callbacks
+ * @param engine the OffloadEngine that will receive the offload callbacks
+ * @throws IllegalStateException if the engine is already registered.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {NETWORK_SETTINGS, PERMISSION_MAINLINE_NETWORK_STACK,
+ NETWORK_STACK})
+ public void registerOffloadEngine(@NonNull String ifaceName,
+ @OffloadEngine.OffloadType long offloadType,
+ @OffloadEngine.OffloadCapability long offloadCapability, @NonNull Executor executor,
+ @NonNull OffloadEngine engine) {
+ Objects.requireNonNull(ifaceName);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(engine);
+ final OffloadEngineProxy cbImpl = new OffloadEngineProxy(executor, engine);
+ synchronized (mOffloadEngines) {
+ if (CollectionUtils.contains(mOffloadEngines, impl -> impl.mEngine == engine)) {
+ throw new IllegalStateException("This engine is already registered");
+ }
+ mOffloadEngines.add(cbImpl);
+ }
+ try {
+ mService.registerOffloadEngine(ifaceName, cbImpl, offloadCapability, offloadType);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Unregisters an OffloadEngine from NsdService.
+ *
+ * A caller can unregister itself as an OffloadEngine when it doesn't want to receive the
+ * callback anymore. The OffloadEngine must have been previously registered with the system
+ * using the {@link NsdManager#registerOffloadEngine} method.
+ *
+ * @param engine OffloadEngine object to be removed from NsdService
+ * @throws IllegalStateException if the engine is not registered.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {NETWORK_SETTINGS, PERMISSION_MAINLINE_NETWORK_STACK,
+ NETWORK_STACK})
+ public void unregisterOffloadEngine(@NonNull OffloadEngine engine) {
+ Objects.requireNonNull(engine);
+ final OffloadEngineProxy cbImpl;
+ synchronized (mOffloadEngines) {
+ final int index = CollectionUtils.indexOf(mOffloadEngines,
+ impl -> impl.mEngine == engine);
+ if (index < 0) {
+ throw new IllegalStateException("This engine is not registered");
+ }
+ cbImpl = mOffloadEngines.remove(index);
+ }
+
+ try {
+ mService.unregisterOffloadEngine(cbImpl);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
private class PerNetworkDiscoveryTracker {
final String mServiceType;
final int mProtocolType;
diff --git a/framework-t/src/android/net/nsd/OffloadEngine.java b/framework-t/src/android/net/nsd/OffloadEngine.java
new file mode 100644
index 0000000..b566b13
--- /dev/null
+++ b/framework-t/src/android/net/nsd/OffloadEngine.java
@@ -0,0 +1,92 @@
+/*
+ * 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.nsd;
+
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * OffloadEngine is an interface for mDns hardware offloading.
+ *
+ * An offloading engine can interact with the firmware code to instruct the hardware to
+ * offload some of mDns network traffic before it reached android OS. This can improve the
+ * power consumption performance of the host system by not always waking up the OS to handle
+ * the mDns packet when the device is in low power mode.
+ *
+ * @hide
+ */
+@SystemApi
+public interface OffloadEngine {
+ /**
+ * Indicates that the OffloadEngine can generate replies to mDns queries.
+ *
+ * @see OffloadServiceInfo#getOffloadPayload()
+ */
+ int OFFLOAD_TYPE_REPLY = 1;
+ /**
+ * Indicates that the OffloadEngine can filter and drop mDns queries.
+ */
+ int OFFLOAD_TYPE_FILTER_QUERIES = 1 << 1;
+ /**
+ * Indicates that the OffloadEngine can filter and drop mDns replies. It can allow mDns packets
+ * to be received even when no app holds a {@link android.net.wifi.WifiManager.MulticastLock}.
+ */
+ int OFFLOAD_TYPE_FILTER_REPLIES = 1 << 2;
+
+ /**
+ * Indicates that the OffloadEngine can bypass multicast lock.
+ */
+ int OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK = 1;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @LongDef(flag = true, prefix = {"OFFLOAD_TYPE"}, value = {
+ OFFLOAD_TYPE_REPLY,
+ OFFLOAD_TYPE_FILTER_QUERIES,
+ OFFLOAD_TYPE_FILTER_REPLIES,
+ })
+ @interface OffloadType {}
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @LongDef(flag = true, prefix = {"OFFLOAD_CAPABILITY"}, value = {
+ OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK
+ })
+ @interface OffloadCapability {}
+
+ /**
+ * To be called when the OffloadServiceInfo is added or updated.
+ *
+ * @param info The OffloadServiceInfo to add or update.
+ */
+ void onOffloadServiceUpdated(@NonNull OffloadServiceInfo info);
+
+ /**
+ * To be called when the OffloadServiceInfo is removed.
+ *
+ * @param info The OffloadServiceInfo to remove.
+ */
+ void onOffloadServiceRemoved(@NonNull OffloadServiceInfo info);
+}
diff --git a/framework-t/src/android/net/nsd/OffloadServiceInfo.java b/framework-t/src/android/net/nsd/OffloadServiceInfo.java
new file mode 100644
index 0000000..7bd5a7d
--- /dev/null
+++ b/framework-t/src/android/net/nsd/OffloadServiceInfo.java
@@ -0,0 +1,314 @@
+/*
+ * 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.nsd;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.net.module.util.HexDump;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The OffloadServiceInfo class contains all the necessary information the OffloadEngine needs to
+ * know about how to offload an mDns service. The OffloadServiceInfo is keyed on
+ * {@link OffloadServiceInfo.Key} which is a (serviceName, serviceType) pair.
+ *
+ * @hide
+ */
+@SystemApi
+public final class OffloadServiceInfo implements Parcelable {
+ @NonNull
+ private final Key mKey;
+ @NonNull
+ private final String mHostname;
+ @NonNull final List<String> mSubtypes;
+ @Nullable
+ private final byte[] mOffloadPayload;
+ private final int mPriority;
+ private final long mOffloadType;
+
+ /**
+ * Creates a new OffloadServiceInfo object with the specified parameters.
+ *
+ * @param key The key of the service.
+ * @param subtypes The list of subTypes of the service.
+ * @param hostname The name of the host offering the service. It is meaningful only when
+ * offloadType contains OFFLOAD_REPLY.
+ * @param offloadPayload The raw udp payload for hardware offloading.
+ * @param priority The priority of the service, @see #getPriority.
+ * @param offloadType The type of the service offload, @see #getOffloadType.
+ */
+ public OffloadServiceInfo(@NonNull Key key,
+ @NonNull List<String> subtypes, @NonNull String hostname,
+ @Nullable byte[] offloadPayload,
+ @IntRange(from = 0, to = Integer.MAX_VALUE) int priority,
+ @OffloadEngine.OffloadType long offloadType) {
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(subtypes);
+ Objects.requireNonNull(hostname);
+ mKey = key;
+ mSubtypes = subtypes;
+ mHostname = hostname;
+ mOffloadPayload = offloadPayload;
+ mPriority = priority;
+ mOffloadType = offloadType;
+ }
+
+ /**
+ * Creates a new OffloadServiceInfo object from a Parcel.
+ *
+ * @param in The Parcel to read the object from.
+ *
+ * @hide
+ */
+ public OffloadServiceInfo(@NonNull Parcel in) {
+ mKey = in.readParcelable(Key.class.getClassLoader(),
+ Key.class);
+ mSubtypes = in.createStringArrayList();
+ mHostname = in.readString();
+ mOffloadPayload = in.createByteArray();
+ mPriority = in.readInt();
+ mOffloadType = in.readLong();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mKey, flags);
+ dest.writeStringList(mSubtypes);
+ dest.writeString(mHostname);
+ dest.writeByteArray(mOffloadPayload);
+ dest.writeInt(mPriority);
+ dest.writeLong(mOffloadType);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<OffloadServiceInfo> CREATOR = new Creator<OffloadServiceInfo>() {
+ @Override
+ public OffloadServiceInfo createFromParcel(Parcel in) {
+ return new OffloadServiceInfo(in);
+ }
+
+ @Override
+ public OffloadServiceInfo[] newArray(int size) {
+ return new OffloadServiceInfo[size];
+ }
+ };
+
+ /**
+ * Get the {@link Key}.
+ */
+ @NonNull
+ public Key getKey() {
+ return mKey;
+ }
+
+ /**
+ * Get the host name. (e.g. "Android.local" )
+ */
+ @NonNull
+ public String getHostname() {
+ return mHostname;
+ }
+
+ /**
+ * Get the service subtypes. (e.g. ["_ann"] )
+ */
+ @NonNull
+ public List<String> getSubtypes() {
+ return Collections.unmodifiableList(mSubtypes);
+ }
+
+ /**
+ * Get the raw udp payload that the OffloadEngine can use to directly reply the incoming query.
+ * <p>
+ * It is null if the OffloadEngine can not handle transmit. The packet must be sent as-is when
+ * replying to query.
+ */
+ @Nullable
+ public byte[] getOffloadPayload() {
+ if (mOffloadPayload == null) {
+ return null;
+ } else {
+ return mOffloadPayload.clone();
+ }
+ }
+
+ /**
+ * Get the offloadType.
+ * <p>
+ * For example, if the {@link com.android.server.NsdService} requests the OffloadEngine to both
+ * filter the mDNS queries and replies, the {@link #mOffloadType} =
+ * ({@link OffloadEngine#OFFLOAD_TYPE_FILTER_QUERIES} |
+ * {@link OffloadEngine#OFFLOAD_TYPE_FILTER_REPLIES}).
+ */
+ @OffloadEngine.OffloadType public long getOffloadType() {
+ return mOffloadType;
+ }
+
+ /**
+ * Get the priority for the OffloadServiceInfo.
+ * <p>
+ * When OffloadEngine don't have enough resource
+ * (e.g. not enough memory) to offload all the OffloadServiceInfo. The OffloadServiceInfo
+ * having lower priority values should be handled by the OffloadEngine first.
+ */
+ public int getPriority() {
+ return mPriority;
+ }
+
+ /**
+ * Only for debug purpose, the string can be long as the raw packet is dump in the string.
+ */
+ @Override
+ public String toString() {
+ return String.format(
+ "OffloadServiceInfo{ mOffloadServiceInfoKey=%s, mHostName=%s, "
+ + "mOffloadPayload=%s, mPriority=%d, mOffloadType=%d, mSubTypes=%s }",
+ mKey,
+ mHostname, HexDump.dumpHexString(mOffloadPayload), mPriority,
+ mOffloadType, mSubtypes.toString());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof OffloadServiceInfo)) return false;
+ OffloadServiceInfo that = (OffloadServiceInfo) o;
+ return mPriority == that.mPriority && mOffloadType == that.mOffloadType
+ && mKey.equals(that.mKey)
+ && mHostname.equals(
+ that.mHostname) && Arrays.equals(mOffloadPayload,
+ that.mOffloadPayload)
+ && mSubtypes.equals(that.mSubtypes);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(mKey, mHostname, mPriority,
+ mOffloadType, mSubtypes);
+ result = 31 * result + Arrays.hashCode(mOffloadPayload);
+ return result;
+ }
+
+ /**
+ * The {@link OffloadServiceInfo.Key} is the (serviceName, serviceType) pair.
+ */
+ public static final class Key implements Parcelable {
+ @NonNull
+ private final String mServiceName;
+ @NonNull
+ private final String mServiceType;
+
+ /**
+ * Creates a new OffloadServiceInfoKey object with the specified parameters.
+ *
+ * @param serviceName The name of the service.
+ * @param serviceType The type of the service.
+ */
+ public Key(@NonNull String serviceName, @NonNull String serviceType) {
+ Objects.requireNonNull(serviceName);
+ Objects.requireNonNull(serviceType);
+ mServiceName = serviceName;
+ mServiceType = serviceType;
+ }
+
+ /**
+ * Creates a new OffloadServiceInfoKey object from a Parcel.
+ *
+ * @param in The Parcel to read the object from.
+ *
+ * @hide
+ */
+ public Key(@NonNull Parcel in) {
+ mServiceName = in.readString();
+ mServiceType = in.readString();
+ }
+ /**
+ * Get the service name. (e.g. "NsdChat")
+ */
+ @NonNull
+ public String getServiceName() {
+ return mServiceName;
+ }
+
+ /**
+ * Get the service type. (e.g. "_http._tcp.local" )
+ */
+ @NonNull
+ public String getServiceType() {
+ return mServiceType;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mServiceName);
+ dest.writeString(mServiceType);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<Key> CREATOR =
+ new Creator<Key>() {
+ @Override
+ public Key createFromParcel(Parcel in) {
+ return new Key(in);
+ }
+
+ @Override
+ public Key[] newArray(int size) {
+ return new Key[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Key)) return false;
+ Key that = (Key) o;
+ return Objects.equals(mServiceName, that.mServiceName) && Objects.equals(
+ mServiceType, that.mServiceType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mServiceName, mServiceType);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("OffloadServiceInfoKey{ mServiceName=%s, mServiceType=%s }",
+ mServiceName, mServiceType);
+ }
+ }
+}
diff --git a/framework/aidl-export/android/net/nsd/OffloadServiceInfo.aidl b/framework/aidl-export/android/net/nsd/OffloadServiceInfo.aidl
new file mode 100644
index 0000000..aa7aef2
--- /dev/null
+++ b/framework/aidl-export/android/net/nsd/OffloadServiceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.nsd;
+
+@JavaOnlyStableParcelable parcelable OffloadServiceInfo;
\ No newline at end of file
diff --git a/framework/jarjar-excludes.txt b/framework/jarjar-excludes.txt
index 9b48d57..1ac5e8e 100644
--- a/framework/jarjar-excludes.txt
+++ b/framework/jarjar-excludes.txt
@@ -27,4 +27,10 @@
# Don't touch anything that's already under android.net.http (cronet)
# This is required since android.net.http contains api classes and hidden classes.
# TODO: Remove this after hidden classes are moved to different package
-android\.net\.http\..+
\ No newline at end of file
+android\.net\.http\..+
+
+# TODO: OffloadServiceInfo is being added as an API, but wasn't an API yet in the first module
+# versions targeting U. Do not jarjar it such versions so that tests do not have to cover both
+# cases. This will be removed in an upcoming change marking it as API.
+android\.net\.nsd\.OffloadServiceInfo(\$.+)?
+android\.net\.nsd\.OffloadEngine(\$.+)?
diff --git a/service-t/Android.bp b/service-t/Android.bp
index 7de749c..c277cf6 100644
--- a/service-t/Android.bp
+++ b/service-t/Android.bp
@@ -88,8 +88,9 @@
name: "service-connectivity-mdns-standalone-build-test",
sdk_version: "core_platform",
srcs: [
- ":service-mdns-droidstubs",
"src/com/android/server/connectivity/mdns/**/*.java",
+ ":framework-connectivity-t-mdns-standalone-build-sources",
+ ":service-mdns-droidstubs"
],
exclude_srcs: [
"src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java",
diff --git a/service-t/jni/com_android_server_net_NetworkStatsService.cpp b/service-t/jni/com_android_server_net_NetworkStatsService.cpp
index dab9d07..bdbb655 100644
--- a/service-t/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/service-t/jni/com_android_server_net_NetworkStatsService.cpp
@@ -34,6 +34,7 @@
using android::bpf::bpfGetUidStats;
using android::bpf::bpfGetIfaceStats;
+using android::bpf::bpfGetIfIndexStats;
using android::bpf::NetworkTraceHandler;
namespace android {
@@ -46,11 +47,9 @@
RX_PACKETS = 1,
TX_BYTES = 2,
TX_PACKETS = 3,
- TCP_RX_PACKETS = 4,
- TCP_TX_PACKETS = 5
};
-static uint64_t getStatsType(Stats* stats, StatsType type) {
+static uint64_t getStatsType(StatsValue* stats, StatsType type) {
switch (type) {
case RX_BYTES:
return stats->rxBytes;
@@ -60,17 +59,13 @@
return stats->txBytes;
case TX_PACKETS:
return stats->txPackets;
- case TCP_RX_PACKETS:
- return stats->tcpRxPackets;
- case TCP_TX_PACKETS:
- return stats->tcpTxPackets;
default:
return UNKNOWN;
}
}
static jlong nativeGetTotalStat(JNIEnv* env, jclass clazz, jint type) {
- Stats stats = {};
+ StatsValue stats = {};
if (bpfGetIfaceStats(NULL, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
@@ -85,7 +80,7 @@
return UNKNOWN;
}
- Stats stats = {};
+ StatsValue stats = {};
if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
@@ -94,8 +89,17 @@
}
}
+static jlong nativeGetIfIndexStat(JNIEnv* env, jclass clazz, jint ifindex, jint type) {
+ StatsValue stats = {};
+ if (bpfGetIfIndexStats(ifindex, &stats) == 0) {
+ return getStatsType(&stats, (StatsType) type);
+ } else {
+ return UNKNOWN;
+ }
+}
+
static jlong nativeGetUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
- Stats stats = {};
+ StatsValue stats = {};
if (bpfGetUidStats(uid, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
@@ -111,6 +115,7 @@
static const JNINativeMethod gMethods[] = {
{"nativeGetTotalStat", "(I)J", (void*)nativeGetTotalStat},
{"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)nativeGetIfaceStat},
+ {"nativeGetIfIndexStat", "(II)J", (void*)nativeGetIfIndexStat},
{"nativeGetUidStat", "(II)J", (void*)nativeGetUidStat},
{"nativeInitNetworkTracing", "()V", (void*)nativeInitNetworkTracing},
};
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
index 1bc8ca5..fed2979 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
@@ -40,30 +40,27 @@
using base::Result;
-int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
+int bpfGetUidStatsInternal(uid_t uid, StatsValue* stats,
const BpfMap<uint32_t, StatsValue>& appUidStatsMap) {
auto statsEntry = appUidStatsMap.readValue(uid);
- if (statsEntry.ok()) {
- stats->rxPackets = statsEntry.value().rxPackets;
- stats->txPackets = statsEntry.value().txPackets;
- stats->rxBytes = statsEntry.value().rxBytes;
- stats->txBytes = statsEntry.value().txBytes;
+ if (!statsEntry.ok()) {
+ *stats = {};
+ return (statsEntry.error().code() == ENOENT) ? 0 : -statsEntry.error().code();
}
- return (statsEntry.ok() || statsEntry.error().code() == ENOENT) ? 0
- : -statsEntry.error().code();
+ *stats = statsEntry.value();
+ return 0;
}
-int bpfGetUidStats(uid_t uid, Stats* stats) {
+int bpfGetUidStats(uid_t uid, StatsValue* stats) {
static BpfMapRO<uint32_t, StatsValue> appUidStatsMap(APP_UID_STATS_MAP_PATH);
return bpfGetUidStatsInternal(uid, stats, appUidStatsMap);
}
-int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
+int bpfGetIfaceStatsInternal(const char* iface, StatsValue* stats,
const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceNameMap) {
+ *stats = {};
int64_t unknownIfaceBytesTotal = 0;
- stats->tcpRxPackets = -1;
- stats->tcpTxPackets = -1;
const auto processIfaceStats =
[iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal](
const uint32_t& key,
@@ -78,10 +75,7 @@
if (!statsEntry.ok()) {
return statsEntry.error();
}
- stats->rxPackets += statsEntry.value().rxPackets;
- stats->txPackets += statsEntry.value().txPackets;
- stats->rxBytes += statsEntry.value().rxBytes;
- stats->txBytes += statsEntry.value().txBytes;
+ *stats += statsEntry.value();
}
return Result<void>();
};
@@ -89,12 +83,28 @@
return res.ok() ? 0 : -res.error().code();
}
-int bpfGetIfaceStats(const char* iface, Stats* stats) {
+int bpfGetIfaceStats(const char* iface, StatsValue* stats) {
static BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
static BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
return bpfGetIfaceStatsInternal(iface, stats, ifaceStatsMap, ifaceIndexNameMap);
}
+int bpfGetIfIndexStatsInternal(uint32_t ifindex, StatsValue* stats,
+ const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) {
+ auto statsEntry = ifaceStatsMap.readValue(ifindex);
+ if (!statsEntry.ok()) {
+ *stats = {};
+ return (statsEntry.error().code() == ENOENT) ? 0 : -statsEntry.error().code();
+ }
+ *stats = statsEntry.value();
+ return 0;
+}
+
+int bpfGetIfIndexStats(int ifindex, StatsValue* stats) {
+ static BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
+ return bpfGetIfIndexStatsInternal(ifindex, stats, ifaceStatsMap);
+}
+
stats_line populateStatsEntry(const StatsKey& statsKey, const StatsValue& statsEntry,
const char* ifname) {
stats_line newLine;
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
index ccd3f5e..76c56eb 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
@@ -116,7 +116,7 @@
EXPECT_RESULT_OK(mFakeIfaceIndexNameMap.writeValue(ifaceIndex, iface, BPF_ANY));
}
- void expectStatsEqual(const StatsValue& target, const Stats& result) {
+ void expectStatsEqual(const StatsValue& target, const StatsValue& result) {
EXPECT_EQ(target.rxPackets, result.rxPackets);
EXPECT_EQ(target.rxBytes, result.rxBytes);
EXPECT_EQ(target.txPackets, result.txPackets);
@@ -194,7 +194,7 @@
.txPackets = 0,
.txBytes = 0,
};
- Stats result1 = {};
+ StatsValue result1 = {};
ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID1, &result1, mFakeAppUidStatsMap));
expectStatsEqual(value1, result1);
}
@@ -217,11 +217,11 @@
};
ASSERT_RESULT_OK(mFakeAppUidStatsMap.writeValue(TEST_UID1, value1, BPF_ANY));
ASSERT_RESULT_OK(mFakeAppUidStatsMap.writeValue(TEST_UID2, value2, BPF_ANY));
- Stats result1 = {};
+ StatsValue result1 = {};
ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID1, &result1, mFakeAppUidStatsMap));
expectStatsEqual(value1, result1);
- Stats result2 = {};
+ StatsValue result2 = {};
ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID2, &result2, mFakeAppUidStatsMap));
expectStatsEqual(value2, result2);
std::vector<stats_line> lines;
@@ -255,15 +255,15 @@
ifaceStatsKey = IFACE_INDEX3;
EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY));
- Stats result1 = {};
+ StatsValue result1 = {};
ASSERT_EQ(0, bpfGetIfaceStatsInternal(IFACE_NAME1, &result1, mFakeIfaceStatsMap,
mFakeIfaceIndexNameMap));
expectStatsEqual(value1, result1);
- Stats result2 = {};
+ StatsValue result2 = {};
ASSERT_EQ(0, bpfGetIfaceStatsInternal(IFACE_NAME2, &result2, mFakeIfaceStatsMap,
mFakeIfaceIndexNameMap));
expectStatsEqual(value2, result2);
- Stats totalResult = {};
+ StatsValue totalResult = {};
ASSERT_EQ(0, bpfGetIfaceStatsInternal(NULL, &totalResult, mFakeIfaceStatsMap,
mFakeIfaceIndexNameMap));
StatsValue totalValue = {
@@ -275,6 +275,20 @@
expectStatsEqual(totalValue, totalResult);
}
+TEST_F(BpfNetworkStatsHelperTest, TestGetIfIndexStatsInternal) {
+ StatsValue value = {
+ .rxPackets = TEST_PACKET0,
+ .rxBytes = TEST_BYTES0,
+ .txPackets = TEST_PACKET1,
+ .txBytes = TEST_BYTES1,
+ };
+ EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(IFACE_INDEX1, value, BPF_ANY));
+
+ StatsValue result = {};
+ ASSERT_EQ(0, bpfGetIfIndexStatsInternal(IFACE_INDEX1, &result, mFakeIfaceStatsMap));
+ expectStatsEqual(value, result);
+}
+
TEST_F(BpfNetworkStatsHelperTest, TestGetStatsDetail) {
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
index 133009f..ea068fc 100644
--- a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
@@ -56,13 +56,16 @@
bool operator<(const stats_line& lhs, const stats_line& rhs);
// For test only
-int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
+int bpfGetUidStatsInternal(uid_t uid, StatsValue* stats,
const BpfMap<uint32_t, StatsValue>& appUidStatsMap);
// For test only
-int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
+int bpfGetIfaceStatsInternal(const char* iface, StatsValue* stats,
const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceNameMap);
// For test only
+int bpfGetIfIndexStatsInternal(uint32_t ifindex, StatsValue* stats,
+ const BpfMap<uint32_t, StatsValue>& ifaceStatsMap);
+// For test only
int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>& lines,
const BpfMap<StatsKey, StatsValue>& statsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceMap);
@@ -110,8 +113,9 @@
const BpfMap<uint32_t, StatsValue>& statsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceMap);
-int bpfGetUidStats(uid_t uid, Stats* stats);
-int bpfGetIfaceStats(const char* iface, Stats* stats);
+int bpfGetUidStats(uid_t uid, StatsValue* stats);
+int bpfGetIfaceStats(const char* iface, StatsValue* stats);
+int bpfGetIfIndexStats(int ifindex, StatsValue* stats);
int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines);
int parseBpfNetworkStatsDev(std::vector<stats_line>* lines);
diff --git a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
index bcedbef..052019f 100644
--- a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
+++ b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
@@ -66,7 +66,8 @@
event.getFoundServiceCount(),
event.getFoundCallbackCount(),
event.getLostCallbackCount(),
- event.getRepliedRequestsCount());
+ event.getRepliedRequestsCount(),
+ event.getSentQueryCount());
}
}
@@ -122,4 +123,102 @@
// TODO: Report repliedRequestsCount
mDependencies.statsWrite(builder.build());
}
+
+ /**
+ * Report service discovery started metric data.
+ *
+ * @param transactionId The transaction id of service discovery.
+ */
+ public void reportServiceDiscoveryStarted(int transactionId) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_DISCOVER);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_DISCOVERY_STARTED);
+ mDependencies.statsWrite(builder.build());
+ }
+
+ /**
+ * Report service discovery failed metric data.
+ *
+ * @param transactionId The transaction id of service discovery.
+ * @param durationMs The duration of service discovery failed.
+ */
+ public void reportServiceDiscoveryFailed(int transactionId, long durationMs) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_DISCOVER);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_DISCOVERY_FAILED);
+ builder.setEventDurationMillisec(durationMs);
+ mDependencies.statsWrite(builder.build());
+ }
+
+ /**
+ * Report service discovery stop metric data.
+ *
+ * @param transactionId The transaction id of service discovery.
+ * @param durationMs The duration of discovering services.
+ * @param foundCallbackCount The count of found service callbacks before stop discovery.
+ * @param lostCallbackCount The count of lost service callbacks before stop discovery.
+ * @param servicesCount The count of found services.
+ */
+ public void reportServiceDiscoveryStop(int transactionId, long durationMs,
+ int foundCallbackCount, int lostCallbackCount, int servicesCount) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_DISCOVER);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_DISCOVERY_STOP);
+ builder.setEventDurationMillisec(durationMs);
+ builder.setFoundCallbackCount(foundCallbackCount);
+ builder.setLostCallbackCount(lostCallbackCount);
+ builder.setFoundServiceCount(servicesCount);
+ mDependencies.statsWrite(builder.build());
+ }
+
+ /**
+ * Report service resolution success metric data.
+ *
+ * @param transactionId The transaction id of service resolution.
+ * @param durationMs The duration of resolving services.
+ * @param isServiceFromCache Whether the resolved service is from cache.
+ */
+ public void reportServiceResolved(int transactionId, long durationMs,
+ boolean isServiceFromCache) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_RESOLVE);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_RESOLVED);
+ builder.setEventDurationMillisec(durationMs);
+ builder.setIsKnownService(isServiceFromCache);
+ mDependencies.statsWrite(builder.build());
+ }
+
+ /**
+ * Report service resolution failed metric data.
+ *
+ * @param transactionId The transaction id of service resolution.
+ * @param durationMs The duration of service resolution failed.
+ */
+ public void reportServiceResolutionFailed(int transactionId, long durationMs) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_RESOLVE);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_RESOLUTION_FAILED);
+ builder.setEventDurationMillisec(durationMs);
+ mDependencies.statsWrite(builder.build());
+ }
+
+ /**
+ * Report service resolution stop metric data.
+ *
+ * @param transactionId The transaction id of service resolution.
+ * @param durationMs The duration before stop resolving the service.
+ */
+ public void reportServiceResolutionStop(int transactionId, long durationMs) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_RESOLVE);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_RESOLUTION_STOP);
+ builder.setEventDurationMillisec(durationMs);
+ mDependencies.statsWrite(builder.build());
+ }
}
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 745c5bc..eb73ee5 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
@@ -46,9 +47,12 @@
import android.net.nsd.INsdManager;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.INsdServiceConnector;
+import android.net.nsd.IOffloadEngine;
import android.net.nsd.MDnsManager;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
+import android.net.nsd.OffloadEngine;
+import android.net.nsd.OffloadServiceInfo;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Handler;
@@ -56,6 +60,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -98,6 +103,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -137,7 +144,7 @@
* "mdns_advertiser_allowlist_othertype_version"
* would be used to toggle MdnsDiscoveryManager / MdnsAdvertiser for each type. The flags will
* be read with
- * {@link DeviceConfigUtils#isFeatureEnabled(Context, String, String, String, boolean)}.
+ * {@link DeviceConfigUtils#isTetheringFeatureEnabled}
*
* @see #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX
* @see #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX
@@ -162,7 +169,9 @@
public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final long CLEANUP_DELAY_MS = 10000;
private static final int IFACE_IDX_ANY = 0;
- private static final int NO_TRANSACTION = -1;
+ private static final int MAX_SERVICES_COUNT_METRIC_PER_CLIENT = 100;
+ @VisibleForTesting
+ static final int NO_TRANSACTION = -1;
private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
private final Context mContext;
@@ -215,7 +224,26 @@
// The number of client that ever connected.
private int mClientNumberId = 1;
- private static class MdnsListener implements MdnsServiceBrowserListener {
+ private final RemoteCallbackList<IOffloadEngine> mOffloadEngines =
+ new RemoteCallbackList<>();
+
+ private static class OffloadEngineInfo {
+ @NonNull final String mInterfaceName;
+ final long mOffloadCapabilities;
+ final long mOffloadType;
+ @NonNull final IOffloadEngine mOffloadEngine;
+
+ OffloadEngineInfo(@NonNull IOffloadEngine offloadEngine,
+ @NonNull String interfaceName, long capabilities, long offloadType) {
+ this.mOffloadEngine = offloadEngine;
+ this.mInterfaceName = interfaceName;
+ this.mOffloadCapabilities = capabilities;
+ this.mOffloadType = offloadType;
+ }
+ }
+
+ @VisibleForTesting
+ static class MdnsListener implements MdnsServiceBrowserListener {
protected final int mClientRequestId;
protected final int mTransactionId;
@NonNull
@@ -237,7 +265,8 @@
}
@Override
- public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) { }
+ public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
+ boolean isServiceFromCache) { }
@Override
public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { }
@@ -246,7 +275,8 @@
public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
@Override
- public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) { }
+ public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
+ boolean isServiceFromCache) { }
@Override
public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
@@ -272,10 +302,11 @@
}
@Override
- public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) {
+ public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
+ boolean isServiceFromCache) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_FOUND,
- new MdnsEvent(mClientRequestId, serviceInfo));
+ new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
}
@Override
@@ -294,10 +325,10 @@
}
@Override
- public void onServiceFound(MdnsServiceInfo serviceInfo) {
+ public void onServiceFound(MdnsServiceInfo serviceInfo, boolean isServiceFromCache) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.RESOLVE_SERVICE_SUCCEEDED,
- new MdnsEvent(mClientRequestId, serviceInfo));
+ new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
}
}
@@ -309,10 +340,11 @@
}
@Override
- public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) {
+ public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
+ boolean isServiceFromCache) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_UPDATED,
- new MdnsEvent(mClientRequestId, serviceInfo));
+ new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
}
@Override
@@ -435,10 +467,17 @@
final int mClientRequestId;
@NonNull
final MdnsServiceInfo mMdnsServiceInfo;
+ final boolean mIsServiceFromCache;
MdnsEvent(int clientRequestId, @NonNull MdnsServiceInfo mdnsServiceInfo) {
+ this(clientRequestId, mdnsServiceInfo, false /* isServiceFromCache */);
+ }
+
+ MdnsEvent(int clientRequestId, @NonNull MdnsServiceInfo mdnsServiceInfo,
+ boolean isServiceFromCache) {
mClientRequestId = clientRequestId;
mMdnsServiceInfo = mdnsServiceInfo;
+ mIsServiceFromCache = isServiceFromCache;
}
}
@@ -559,7 +598,7 @@
case NsdManager.DISCOVER_SERVICES:
cInfo = getClientInfoForReply(msg);
if (cInfo != null) {
- cInfo.onDiscoverServicesFailed(
+ cInfo.onDiscoverServicesFailedImmediately(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
@@ -587,7 +626,7 @@
case NsdManager.RESOLVE_SERVICE:
cInfo = getClientInfoForReply(msg);
if (cInfo != null) {
- cInfo.onResolveServiceFailed(
+ cInfo.onResolveServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
@@ -654,9 +693,9 @@
}
private void storeLegacyRequestMap(int clientRequestId, int transactionId,
- ClientInfo clientInfo, int what) {
- clientInfo.mClientRequests.put(clientRequestId, new LegacyClientRequest(
- transactionId, what, mClock, mClock.elapsedRealtime()));
+ ClientInfo clientInfo, int what, long startTimeMs) {
+ clientInfo.mClientRequests.put(clientRequestId,
+ new LegacyClientRequest(transactionId, what, startTimeMs));
mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
// Remove the cleanup event because here comes a new request.
cancelStop();
@@ -665,7 +704,7 @@
private void storeAdvertiserRequestMap(int clientRequestId, int transactionId,
ClientInfo clientInfo, @Nullable Network requestedNetwork) {
clientInfo.mClientRequests.put(clientRequestId, new AdvertiserClientRequest(
- transactionId, requestedNetwork, mClock, mClock.elapsedRealtime()));
+ transactionId, requestedNetwork, mClock.elapsedRealtime()));
mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
updateMulticastLock();
}
@@ -689,8 +728,7 @@
MdnsListener listener, ClientInfo clientInfo,
@Nullable Network requestedNetwork) {
clientInfo.mClientRequests.put(clientRequestId, new DiscoveryManagerRequest(
- transactionId, listener, requestedNetwork, mClock,
- mClock.elapsedRealtime()));
+ transactionId, listener, requestedNetwork, mClock.elapsedRealtime()));
mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
updateMulticastLock();
}
@@ -719,6 +757,7 @@
final int transactionId;
final int clientRequestId = msg.arg2;
final ListenerArgs args;
+ final OffloadEngineInfo offloadEngineInfo;
switch (msg.what) {
case NsdManager.DISCOVER_SERVICES: {
if (DBG) Log.d(TAG, "Discover services");
@@ -733,7 +772,7 @@
}
if (requestLimitReached(clientInfo)) {
- clientInfo.onDiscoverServicesFailed(
+ clientInfo.onDiscoverServicesFailedImmediately(
clientRequestId, NsdManager.FAILURE_MAX_LIMIT);
break;
}
@@ -748,8 +787,8 @@
|| mDeps.isMdnsDiscoveryManagerEnabled(mContext)
|| useDiscoveryManagerForType(serviceType)) {
if (serviceType == null) {
- clientInfo.onDiscoverServicesFailed(clientRequestId,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onDiscoverServicesFailedImmediately(
+ clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
}
@@ -771,7 +810,8 @@
listenServiceType, listener, optionsBuilder.build());
storeDiscoveryManagerRequestMap(clientRequestId, transactionId,
listener, clientInfo, info.getNetwork());
- clientInfo.onDiscoverServicesStarted(clientRequestId, info);
+ clientInfo.onDiscoverServicesStarted(
+ clientRequestId, info, transactionId);
clientInfo.log("Register a DiscoveryListener " + transactionId
+ " for service type:" + listenServiceType);
} else {
@@ -781,13 +821,14 @@
Log.d(TAG, "Discover " + msg.arg2 + " " + transactionId
+ info.getServiceType());
}
- storeLegacyRequestMap(
- clientRequestId, transactionId, clientInfo, msg.what);
- clientInfo.onDiscoverServicesStarted(clientRequestId, info);
+ storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
+ msg.what, mClock.elapsedRealtime());
+ clientInfo.onDiscoverServicesStarted(
+ clientRequestId, info, transactionId);
} else {
stopServiceDiscovery(transactionId);
- clientInfo.onDiscoverServicesFailed(clientRequestId,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onDiscoverServicesFailedImmediately(
+ clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
}
}
break;
@@ -817,12 +858,12 @@
if (request instanceof DiscoveryManagerRequest) {
stopDiscoveryManagerRequest(
request, clientRequestId, transactionId, clientInfo);
- clientInfo.onStopDiscoverySucceeded(clientRequestId);
+ clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
clientInfo.log("Unregister the DiscoveryListener " + transactionId);
} else {
removeRequestMap(clientRequestId, transactionId, clientInfo);
if (stopServiceDiscovery(transactionId)) {
- clientInfo.onStopDiscoverySucceeded(clientRequestId);
+ clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
} else {
clientInfo.onStopDiscoveryFailed(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
@@ -882,8 +923,8 @@
Log.d(TAG, "Register " + clientRequestId
+ " " + transactionId);
}
- storeLegacyRequestMap(
- clientRequestId, transactionId, clientInfo, msg.what);
+ storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
+ msg.what, mClock.elapsedRealtime());
// Return success after mDns reports success
} else {
unregisterService(transactionId);
@@ -917,14 +958,16 @@
// Note isMdnsAdvertiserEnabled may have changed to false at this point,
// so this needs to check the type of the original request to unregister
// instead of looking at the flag value.
+ final long stopTimeMs = mClock.elapsedRealtime();
if (request instanceof AdvertiserClientRequest) {
mAdvertiser.removeService(transactionId);
clientInfo.onUnregisterServiceSucceeded(clientRequestId, transactionId,
- request.calculateRequestDurationMs());
+ request.calculateRequestDurationMs(stopTimeMs));
} else {
if (unregisterService(transactionId)) {
clientInfo.onUnregisterServiceSucceeded(clientRequestId,
- transactionId, request.calculateRequestDurationMs());
+ transactionId,
+ request.calculateRequestDurationMs(stopTimeMs));
} else {
clientInfo.onUnregisterServiceFailed(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
@@ -954,8 +997,8 @@
|| mDeps.isMdnsDiscoveryManagerEnabled(mContext)
|| useDiscoveryManagerForType(serviceType)) {
if (serviceType == null) {
- clientInfo.onResolveServiceFailed(clientRequestId,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailedImmediately(
+ clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
}
final String resolveServiceType = serviceType + ".local";
@@ -977,7 +1020,7 @@
+ " for service type:" + resolveServiceType);
} else {
if (clientInfo.mResolvedService != null) {
- clientInfo.onResolveServiceFailed(
+ clientInfo.onResolveServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_ALREADY_ACTIVE);
break;
}
@@ -985,10 +1028,10 @@
maybeStartDaemon();
if (resolveService(transactionId, info)) {
clientInfo.mResolvedService = new NsdServiceInfo();
- storeLegacyRequestMap(
- clientRequestId, transactionId, clientInfo, msg.what);
+ storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
+ msg.what, mClock.elapsedRealtime());
} else {
- clientInfo.onResolveServiceFailed(
+ clientInfo.onResolveServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
}
}
@@ -1019,12 +1062,12 @@
if (request instanceof DiscoveryManagerRequest) {
stopDiscoveryManagerRequest(
request, clientRequestId, transactionId, clientInfo);
- clientInfo.onStopResolutionSucceeded(clientRequestId);
+ clientInfo.onStopResolutionSucceeded(clientRequestId, request);
clientInfo.log("Unregister the ResolutionListener " + transactionId);
} else {
removeRequestMap(clientRequestId, transactionId, clientInfo);
if (stopResolveService(transactionId)) {
- clientInfo.onStopResolutionSucceeded(clientRequestId);
+ clientInfo.onStopResolutionSucceeded(clientRequestId, request);
} else {
clientInfo.onStopResolutionFailed(
clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
@@ -1114,6 +1157,16 @@
return NOT_HANDLED;
}
break;
+ case NsdManager.REGISTER_OFFLOAD_ENGINE:
+ offloadEngineInfo = (OffloadEngineInfo) msg.obj;
+ // TODO: Limits the number of registrations created by a given class.
+ mOffloadEngines.register(offloadEngineInfo.mOffloadEngine,
+ offloadEngineInfo);
+ // TODO: Sends all the existing OffloadServiceInfos back.
+ break;
+ case NsdManager.UNREGISTER_OFFLOAD_ENGINE:
+ mOffloadEngines.unregister((IOffloadEngine) msg.obj);
+ break;
default:
return NOT_HANDLED;
}
@@ -1139,6 +1192,11 @@
code, transactionId));
return false;
}
+ final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
+ if (request == null) {
+ Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
+ return false;
+ }
if (DBG) {
Log.d(TAG, String.format(
"MDns service event code:%d transactionId=%d", code, transactionId));
@@ -1163,7 +1221,8 @@
break;
}
setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
- clientInfo.onServiceFound(clientRequestId, servInfo);
+
+ clientInfo.onServiceFound(clientRequestId, servInfo, request);
break;
}
case IMDnsEventListener.SERVICE_LOST: {
@@ -1177,29 +1236,27 @@
// TODO: avoid returning null in that case, possibly by remembering
// found services on the same interface index and their network at the time
setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
- clientInfo.onServiceLost(clientRequestId, servInfo);
+ clientInfo.onServiceLost(clientRequestId, servInfo, request);
break;
}
case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
- clientInfo.onDiscoverServicesFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onDiscoverServicesFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
break;
case IMDnsEventListener.SERVICE_REGISTERED: {
final RegistrationInfo info = (RegistrationInfo) obj;
final String name = info.serviceName;
servInfo = new NsdServiceInfo(name, null /* serviceType */);
- final ClientRequest request =
- clientInfo.mClientRequests.get(clientRequestId);
clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo,
- transactionId, request.calculateRequestDurationMs());
+ transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
break;
}
case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
- final ClientRequest request =
- clientInfo.mClientRequests.get(clientRequestId);
clientInfo.onRegisterServiceFailed(clientRequestId,
NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
- request.calculateRequestDurationMs());
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
break;
case IMDnsEventListener.SERVICE_RESOLVED: {
final ResolutionInfo info = (ResolutionInfo) obj;
@@ -1233,10 +1290,11 @@
final int transactionId2 = getUniqueId();
if (getAddrInfo(transactionId2, info.hostname, info.interfaceIdx)) {
storeLegacyRequestMap(clientRequestId, transactionId2, clientInfo,
- NsdManager.RESOLVE_SERVICE);
+ NsdManager.RESOLVE_SERVICE, request.mStartTimeMs);
} else {
- clientInfo.onResolveServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
clientInfo.mResolvedService = null;
}
break;
@@ -1245,16 +1303,18 @@
/* NNN resolveId errorCode */
stopResolveService(transactionId);
removeRequestMap(clientRequestId, transactionId, clientInfo);
- clientInfo.onResolveServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
clientInfo.mResolvedService = null;
break;
case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
/* NNN resolveId errorCode */
stopGetAddrInfo(transactionId);
removeRequestMap(clientRequestId, transactionId, clientInfo);
- clientInfo.onResolveServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
clientInfo.mResolvedService = null;
break;
case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
@@ -1277,10 +1337,11 @@
setServiceNetworkForCallback(clientInfo.mResolvedService,
netId, info.interfaceIdx);
clientInfo.onResolveServiceSucceeded(
- clientRequestId, clientInfo.mResolvedService);
+ clientRequestId, clientInfo.mResolvedService, request);
} else {
- clientInfo.onResolveServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
}
stopGetAddrInfo(transactionId);
removeRequestMap(clientRequestId, transactionId, clientInfo);
@@ -1355,20 +1416,19 @@
mServiceLogs.log(String.format(
"MdnsDiscoveryManager event code=%s transactionId=%d",
NsdManager.nameOf(code), transactionId));
+ final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
+ if (request == null) {
+ Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
+ return false;
+ }
switch (code) {
case NsdManager.SERVICE_FOUND:
- clientInfo.onServiceFound(clientRequestId, info);
+ clientInfo.onServiceFound(clientRequestId, info, request);
break;
case NsdManager.SERVICE_LOST:
- clientInfo.onServiceLost(clientRequestId, info);
+ clientInfo.onServiceLost(clientRequestId, info, request);
break;
case NsdManager.RESOLVE_SERVICE_SUCCEEDED: {
- final ClientRequest request =
- clientInfo.mClientRequests.get(clientRequestId);
- if (request == null) {
- Log.e(TAG, "Unknown client request in RESOLVE_SERVICE_SUCCEEDED");
- break;
- }
final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
info.setPort(serviceInfo.getPort());
@@ -1384,11 +1444,13 @@
final List<InetAddress> addresses = getInetAddresses(serviceInfo);
if (addresses.size() != 0) {
info.setHostAddresses(addresses);
- clientInfo.onResolveServiceSucceeded(clientRequestId, info);
+ request.setServiceFromCache(event.mIsServiceFromCache);
+ clientInfo.onResolveServiceSucceeded(clientRequestId, info, request);
} else {
// No address. Notify resolution failure.
- clientInfo.onResolveServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
}
// Unregister the listener immediately like IMDnsEventListener design
@@ -1573,7 +1635,8 @@
mRunningAppActiveImportanceCutoff);
mMdnsSocketClient =
- new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
+ new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider,
+ LOGGER.forSubComponent("MdnsMultinetworkSocketClient"));
mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"));
handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
@@ -1594,9 +1657,9 @@
* @return true if the MdnsDiscoveryManager feature is enabled.
*/
public boolean isMdnsDiscoveryManagerEnabled(Context context) {
- return isAtLeastU() || DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING,
- MDNS_DISCOVERY_MANAGER_VERSION, DeviceConfigUtils.TETHERING_MODULE_NAME,
- false /* defaultEnabled */);
+ return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
+ NAMESPACE_TETHERING, MDNS_DISCOVERY_MANAGER_VERSION,
+ DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultEnabled */);
}
/**
@@ -1606,9 +1669,9 @@
* @return true if the MdnsAdvertiser feature is enabled.
*/
public boolean isMdnsAdvertiserEnabled(Context context) {
- return isAtLeastU() || DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING,
- MDNS_ADVERTISER_VERSION, DeviceConfigUtils.TETHERING_MODULE_NAME,
- false /* defaultEnabled */);
+ return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
+ NAMESPACE_TETHERING, MDNS_ADVERTISER_VERSION,
+ DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultEnabled */);
}
/**
@@ -1622,10 +1685,10 @@
}
/**
- * @see DeviceConfigUtils#isFeatureEnabled(Context, String, String, String, boolean)
+ * @see DeviceConfigUtils#isTetheringFeatureEnabled
*/
public boolean isFeatureEnabled(Context context, String feature) {
- return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING,
+ return DeviceConfigUtils.isTetheringFeatureEnabled(context, NAMESPACE_TETHERING,
feature, DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultEnabled */);
}
@@ -1771,7 +1834,42 @@
}
}
+ private void sendOffloadServiceInfosUpdate(@NonNull String targetInterfaceName,
+ @NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove) {
+ final int count = mOffloadEngines.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ final OffloadEngineInfo offloadEngineInfo =
+ (OffloadEngineInfo) mOffloadEngines.getBroadcastCookie(i);
+ final String interfaceName = offloadEngineInfo.mInterfaceName;
+ if (!targetInterfaceName.equals(interfaceName)
+ || ((offloadEngineInfo.mOffloadType
+ & offloadServiceInfo.getOffloadType()) == 0)) {
+ continue;
+ }
+ try {
+ if (isRemove) {
+ mOffloadEngines.getBroadcastItem(i).onOffloadServiceRemoved(
+ offloadServiceInfo);
+ } else {
+ mOffloadEngines.getBroadcastItem(i).onOffloadServiceUpdated(
+ offloadServiceInfo);
+ }
+ } catch (RemoteException e) {
+ // Can happen in regular cases, do not log a stacktrace
+ Log.i(TAG, "Failed to send offload callback, remote died", e);
+ }
+ }
+ } finally {
+ mOffloadEngines.finishBroadcast();
+ }
+ }
+
private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
+ // TODO: add a callback to notify when a service is being added on each interface (as soon
+ // as probing starts), and call mOffloadCallbacks. This callback is for
+ // OFFLOAD_CAPABILITY_FILTER_REPLIES offload type.
+
@Override
public void onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo) {
mServiceLogs.log("onRegisterServiceSucceeded: transactionId " + transactionId);
@@ -1785,8 +1883,8 @@
// historical behavior.
final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null);
final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
- clientInfo.onRegisterServiceSucceeded(
- clientRequestId, cbInfo, transactionId, request.calculateRequestDurationMs());
+ clientInfo.onRegisterServiceSucceeded(clientRequestId, cbInfo, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
}
@Override
@@ -1798,7 +1896,19 @@
if (clientRequestId < 0) return;
final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
clientInfo.onRegisterServiceFailed(clientRequestId, errorCode, transactionId,
- request.calculateRequestDurationMs());
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
+ }
+
+ @Override
+ public void onOffloadStartOrUpdate(@NonNull String interfaceName,
+ @NonNull OffloadServiceInfo offloadServiceInfo) {
+ sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, false /* isRemove */);
+ }
+
+ @Override
+ public void onOffloadStop(@NonNull String interfaceName,
+ @NonNull OffloadServiceInfo offloadServiceInfo) {
+ sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, true /* isRemove */);
}
private ClientInfo getClientInfoOrLog(int transactionId) {
@@ -1837,11 +1947,14 @@
@Override
public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
+ final int uid = mDeps.getCallingUid();
+ if (cb == null) {
+ throw new IllegalArgumentException("Unknown client callback from uid=" + uid);
+ }
if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend);
final INsdServiceConnector connector = new NsdServiceConnector();
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT,
- new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend,
- mDeps.getCallingUid())));
+ new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend, uid)));
return connector;
}
@@ -1920,6 +2033,32 @@
public void binderDied() {
mNsdStateMachine.sendMessage(
mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
+
+ }
+
+ @Override
+ public void registerOffloadEngine(String ifaceName, IOffloadEngine cb,
+ @OffloadEngine.OffloadCapability long offloadCapabilities,
+ @OffloadEngine.OffloadType long offloadTypes) {
+ // TODO: Relax the permission because NETWORK_SETTINGS is a signature permission, and
+ // it may not be possible for all the callers of this API to have it.
+ PermissionUtils.enforceNetworkStackPermissionOr(mContext, NETWORK_SETTINGS);
+ Objects.requireNonNull(ifaceName);
+ Objects.requireNonNull(cb);
+ mNsdStateMachine.sendMessage(
+ mNsdStateMachine.obtainMessage(NsdManager.REGISTER_OFFLOAD_ENGINE,
+ new OffloadEngineInfo(cb, ifaceName, offloadCapabilities,
+ offloadTypes)));
+ }
+
+ @Override
+ public void unregisterOffloadEngine(IOffloadEngine cb) {
+ // TODO: Relax the permission because NETWORK_SETTINGS is a signature permission, and
+ // it may not be possible for all the callers of this API to have it.
+ PermissionUtils.enforceNetworkStackPermissionOr(mContext, NETWORK_SETTINGS);
+ Objects.requireNonNull(cb);
+ mNsdStateMachine.sendMessage(
+ mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_OFFLOAD_ENGINE, cb));
}
}
@@ -2003,25 +2142,41 @@
return IFACE_IDX_ANY;
}
- final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
- if (cm == null) {
- Log.wtf(TAG, "No ConnectivityManager for resolveService");
+ String interfaceName = getNetworkInterfaceName(network);
+ if (interfaceName == null) {
return IFACE_IDX_ANY;
}
- final LinkProperties lp = cm.getLinkProperties(network);
- if (lp == null) return IFACE_IDX_ANY;
+ return getNetworkInterfaceIndexByName(interfaceName);
+ }
+ private String getNetworkInterfaceName(@Nullable Network network) {
+ if (network == null) {
+ return null;
+ }
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ if (cm == null) {
+ Log.wtf(TAG, "No ConnectivityManager");
+ return null;
+ }
+ final LinkProperties lp = cm.getLinkProperties(network);
+ if (lp == null) {
+ return null;
+ }
// Only resolve on non-stacked interfaces
+ return lp.getInterfaceName();
+ }
+
+ private int getNetworkInterfaceIndexByName(final String ifaceName) {
final NetworkInterface iface;
try {
- iface = NetworkInterface.getByName(lp.getInterfaceName());
+ iface = NetworkInterface.getByName(ifaceName);
} catch (SocketException e) {
Log.e(TAG, "Error querying interface", e);
return IFACE_IDX_ANY;
}
if (iface == null) {
- Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
+ Log.e(TAG, "Interface not found: " + ifaceName);
return IFACE_IDX_ANY;
}
@@ -2058,27 +2213,58 @@
private abstract static class ClientRequest {
private final int mTransactionId;
- private final Clock mClock;
private final long mStartTimeMs;
+ private int mFoundServiceCount = 0;
+ private int mLostServiceCount = 0;
+ private final Set<String> mServices = new ArraySet<>();
+ private boolean mIsServiceFromCache = false;
- private ClientRequest(int transactionId, @NonNull Clock clock, long startTimeMs) {
+ private ClientRequest(int transactionId, long startTimeMs) {
mTransactionId = transactionId;
- mClock = clock;
mStartTimeMs = startTimeMs;
}
- public long calculateRequestDurationMs() {
- final long stopTimeMs = mClock.elapsedRealtime();
+ public long calculateRequestDurationMs(long stopTimeMs) {
return stopTimeMs - mStartTimeMs;
}
+
+ public void onServiceFound(String serviceName) {
+ mFoundServiceCount++;
+ if (mServices.size() <= MAX_SERVICES_COUNT_METRIC_PER_CLIENT) {
+ mServices.add(serviceName);
+ }
+ }
+
+ public void onServiceLost() {
+ mLostServiceCount++;
+ }
+
+ public int getFoundServiceCount() {
+ return mFoundServiceCount;
+ }
+
+ public int getLostServiceCount() {
+ return mLostServiceCount;
+ }
+
+ public int getServicesCount() {
+ return mServices.size();
+ }
+
+ public void setServiceFromCache(boolean isServiceFromCache) {
+ mIsServiceFromCache = isServiceFromCache;
+ }
+
+ public boolean isServiceFromCache() {
+ return mIsServiceFromCache;
+ }
}
private static class LegacyClientRequest extends ClientRequest {
private final int mRequestCode;
- private LegacyClientRequest(int transactionId, int requestCode, @NonNull Clock clock,
- long startTimeMs) {
- super(transactionId, clock, startTimeMs);
+ private LegacyClientRequest(int transactionId, int requestCode, long startTimeMs) {
+ super(transactionId, startTimeMs);
mRequestCode = requestCode;
}
}
@@ -2088,8 +2274,8 @@
private final Network mRequestedNetwork;
private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork,
- @NonNull Clock clock, long startTimeMs) {
- super(transactionId, clock, startTimeMs);
+ long startTimeMs) {
+ super(transactionId, startTimeMs);
mRequestedNetwork = requestedNetwork;
}
@@ -2101,8 +2287,8 @@
private static class AdvertiserClientRequest extends JavaBackendClientRequest {
private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork,
- @NonNull Clock clock, long startTimeMs) {
- super(transactionId, requestedNetwork, clock, startTimeMs);
+ long startTimeMs) {
+ super(transactionId, requestedNetwork, startTimeMs);
}
}
@@ -2111,8 +2297,8 @@
private final MdnsListener mListener;
private DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener,
- @Nullable Network requestedNetwork, @NonNull Clock clock, long startTimeMs) {
- super(transactionId, requestedNetwork, clock, startTimeMs);
+ @Nullable Network requestedNetwork, long startTimeMs) {
+ super(transactionId, requestedNetwork, startTimeMs);
mListener = listener;
}
}
@@ -2175,11 +2361,12 @@
mIsPreSClient = true;
}
- private void unregisterMdnsListenerFromRequest(ClientRequest request) {
+ private MdnsListener unregisterMdnsListenerFromRequest(ClientRequest request) {
final MdnsListener listener =
((DiscoveryManagerRequest) request).mListener;
mMdnsDiscoveryManager.unregisterListener(
listener.getListenedServiceType(), listener);
+ return listener;
}
// Remove any pending requests from the global map when we get rid of a client,
@@ -2199,14 +2386,24 @@
}
if (request instanceof DiscoveryManagerRequest) {
- unregisterMdnsListenerFromRequest(request);
+ final MdnsListener listener = unregisterMdnsListenerFromRequest(request);
+ if (listener instanceof DiscoveryListener) {
+ mMetrics.reportServiceDiscoveryStop(transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()),
+ request.getFoundServiceCount(),
+ request.getLostServiceCount(),
+ request.getServicesCount());
+ } else if (listener instanceof ResolutionListener) {
+ mMetrics.reportServiceResolutionStop(transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
+ }
continue;
}
if (request instanceof AdvertiserClientRequest) {
mAdvertiser.removeService(transactionId);
- mMetrics.reportServiceUnregistration(
- transactionId, request.calculateRequestDurationMs());
+ mMetrics.reportServiceUnregistration(transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
continue;
}
@@ -2217,14 +2414,21 @@
switch (((LegacyClientRequest) request).mRequestCode) {
case NsdManager.DISCOVER_SERVICES:
stopServiceDiscovery(transactionId);
+ mMetrics.reportServiceDiscoveryStop(transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()),
+ request.getFoundServiceCount(),
+ request.getLostServiceCount(),
+ request.getServicesCount());
break;
case NsdManager.RESOLVE_SERVICE:
stopResolveService(transactionId);
+ mMetrics.reportServiceResolutionStop(transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
break;
case NsdManager.REGISTER_SERVICE:
unregisterService(transactionId);
- mMetrics.reportServiceUnregistration(
- transactionId, request.calculateRequestDurationMs());
+ mMetrics.reportServiceUnregistration(transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
break;
default:
break;
@@ -2268,15 +2472,21 @@
mClientLogs.log(message);
}
- void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
+ void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info, int transactionId) {
+ mMetrics.reportServiceDiscoveryStarted(transactionId);
try {
mCb.onDiscoverServicesStarted(listenerKey, info);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
}
}
+ void onDiscoverServicesFailedImmediately(int listenerKey, int error) {
+ onDiscoverServicesFailed(listenerKey, error, NO_TRANSACTION, 0L /* durationMs */);
+ }
- void onDiscoverServicesFailed(int listenerKey, int error) {
+ void onDiscoverServicesFailed(int listenerKey, int error, int transactionId,
+ long durationMs) {
+ mMetrics.reportServiceDiscoveryFailed(transactionId, durationMs);
try {
mCb.onDiscoverServicesFailed(listenerKey, error);
} catch (RemoteException e) {
@@ -2284,7 +2494,8 @@
}
}
- void onServiceFound(int listenerKey, NsdServiceInfo info) {
+ void onServiceFound(int listenerKey, NsdServiceInfo info, ClientRequest request) {
+ request.onServiceFound(info.getServiceName());
try {
mCb.onServiceFound(listenerKey, info);
} catch (RemoteException e) {
@@ -2292,7 +2503,8 @@
}
}
- void onServiceLost(int listenerKey, NsdServiceInfo info) {
+ void onServiceLost(int listenerKey, NsdServiceInfo info, ClientRequest request) {
+ request.onServiceLost();
try {
mCb.onServiceLost(listenerKey, info);
} catch (RemoteException e) {
@@ -2308,7 +2520,13 @@
}
}
- void onStopDiscoverySucceeded(int listenerKey) {
+ void onStopDiscoverySucceeded(int listenerKey, ClientRequest request) {
+ mMetrics.reportServiceDiscoveryStop(
+ request.mTransactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()),
+ request.getFoundServiceCount(),
+ request.getLostServiceCount(),
+ request.getServicesCount());
try {
mCb.onStopDiscoverySucceeded(listenerKey);
} catch (RemoteException e) {
@@ -2317,7 +2535,7 @@
}
void onRegisterServiceFailedImmediately(int listenerKey, int error) {
- onRegisterServiceFailed(listenerKey, error, NO_TRANSACTION, 0 /* durationMs */);
+ onRegisterServiceFailed(listenerKey, error, NO_TRANSACTION, 0L /* durationMs */);
}
void onRegisterServiceFailed(int listenerKey, int error, int transactionId,
@@ -2357,7 +2575,13 @@
}
}
- void onResolveServiceFailed(int listenerKey, int error) {
+ void onResolveServiceFailedImmediately(int listenerKey, int error) {
+ onResolveServiceFailed(listenerKey, error, NO_TRANSACTION, 0L /* durationMs */);
+ }
+
+ void onResolveServiceFailed(int listenerKey, int error, int transactionId,
+ long durationMs) {
+ mMetrics.reportServiceResolutionFailed(transactionId, durationMs);
try {
mCb.onResolveServiceFailed(listenerKey, error);
} catch (RemoteException e) {
@@ -2365,7 +2589,12 @@
}
}
- void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info,
+ ClientRequest request) {
+ mMetrics.reportServiceResolved(
+ request.mTransactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()),
+ request.isServiceFromCache());
try {
mCb.onResolveServiceSucceeded(listenerKey, info);
} catch (RemoteException e) {
@@ -2381,7 +2610,10 @@
}
}
- void onStopResolutionSucceeded(int listenerKey) {
+ void onStopResolutionSucceeded(int listenerKey, ClientRequest request) {
+ mMetrics.reportServiceResolutionStop(
+ request.mTransactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
try {
mCb.onStopResolutionSucceeded(listenerKey);
} catch (RemoteException e) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/ConnectivityMonitorWithConnectivityManager.java b/service-t/src/com/android/server/connectivity/mdns/ConnectivityMonitorWithConnectivityManager.java
index 551e3db..87aa0d2 100644
--- a/service-t/src/com/android/server/connectivity/mdns/ConnectivityMonitorWithConnectivityManager.java
+++ b/service-t/src/com/android/server/connectivity/mdns/ConnectivityMonitorWithConnectivityManager.java
@@ -25,13 +25,12 @@
import android.net.NetworkRequest;
import android.os.Build;
-import com.android.server.connectivity.mdns.util.MdnsLogger;
+import com.android.net.module.util.SharedLog;
/** Class for monitoring connectivity changes using {@link ConnectivityManager}. */
public class ConnectivityMonitorWithConnectivityManager implements ConnectivityMonitor {
private static final String TAG = "ConnMntrWConnMgr";
- private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
-
+ private final SharedLog sharedLog;
private final Listener listener;
private final ConnectivityManager.NetworkCallback networkCallback;
private final ConnectivityManager connectivityManager;
@@ -42,8 +41,10 @@
@SuppressWarnings({"nullness:assignment", "nullness:method.invocation"})
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public ConnectivityMonitorWithConnectivityManager(Context context, Listener listener) {
+ public ConnectivityMonitorWithConnectivityManager(Context context, Listener listener,
+ SharedLog sharedLog) {
this.listener = listener;
+ this.sharedLog = sharedLog;
connectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -51,20 +52,20 @@
new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
- LOGGER.log("network available.");
+ sharedLog.log("network available.");
lastAvailableNetwork = network;
notifyConnectivityChange();
}
@Override
public void onLost(Network network) {
- LOGGER.log("network lost.");
+ sharedLog.log("network lost.");
notifyConnectivityChange();
}
@Override
public void onUnavailable() {
- LOGGER.log("network unavailable.");
+ sharedLog.log("network unavailable.");
notifyConnectivityChange();
}
};
@@ -82,7 +83,7 @@
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void startWatchingConnectivityChanges() {
- LOGGER.log("Start watching connectivity changes");
+ sharedLog.log("Start watching connectivity changes");
if (isCallbackRegistered) {
return;
}
@@ -98,7 +99,7 @@
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void stopWatchingConnectivityChanges() {
- LOGGER.log("Stop watching connectivity changes");
+ sharedLog.log("Stop watching connectivity changes");
if (!isCallbackRegistered) {
return;
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
index b7417ed..fa3b646 100644
--- a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
+++ b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
@@ -20,10 +20,9 @@
import android.annotation.NonNull;
import android.text.TextUtils;
-import android.util.Log;
import android.util.Pair;
-import com.android.server.connectivity.mdns.util.MdnsLogger;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
@@ -44,7 +43,6 @@
public class EnqueueMdnsQueryCallable implements Callable<Pair<Integer, List<String>>> {
private static final String TAG = "MdnsQueryCallable";
- private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
private static final List<Integer> castShellEmulatorMdnsPorts;
static {
@@ -77,6 +75,8 @@
private final List<MdnsResponse> servicesToResolve;
@NonNull
private final MdnsUtils.Clock clock;
+ @NonNull
+ private final SharedLog sharedLog;
private final boolean onlyUseIpv6OnIpv6OnlyNetworks;
EnqueueMdnsQueryCallable(
@@ -90,7 +90,8 @@
boolean onlyUseIpv6OnIpv6OnlyNetworks,
boolean sendDiscoveryQueries,
@NonNull Collection<MdnsResponse> servicesToResolve,
- @NonNull MdnsUtils.Clock clock) {
+ @NonNull MdnsUtils.Clock clock,
+ @NonNull SharedLog sharedLog) {
weakRequestSender = new WeakReference<>(requestSender);
this.packetWriter = packetWriter;
serviceTypeLabels = TextUtils.split(serviceType, "\\.");
@@ -102,6 +103,7 @@
this.sendDiscoveryQueries = sendDiscoveryQueries;
this.servicesToResolve = new ArrayList<>(servicesToResolve);
this.clock = clock;
+ this.sharedLog = sharedLog;
}
/**
@@ -200,7 +202,7 @@
}
return Pair.create(transactionId, subtypes);
} catch (IOException e) {
- LOGGER.e(String.format("Failed to create mDNS packet for subtype: %s.",
+ sharedLog.e(String.format("Failed to create mDNS packet for subtype: %s.",
TextUtils.join(",", subtypes)), e);
return Pair.create(INVALID_TRANSACTION_ID, new ArrayList<>());
}
@@ -242,13 +244,13 @@
sendPacket(requestSender,
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), port));
} catch (IOException e) {
- Log.i(TAG, "Can't send packet to IPv4", e);
+ sharedLog.e("Can't send packet to IPv4", e);
}
try {
sendPacket(requestSender,
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), port));
} catch (IOException e) {
- Log.i(TAG, "Can't send packet to IPv6", e);
+ sharedLog.e("Can't send packet to IPv6", e);
}
}
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
index 158d7a3..dd72d11 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
@@ -24,15 +24,19 @@
import android.net.Network;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
+import android.net.nsd.OffloadEngine;
+import android.net.nsd.OffloadServiceInfo;
import android.os.Looper;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.util.MdnsUtils;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -68,9 +72,10 @@
new ArrayMap<>();
private final SparseArray<Registration> mRegistrations = new SparseArray<>();
private final Dependencies mDeps;
-
private String[] mDeviceHostName;
@NonNull private final SharedLog mSharedLog;
+ private final Map<String, List<OffloadServiceInfoWrapper>> mInterfaceOffloadServices =
+ new ArrayMap<>();
/**
* Dependencies for {@link MdnsAdvertiser}, useful for testing.
@@ -115,18 +120,32 @@
private final MdnsInterfaceAdvertiser.Callback mInterfaceAdvertiserCb =
new MdnsInterfaceAdvertiser.Callback() {
@Override
- public void onRegisterServiceSucceeded(
+ public void onServiceProbingSucceeded(
@NonNull MdnsInterfaceAdvertiser advertiser, int serviceId) {
+ final Registration registration = mRegistrations.get(serviceId);
+ if (registration == null) {
+ mSharedLog.wtf("Register succeeded for unknown registration");
+ return;
+ }
+
+ final String interfaceName = advertiser.getSocketInterfaceName();
+ final List<OffloadServiceInfoWrapper> existingOffloadServiceInfoWrappers =
+ mInterfaceOffloadServices.computeIfAbsent(
+ interfaceName, k -> new ArrayList<>());
+ // Remove existing offload services from cache for update.
+ existingOffloadServiceInfoWrappers.removeIf(item -> item.mServiceId == serviceId);
+ final OffloadServiceInfoWrapper newOffloadServiceInfoWrapper = createOffloadService(
+ serviceId,
+ registration);
+ existingOffloadServiceInfoWrappers.add(newOffloadServiceInfoWrapper);
+ mCb.onOffloadStartOrUpdate(interfaceName,
+ newOffloadServiceInfoWrapper.mOffloadServiceInfo);
+
// Wait for all current interfaces to be done probing before notifying of success.
if (any(mAllAdvertisers, (k, a) -> a.isProbing(serviceId))) return;
// The service may still be unregistered/renamed if a conflict is found on a later added
// interface, or if a conflicting announcement/reply is detected (RFC6762 9.)
- final Registration registration = mRegistrations.get(serviceId);
- if (registration == null) {
- Log.wtf(TAG, "Register succeeded for unknown registration");
- return;
- }
if (!registration.mNotifiedRegistrationSuccess) {
mCb.onRegisterServiceSucceeded(serviceId, registration.getServiceInfo());
registration.mNotifiedRegistrationSuccess = true;
@@ -148,7 +167,12 @@
registration.mNotifiedRegistrationSuccess = false;
// The service was done probing, just reset it to probing state (RFC6762 9.)
- forAllAdvertisers(a -> a.restartProbingForConflict(serviceId));
+ forAllAdvertisers(a -> {
+ if (!a.maybeRestartProbingForConflict(serviceId)) {
+ return;
+ }
+ maybeSendOffloadStop(a.getSocketInterfaceName(), serviceId);
+ });
return;
}
@@ -196,6 +220,22 @@
registration.updateForConflict(newInfo, renameCount);
}
+ private void maybeSendOffloadStop(final String interfaceName, int serviceId) {
+ final List<OffloadServiceInfoWrapper> existingOffloadServiceInfoWrappers =
+ mInterfaceOffloadServices.get(interfaceName);
+ if (existingOffloadServiceInfoWrappers == null) {
+ return;
+ }
+ // Stop the offloaded service by matching the service id
+ int idx = CollectionUtils.indexOf(existingOffloadServiceInfoWrappers,
+ item -> item.mServiceId == serviceId);
+ if (idx >= 0) {
+ mCb.onOffloadStop(interfaceName,
+ existingOffloadServiceInfoWrappers.get(idx).mOffloadServiceInfo);
+ existingOffloadServiceInfoWrappers.remove(idx);
+ }
+ }
+
/**
* A request for a {@link MdnsInterfaceAdvertiser}.
*
@@ -221,7 +261,22 @@
* @return true if this {@link InterfaceAdvertiserRequest} should now be deleted.
*/
boolean onAdvertiserDestroyed(@NonNull MdnsInterfaceSocket socket) {
- mAdvertisers.remove(socket);
+ final MdnsInterfaceAdvertiser removedAdvertiser = mAdvertisers.remove(socket);
+ if (removedAdvertiser != null) {
+ final String interfaceName = removedAdvertiser.getSocketInterfaceName();
+ // If the interface is destroyed, stop all hardware offloading on that interface.
+ final List<OffloadServiceInfoWrapper> offloadServiceInfoWrappers =
+ mInterfaceOffloadServices.remove(
+ interfaceName);
+ if (offloadServiceInfoWrappers != null) {
+ for (OffloadServiceInfoWrapper offloadServiceInfoWrapper :
+ offloadServiceInfoWrappers) {
+ mCb.onOffloadStop(interfaceName,
+ offloadServiceInfoWrapper.mOffloadServiceInfo);
+ }
+ }
+ }
+
if (mAdvertisers.size() == 0 && mPendingRegistrations.size() == 0) {
// No advertiser is using sockets from this request anymore (in particular for exit
// announcements), and there is no registration so newer sockets will not be
@@ -274,7 +329,8 @@
mAdvertisers.valueAt(i).addService(
id, registration.getServiceInfo(), registration.getSubtype());
} catch (NameConflictException e) {
- Log.wtf(TAG, "Name conflict adding services that should have unique names", e);
+ mSharedLog.wtf("Name conflict adding services that should have unique names",
+ e);
}
}
}
@@ -282,7 +338,10 @@
void removeService(int id) {
mPendingRegistrations.remove(id);
for (int i = 0; i < mAdvertisers.size(); i++) {
- mAdvertisers.valueAt(i).removeService(id);
+ final MdnsInterfaceAdvertiser advertiser = mAdvertisers.valueAt(i);
+ advertiser.removeService(id);
+
+ maybeSendOffloadStop(advertiser.getSocketInterfaceName(), id);
}
}
@@ -305,7 +364,8 @@
advertiser.addService(mPendingRegistrations.keyAt(i),
registration.getServiceInfo(), registration.getSubtype());
} catch (NameConflictException e) {
- Log.wtf(TAG, "Name conflict adding services that should have unique names", e);
+ mSharedLog.wtf("Name conflict adding services that should have unique names",
+ e);
}
}
}
@@ -325,6 +385,16 @@
}
}
+ private static class OffloadServiceInfoWrapper {
+ private final @NonNull OffloadServiceInfo mOffloadServiceInfo;
+ private final int mServiceId;
+
+ OffloadServiceInfoWrapper(int serviceId, OffloadServiceInfo offloadServiceInfo) {
+ mOffloadServiceInfo = offloadServiceInfo;
+ mServiceId = serviceId;
+ }
+ }
+
private static class Registration {
@NonNull
final String mOriginalName;
@@ -425,6 +495,24 @@
// Unregistration is notified immediately as success in NsdService so no callback is needed
// here.
+
+ /**
+ * Called when a service is ready to be sent for hardware offloading.
+ *
+ * @param interfaceName the interface for sending the update to.
+ * @param offloadServiceInfo the offloading content.
+ */
+ void onOffloadStartOrUpdate(@NonNull String interfaceName,
+ @NonNull OffloadServiceInfo offloadServiceInfo);
+
+ /**
+ * Called when a service is removed or the MdnsInterfaceAdvertiser is destroyed.
+ *
+ * @param interfaceName the interface for sending the update to.
+ * @param offloadServiceInfo the offloading content.
+ */
+ void onOffloadStop(@NonNull String interfaceName,
+ @NonNull OffloadServiceInfo offloadServiceInfo);
}
public MdnsAdvertiser(@NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
@@ -459,7 +547,7 @@
public void addService(int id, NsdServiceInfo service, @Nullable String subtype) {
checkThread();
if (mRegistrations.get(id) != null) {
- Log.e(TAG, "Adding duplicate registration for " + service);
+ mSharedLog.e("Adding duplicate registration for " + service);
// TODO (b/264986328): add a more specific error code
mCb.onRegisterServiceFailed(id, NsdManager.FAILURE_INTERNAL_ERROR);
return;
@@ -525,4 +613,28 @@
return false;
});
}
+
+ private OffloadServiceInfoWrapper createOffloadService(int serviceId,
+ @NonNull Registration registration) {
+ final NsdServiceInfo nsdServiceInfo = registration.getServiceInfo();
+ List<String> subTypes = new ArrayList<>();
+ String subType = registration.getSubtype();
+ if (subType != null) {
+ subTypes.add(subType);
+ }
+ final OffloadServiceInfo offloadServiceInfo = new OffloadServiceInfo(
+ new OffloadServiceInfo.Key(nsdServiceInfo.getServiceName(),
+ nsdServiceInfo.getServiceType()),
+ subTypes,
+ String.join(".", mDeviceHostName),
+ null /* rawOffloadPacket */,
+ // TODO: define overlayable resources in
+ // ServiceConnectivityResources that set the priority based on
+ // service type.
+ 0 /* priority */,
+ // TODO: set the offloadType based on the callback timing.
+ OffloadEngine.OFFLOAD_TYPE_REPLY);
+ return new OffloadServiceInfoWrapper(serviceId, offloadServiceInfo);
+ }
+
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsAnnouncer.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAnnouncer.java
index 27fc945..fd2c32e 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsAnnouncer.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAnnouncer.java
@@ -21,6 +21,7 @@
import android.os.Looper;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.SharedLog;
import java.util.Collections;
import java.util.List;
@@ -39,9 +40,6 @@
private static final long EXIT_DELAY_MS = 2000L;
private static final int EXIT_COUNT = 3;
- @NonNull
- private final String mLogTag;
-
/** Base class for announcement requests to send with {@link MdnsAnnouncer}. */
public abstract static class BaseAnnouncementInfo implements MdnsPacketRepeater.Request {
private final int mServiceId;
@@ -105,16 +103,11 @@
}
}
- public MdnsAnnouncer(@NonNull String interfaceTag, @NonNull Looper looper,
+ public MdnsAnnouncer(@NonNull Looper looper,
@NonNull MdnsReplySender replySender,
- @Nullable PacketRepeaterCallback<BaseAnnouncementInfo> cb) {
- super(looper, replySender, cb);
- mLogTag = MdnsAnnouncer.class.getSimpleName() + "/" + interfaceTag;
- }
-
- @Override
- protected String getTag() {
- return mLogTag;
+ @Nullable PacketRepeaterCallback<BaseAnnouncementInfo> cb,
+ @NonNull SharedLog sharedLog) {
+ super(looper, replySender, cb, sharedLog);
}
// TODO: Notify MdnsRecordRepository that the records were announced for that service ID,
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
index 724a704..a83b852 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
@@ -22,7 +22,6 @@
import android.net.nsd.NsdServiceInfo;
import android.os.Handler;
import android.os.Looper;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.HexDump;
@@ -42,8 +41,6 @@
@VisibleForTesting
public static final long EXIT_ANNOUNCEMENT_DELAY_MS = 100L;
@NonNull
- private final String mTag;
- @NonNull
private final ProbingCallback mProbingCallback = new ProbingCallback();
@NonNull
private final AnnouncingCallback mAnnouncingCallback = new AnnouncingCallback();
@@ -73,7 +70,7 @@
/**
* Called by the advertiser after it successfully registered a service, after probing.
*/
- void onRegisterServiceSucceeded(@NonNull MdnsInterfaceAdvertiser advertiser, int serviceId);
+ void onServiceProbingSucceeded(@NonNull MdnsInterfaceAdvertiser advertiser, int serviceId);
/**
* Called by the advertiser when a conflict was found, during or after probing.
@@ -101,7 +98,7 @@
public void onFinished(MdnsProber.ProbingInfo info) {
final MdnsAnnouncer.AnnouncementInfo announcementInfo;
mSharedLog.i("Probing finished for service " + info.getServiceId());
- mCbHandler.post(() -> mCb.onRegisterServiceSucceeded(
+ mCbHandler.post(() -> mCb.onServiceProbingSucceeded(
MdnsInterfaceAdvertiser.this, info.getServiceId()));
try {
announcementInfo = mRecordRepository.onProbingSucceeded(info);
@@ -151,22 +148,30 @@
/** @see MdnsReplySender */
@NonNull
public MdnsReplySender makeReplySender(@NonNull String interfaceTag, @NonNull Looper looper,
- @NonNull MdnsInterfaceSocket socket, @NonNull byte[] packetCreationBuffer) {
- return new MdnsReplySender(interfaceTag, looper, socket, packetCreationBuffer);
+ @NonNull MdnsInterfaceSocket socket, @NonNull byte[] packetCreationBuffer,
+ @NonNull SharedLog sharedLog) {
+ return new MdnsReplySender(looper, socket, packetCreationBuffer,
+ sharedLog.forSubComponent(
+ MdnsReplySender.class.getSimpleName() + "/" + interfaceTag));
}
/** @see MdnsAnnouncer */
public MdnsAnnouncer makeMdnsAnnouncer(@NonNull String interfaceTag, @NonNull Looper looper,
@NonNull MdnsReplySender replySender,
- @Nullable PacketRepeaterCallback<MdnsAnnouncer.BaseAnnouncementInfo> cb) {
- return new MdnsAnnouncer(interfaceTag, looper, replySender, cb);
+ @Nullable PacketRepeaterCallback<MdnsAnnouncer.BaseAnnouncementInfo> cb,
+ @NonNull SharedLog sharedLog) {
+ return new MdnsAnnouncer(looper, replySender, cb,
+ sharedLog.forSubComponent(
+ MdnsAnnouncer.class.getSimpleName() + "/" + interfaceTag));
}
/** @see MdnsProber */
public MdnsProber makeMdnsProber(@NonNull String interfaceTag, @NonNull Looper looper,
@NonNull MdnsReplySender replySender,
- @NonNull PacketRepeaterCallback<MdnsProber.ProbingInfo> cb) {
- return new MdnsProber(interfaceTag, looper, replySender, cb);
+ @NonNull PacketRepeaterCallback<MdnsProber.ProbingInfo> cb,
+ @NonNull SharedLog sharedLog) {
+ return new MdnsProber(looper, replySender, cb, sharedLog.forSubComponent(
+ MdnsProber.class.getSimpleName() + "/" + interfaceTag));
}
}
@@ -182,17 +187,17 @@
@NonNull List<LinkAddress> initialAddresses, @NonNull Looper looper,
@NonNull byte[] packetCreationBuffer, @NonNull Callback cb, @NonNull Dependencies deps,
@NonNull String[] deviceHostName, @NonNull SharedLog sharedLog) {
- mTag = MdnsInterfaceAdvertiser.class.getSimpleName() + "/" + sharedLog.getTag();
mRecordRepository = deps.makeRecordRepository(looper, deviceHostName);
mRecordRepository.updateAddresses(initialAddresses);
mSocket = socket;
mCb = cb;
mCbHandler = new Handler(looper);
mReplySender = deps.makeReplySender(sharedLog.getTag(), looper, socket,
- packetCreationBuffer);
+ packetCreationBuffer, sharedLog);
mAnnouncer = deps.makeMdnsAnnouncer(sharedLog.getTag(), looper, mReplySender,
- mAnnouncingCallback);
- mProber = deps.makeMdnsProber(sharedLog.getTag(), looper, mReplySender, mProbingCallback);
+ mAnnouncingCallback, sharedLog);
+ mProber = deps.makeMdnsProber(sharedLog.getTag(), looper, mReplySender, mProbingCallback,
+ sharedLog);
mSharedLog = sharedLog;
}
@@ -282,11 +287,12 @@
/**
* Reset a service to the probing state due to a conflict found on the network.
*/
- public void restartProbingForConflict(int serviceId) {
+ public boolean maybeRestartProbingForConflict(int serviceId) {
final MdnsProber.ProbingInfo probingInfo = mRecordRepository.setServiceProbing(serviceId);
- if (probingInfo == null) return;
+ if (probingInfo == null) return false;
mProber.restartForConflict(probingInfo);
+ return true;
}
/**
@@ -317,20 +323,18 @@
try {
packet = MdnsPacket.parse(new MdnsPacketReader(recvbuf, length));
} catch (MdnsPacket.ParseException e) {
- Log.e(mTag, "Error parsing mDNS packet", e);
+ mSharedLog.e("Error parsing mDNS packet", e);
if (DBG) {
- Log.v(
- mTag, "Packet: " + HexDump.toHexString(recvbuf, 0, length));
+ mSharedLog.v("Packet: " + HexDump.toHexString(recvbuf, 0, length));
}
return;
}
if (DBG) {
- Log.v(mTag,
- "Parsed packet with " + packet.questions.size() + " questions, "
- + packet.answers.size() + " answers, "
- + packet.authorityRecords.size() + " authority, "
- + packet.additionalRecords.size() + " additional from " + src);
+ mSharedLog.v("Parsed packet with " + packet.questions.size() + " questions, "
+ + packet.answers.size() + " answers, "
+ + packet.authorityRecords.size() + " authority, "
+ + packet.additionalRecords.size() + " additional from " + src);
}
for (int conflictServiceId : mRecordRepository.getConflictingServices(packet)) {
@@ -346,4 +350,8 @@
if (answers == null) return;
mReplySender.queueReply(answers);
}
+
+ public String getSocketInterfaceName() {
+ return mSocket.getInterface().getName();
+ }
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
index 119c7a8..534f8d0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
@@ -28,7 +28,8 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
-import android.util.Log;
+
+import com.android.net.module.util.SharedLog;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -54,11 +55,12 @@
@NonNull private final NetworkInterface mNetworkInterface;
@NonNull private final MulticastPacketReader mPacketReader;
@NonNull private final ParcelFileDescriptor mFileDescriptor;
+ @NonNull private final SharedLog mSharedLog;
private boolean mJoinedIpv4 = false;
private boolean mJoinedIpv6 = false;
public MdnsInterfaceSocket(@NonNull NetworkInterface networkInterface, int port,
- @NonNull Looper looper, @NonNull byte[] packetReadBuffer)
+ @NonNull Looper looper, @NonNull byte[] packetReadBuffer, @NonNull SharedLog sharedLog)
throws IOException {
mNetworkInterface = networkInterface;
mMulticastSocket = new MulticastSocket(port);
@@ -80,6 +82,8 @@
mPacketReader = new MulticastPacketReader(networkInterface.getName(), mFileDescriptor,
new Handler(looper), packetReadBuffer);
mPacketReader.start();
+
+ mSharedLog = sharedLog;
}
/**
@@ -117,7 +121,7 @@
return true;
} catch (IOException e) {
// The address may have just been removed
- Log.e(TAG, "Error joining multicast group for " + mNetworkInterface, e);
+ mSharedLog.e("Error joining multicast group for " + mNetworkInterface, e);
return false;
}
}
@@ -148,7 +152,7 @@
try {
mFileDescriptor.close();
} catch (IOException e) {
- Log.e(TAG, "Close file descriptor failed.");
+ mSharedLog.e("Close file descriptor failed.");
}
mMulticastSocket.close();
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
index d1fa57c..2ef7368 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -25,7 +25,8 @@
import android.os.Handler;
import android.os.Looper;
import android.util.ArrayMap;
-import android.util.Log;
+
+import com.android.net.module.util.SharedLog;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -33,7 +34,6 @@
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.util.List;
-import java.util.Objects;
/**
* The {@link MdnsMultinetworkSocketClient} manages the multinetwork socket for mDns
@@ -46,26 +46,27 @@
@NonNull private final Handler mHandler;
@NonNull private final MdnsSocketProvider mSocketProvider;
+ @NonNull private final SharedLog mSharedLog;
- private final ArrayMap<MdnsServiceBrowserListener, InterfaceSocketCallback> mRequestedNetworks =
+ private final ArrayMap<MdnsServiceBrowserListener, InterfaceSocketCallback> mSocketRequests =
new ArrayMap<>();
- private final ArrayMap<MdnsInterfaceSocket, ReadPacketHandler> mSocketPacketHandlers =
- new ArrayMap<>();
+ private final ArrayMap<SocketKey, ReadPacketHandler> mSocketPacketHandlers = new ArrayMap<>();
private MdnsSocketClientBase.Callback mCallback = null;
private int mReceivedPacketNumber = 0;
public MdnsMultinetworkSocketClient(@NonNull Looper looper,
- @NonNull MdnsSocketProvider provider) {
+ @NonNull MdnsSocketProvider provider,
+ @NonNull SharedLog sharedLog) {
mHandler = new Handler(looper);
mSocketProvider = provider;
+ mSharedLog = sharedLog;
}
private class InterfaceSocketCallback implements MdnsSocketProvider.SocketCallback {
@NonNull
private final SocketCreationCallback mSocketCreationCallback;
@NonNull
- private final ArrayMap<MdnsInterfaceSocket, SocketKey> mActiveSockets =
- new ArrayMap<>();
+ private final ArrayMap<SocketKey, MdnsInterfaceSocket> mActiveSockets = new ArrayMap<>();
InterfaceSocketCallback(SocketCreationCallback socketCreationCallback) {
mSocketCreationCallback = socketCreationCallback;
@@ -76,27 +77,27 @@
@NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> addresses) {
// The socket may be already created by other request before, try to get the stored
// ReadPacketHandler.
- ReadPacketHandler handler = mSocketPacketHandlers.get(socket);
+ ReadPacketHandler handler = mSocketPacketHandlers.get(socketKey);
if (handler == null) {
// First request to create this socket. Initial a ReadPacketHandler for this socket.
handler = new ReadPacketHandler(socketKey);
- mSocketPacketHandlers.put(socket, handler);
+ mSocketPacketHandlers.put(socketKey, handler);
}
socket.addPacketHandler(handler);
- mActiveSockets.put(socket, socketKey);
+ mActiveSockets.put(socketKey, socket);
mSocketCreationCallback.onSocketCreated(socketKey);
}
@Override
public void onInterfaceDestroyed(@NonNull SocketKey socketKey,
@NonNull MdnsInterfaceSocket socket) {
- notifySocketDestroyed(socket);
- maybeCleanupPacketHandler(socket);
+ notifySocketDestroyed(socketKey);
+ maybeCleanupPacketHandler(socketKey);
}
- private void notifySocketDestroyed(@NonNull MdnsInterfaceSocket socket) {
- final SocketKey socketKey = mActiveSockets.remove(socket);
- if (!isSocketActive(socket)) {
+ private void notifySocketDestroyed(@NonNull SocketKey socketKey) {
+ mActiveSockets.remove(socketKey);
+ if (!isSocketActive(socketKey)) {
mSocketCreationCallback.onSocketDestroyed(socketKey);
}
}
@@ -104,35 +105,38 @@
void onNetworkUnrequested() {
for (int i = mActiveSockets.size() - 1; i >= 0; i--) {
// Iterate from the end so the socket can be removed
- final MdnsInterfaceSocket socket = mActiveSockets.keyAt(i);
- notifySocketDestroyed(socket);
- maybeCleanupPacketHandler(socket);
+ final SocketKey socketKey = mActiveSockets.keyAt(i);
+ notifySocketDestroyed(socketKey);
+ maybeCleanupPacketHandler(socketKey);
}
}
}
- private boolean isSocketActive(@NonNull MdnsInterfaceSocket socket) {
- for (int i = 0; i < mRequestedNetworks.size(); i++) {
- final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
- if (isc.mActiveSockets.containsKey(socket)) {
+ private boolean isSocketActive(@NonNull SocketKey socketKey) {
+ for (int i = 0; i < mSocketRequests.size(); i++) {
+ final InterfaceSocketCallback ifaceSocketCallback = mSocketRequests.valueAt(i);
+ if (ifaceSocketCallback.mActiveSockets.containsKey(socketKey)) {
return true;
}
}
return false;
}
- private ArrayMap<MdnsInterfaceSocket, SocketKey> getActiveSockets() {
- final ArrayMap<MdnsInterfaceSocket, SocketKey> sockets = new ArrayMap<>();
- for (int i = 0; i < mRequestedNetworks.size(); i++) {
- final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
- sockets.putAll(isc.mActiveSockets);
+ @Nullable
+ private MdnsInterfaceSocket getTargetSocket(@NonNull SocketKey targetSocketKey) {
+ for (int i = 0; i < mSocketRequests.size(); i++) {
+ final InterfaceSocketCallback ifaceSocketCallback = mSocketRequests.valueAt(i);
+ final int index = ifaceSocketCallback.mActiveSockets.indexOfKey(targetSocketKey);
+ if (index >= 0) {
+ return ifaceSocketCallback.mActiveSockets.valueAt(index);
+ }
}
- return sockets;
+ return null;
}
- private void maybeCleanupPacketHandler(@NonNull MdnsInterfaceSocket socket) {
- if (isSocketActive(socket)) return;
- mSocketPacketHandlers.remove(socket);
+ private void maybeCleanupPacketHandler(@NonNull SocketKey socketKey) {
+ if (isSocketActive(socketKey)) return;
+ mSocketPacketHandlers.remove(socketKey);
}
private class ReadPacketHandler implements MulticastPacketReader.PacketHandler {
@@ -167,14 +171,14 @@
public void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener,
@Nullable Network network, @NonNull SocketCreationCallback socketCreationCallback) {
ensureRunningOnHandlerThread(mHandler);
- InterfaceSocketCallback callback = mRequestedNetworks.get(listener);
+ InterfaceSocketCallback callback = mSocketRequests.get(listener);
if (callback != null) {
throw new IllegalArgumentException("Can not register duplicated listener");
}
- if (DBG) Log.d(TAG, "notifyNetworkRequested: network=" + network);
+ if (DBG) mSharedLog.v("notifyNetworkRequested: network=" + network);
callback = new InterfaceSocketCallback(socketCreationCallback);
- mRequestedNetworks.put(listener, callback);
+ mSocketRequests.put(listener, callback);
mSocketProvider.requestSocket(network, callback);
}
@@ -182,14 +186,14 @@
@Override
public void notifyNetworkUnrequested(@NonNull MdnsServiceBrowserListener listener) {
ensureRunningOnHandlerThread(mHandler);
- final InterfaceSocketCallback callback = mRequestedNetworks.get(listener);
+ final InterfaceSocketCallback callback = mSocketRequests.get(listener);
if (callback == null) {
- Log.e(TAG, "Can not be unrequested with unknown listener=" + listener);
+ mSharedLog.e("Can not be unrequested with unknown listener=" + listener);
return;
}
callback.onNetworkUnrequested();
- // onNetworkUnrequested does cleanups based on mRequestedNetworks, only remove afterwards
- mRequestedNetworks.remove(listener);
+ // onNetworkUnrequested does cleanups based on mSocketRequests, only remove afterwards
+ mSocketRequests.remove(listener);
mSocketProvider.unrequestSocket(callback);
}
@@ -205,42 +209,28 @@
private void sendMdnsPacket(@NonNull DatagramPacket packet, @NonNull SocketKey targetSocketKey,
boolean onlyUseIpv6OnIpv6OnlyNetworks) {
+ final MdnsInterfaceSocket socket = getTargetSocket(targetSocketKey);
+ if (socket == null) {
+ mSharedLog.e("No socket matches targetSocketKey=" + targetSocketKey);
+ return;
+ }
+
final boolean isIpv6 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
instanceof Inet6Address;
final boolean isIpv4 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
instanceof Inet4Address;
- final ArrayMap<MdnsInterfaceSocket, SocketKey> activeSockets = getActiveSockets();
- boolean shouldQueryIpv6 = !onlyUseIpv6OnIpv6OnlyNetworks || isIpv6OnlySockets(
- activeSockets, targetSocketKey);
- for (int i = 0; i < activeSockets.size(); i++) {
- final MdnsInterfaceSocket socket = activeSockets.keyAt(i);
- final SocketKey socketKey = activeSockets.valueAt(i);
- // Check ip capability and network before sending packet
- if (((isIpv6 && socket.hasJoinedIpv6() && shouldQueryIpv6)
- || (isIpv4 && socket.hasJoinedIpv4()))
- && Objects.equals(socketKey, targetSocketKey)) {
- try {
- socket.send(packet);
- } catch (IOException e) {
- Log.e(TAG, "Failed to send a mDNS packet.", e);
- }
+ final boolean shouldQueryIpv6 = !onlyUseIpv6OnIpv6OnlyNetworks || !socket.hasJoinedIpv4();
+ // Check ip capability and network before sending packet
+ if ((isIpv6 && socket.hasJoinedIpv6() && shouldQueryIpv6)
+ || (isIpv4 && socket.hasJoinedIpv4())) {
+ try {
+ socket.send(packet);
+ } catch (IOException e) {
+ mSharedLog.e("Failed to send a mDNS packet.", e);
}
}
}
- private boolean isIpv6OnlySockets(
- @NonNull ArrayMap<MdnsInterfaceSocket, SocketKey> activeSockets,
- @NonNull SocketKey targetSocketKey) {
- for (int i = 0; i < activeSockets.size(); i++) {
- final MdnsInterfaceSocket socket = activeSockets.keyAt(i);
- final SocketKey socketKey = activeSockets.valueAt(i);
- if (Objects.equals(socketKey, targetSocketKey) && socket.hasJoinedIpv4()) {
- return false;
- }
- }
- return true;
- }
-
private void processResponsePacket(byte[] recvbuf, int length, @NonNull SocketKey socketKey) {
int packetNumber = ++mReceivedPacketNumber;
@@ -249,7 +239,7 @@
response = MdnsResponseDecoder.parseResponse(recvbuf, length);
} catch (MdnsPacket.ParseException e) {
if (e.code != MdnsResponseErrorCode.ERROR_NOT_RESPONSE_MESSAGE) {
- Log.e(TAG, e.getMessage(), e);
+ mSharedLog.e(e.getMessage(), e);
if (mCallback != null) {
mCallback.onFailedToParseMdnsResponse(packetNumber, e.code, socketKey);
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPacketRepeater.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
index 4c385da..644560c 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
@@ -24,7 +24,8 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.util.Log;
+
+import com.android.net.module.util.SharedLog;
import java.io.IOException;
import java.net.InetSocketAddress;
@@ -45,6 +46,8 @@
protected final Handler mHandler;
@Nullable
private final PacketRepeaterCallback<T> mCb;
+ @NonNull
+ private final SharedLog mSharedLog;
/**
* Status callback from {@link MdnsPacketRepeater}.
@@ -87,12 +90,6 @@
int getNumSends();
}
- /**
- * Get the logging tag to use.
- */
- @NonNull
- protected abstract String getTag();
-
private final class ProbeHandler extends Handler {
ProbeHandler(@NonNull Looper looper) {
super(looper);
@@ -112,7 +109,7 @@
final MdnsPacket packet = request.getPacket(index);
if (DBG) {
- Log.v(getTag(), "Sending packets for iteration " + index + " out of "
+ mSharedLog.v("Sending packets for iteration " + index + " out of "
+ request.getNumSends() + " for ID " + msg.what);
}
// Send to both v4 and v6 addresses; the reply sender will take care of ignoring the
@@ -121,7 +118,7 @@
try {
mReplySender.sendNow(packet, destination);
} catch (IOException e) {
- Log.e(getTag(), "Error sending packet to " + destination, e);
+ mSharedLog.e("Error sending packet to " + destination, e);
}
}
@@ -133,7 +130,7 @@
// likely not to be available since the device is in deep sleep anyway.
final long delay = request.getDelayMs(nextIndex);
sendMessageDelayed(obtainMessage(msg.what, nextIndex, 0, request), delay);
- if (DBG) Log.v(getTag(), "Scheduled next packet in " + delay + "ms");
+ if (DBG) mSharedLog.v("Scheduled next packet in " + delay + "ms");
}
// Call onSent after scheduling the next run, to allow the callback to cancel it
@@ -144,15 +141,16 @@
}
protected MdnsPacketRepeater(@NonNull Looper looper, @NonNull MdnsReplySender replySender,
- @Nullable PacketRepeaterCallback<T> cb) {
+ @Nullable PacketRepeaterCallback<T> cb, @NonNull SharedLog sharedLog) {
mHandler = new ProbeHandler(looper);
mReplySender = replySender;
mCb = cb;
+ mSharedLog = sharedLog;
}
protected void startSending(int id, @NonNull T request, long initialDelayMs) {
if (DBG) {
- Log.v(getTag(), "Starting send with id " + id + ", request "
+ mSharedLog.v("Starting send with id " + id + ", request "
+ request.getClass().getSimpleName() + ", delay " + initialDelayMs);
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(id, 0, 0, request), initialDelayMs);
@@ -171,7 +169,7 @@
// message cannot be cancelled.
if (mHandler.hasMessages(id)) {
if (DBG) {
- Log.v(getTag(), "Stopping send on id " + id);
+ mSharedLog.v("Stopping send on id " + id);
}
mHandler.removeMessages(id);
return true;
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java b/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
index ecf846e..ba37f32 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
@@ -21,6 +21,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.util.ArrayList;
@@ -34,14 +35,11 @@
*/
public class MdnsProber extends MdnsPacketRepeater<MdnsProber.ProbingInfo> {
private static final long CONFLICT_RETRY_DELAY_MS = 5_000L;
- @NonNull
- private final String mLogTag;
- public MdnsProber(@NonNull String interfaceTag, @NonNull Looper looper,
- @NonNull MdnsReplySender replySender,
- @NonNull PacketRepeaterCallback<ProbingInfo> cb) {
- super(looper, replySender, cb);
- mLogTag = MdnsProber.class.getSimpleName() + "/" + interfaceTag;
+ public MdnsProber(@NonNull Looper looper, @NonNull MdnsReplySender replySender,
+ @NonNull PacketRepeaterCallback<ProbingInfo> cb,
+ @NonNull SharedLog sharedLog) {
+ super(looper, replySender, cb, sharedLog);
}
/** Probing request to send with {@link MdnsProber}. */
@@ -118,11 +116,6 @@
}
}
- @NonNull
- @Override
- protected String getTag() {
- return mLogTag;
- }
@VisibleForTesting
protected long getInitialDelay() {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java b/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java
new file mode 100644
index 0000000..3fcf0d4
--- /dev/null
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java
@@ -0,0 +1,144 @@
+/*
+ * 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 com.android.server.connectivity.mdns;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * The query scheduler class for calculating next query tasks parameters.
+ * <p>
+ * The class is not thread-safe and needs to be used on a consistent thread.
+ */
+public class MdnsQueryScheduler {
+
+ /**
+ * The argument for tracking the query tasks status.
+ */
+ public static class ScheduledQueryTaskArgs {
+ public final QueryTaskConfig config;
+ public final long timeToRun;
+ public final long minTtlExpirationTimeWhenScheduled;
+ public final long sessionId;
+
+ ScheduledQueryTaskArgs(@NonNull QueryTaskConfig config, long timeToRun,
+ long minTtlExpirationTimeWhenScheduled, long sessionId) {
+ this.config = config;
+ this.timeToRun = timeToRun;
+ this.minTtlExpirationTimeWhenScheduled = minTtlExpirationTimeWhenScheduled;
+ this.sessionId = sessionId;
+ }
+ }
+
+ @Nullable
+ private ScheduledQueryTaskArgs mLastScheduledQueryTaskArgs;
+
+ public MdnsQueryScheduler() {
+ }
+
+ /**
+ * Cancel the scheduled run. The method needed to be called when the scheduled task need to
+ * be canceled and rescheduling is not need.
+ */
+ public void cancelScheduledRun() {
+ mLastScheduledQueryTaskArgs = null;
+ }
+
+ /**
+ * Calculates ScheduledQueryTaskArgs for rescheduling the current task. Returns null if the
+ * rescheduling is not necessary.
+ */
+ @Nullable
+ public ScheduledQueryTaskArgs maybeRescheduleCurrentRun(long now,
+ long minRemainingTtl, long lastSentTime, long sessionId) {
+ if (mLastScheduledQueryTaskArgs == null) {
+ return null;
+ }
+ if (!mLastScheduledQueryTaskArgs.config.shouldUseQueryBackoff()) {
+ return null;
+ }
+
+ final long timeToRun = calculateTimeToRun(mLastScheduledQueryTaskArgs,
+ mLastScheduledQueryTaskArgs.config, now, minRemainingTtl, lastSentTime);
+
+ if (timeToRun <= mLastScheduledQueryTaskArgs.timeToRun) {
+ return null;
+ }
+
+ mLastScheduledQueryTaskArgs = new ScheduledQueryTaskArgs(mLastScheduledQueryTaskArgs.config,
+ timeToRun,
+ minRemainingTtl + now,
+ sessionId);
+ return mLastScheduledQueryTaskArgs;
+ }
+
+ /**
+ * Calculates the ScheduledQueryTaskArgs for the next run.
+ */
+ @NonNull
+ public ScheduledQueryTaskArgs scheduleNextRun(
+ @NonNull QueryTaskConfig currentConfig,
+ long minRemainingTtl,
+ long now,
+ long lastSentTime,
+ long sessionId) {
+ final QueryTaskConfig nextRunConfig = currentConfig.getConfigForNextRun();
+ final long timeToRun;
+ if (mLastScheduledQueryTaskArgs == null) {
+ timeToRun = now + nextRunConfig.delayUntilNextTaskWithoutBackoffMs;
+ } else {
+ timeToRun = calculateTimeToRun(mLastScheduledQueryTaskArgs,
+ nextRunConfig, now, minRemainingTtl, lastSentTime);
+ }
+ mLastScheduledQueryTaskArgs = new ScheduledQueryTaskArgs(nextRunConfig, timeToRun,
+ minRemainingTtl + now,
+ sessionId);
+ return mLastScheduledQueryTaskArgs;
+ }
+
+ /**
+ * Calculates the ScheduledQueryTaskArgs for the initial run.
+ */
+ public ScheduledQueryTaskArgs scheduleFirstRun(@NonNull QueryTaskConfig taskConfig,
+ long now, long minRemainingTtl, long currentSessionId) {
+ mLastScheduledQueryTaskArgs = new ScheduledQueryTaskArgs(taskConfig, now /* timeToRun */,
+ now + minRemainingTtl/* minTtlExpirationTimeWhenScheduled */,
+ currentSessionId);
+ return mLastScheduledQueryTaskArgs;
+ }
+
+ private static long calculateTimeToRun(@NonNull ScheduledQueryTaskArgs taskArgs,
+ QueryTaskConfig queryTaskConfig, long now, long minRemainingTtl, long lastSentTime) {
+ final long baseDelayInMs = queryTaskConfig.delayUntilNextTaskWithoutBackoffMs;
+ if (!queryTaskConfig.shouldUseQueryBackoff()) {
+ return lastSentTime + baseDelayInMs;
+ }
+ if (minRemainingTtl <= 0) {
+ // There's no service, or there is an expired service. In any case, schedule for the
+ // minimum time, which is the base delay.
+ return lastSentTime + baseDelayInMs;
+ }
+ // If the next TTL expiration time hasn't changed, then use previous calculated timeToRun.
+ if (lastSentTime < now
+ && taskArgs.minTtlExpirationTimeWhenScheduled == now + minRemainingTtl) {
+ // Use the original scheduling time if the TTL has not changed, to avoid continuously
+ // rescheduling to 80% of the remaining TTL as time passes
+ return taskArgs.timeToRun;
+ }
+ return Math.max(now + (long) (0.8 * minRemainingTtl), lastSentTime + baseDelayInMs);
+ }
+}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java b/service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java
index 8bc598d..16c7d27 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java
@@ -22,8 +22,8 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.util.Log;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.MdnsRecordRepository.ReplyInfo;
import java.io.IOException;
@@ -43,21 +43,21 @@
public class MdnsReplySender {
private static final boolean DBG = MdnsAdvertiser.DBG;
private static final int MSG_SEND = 1;
-
- private final String mLogTag;
@NonNull
private final MdnsInterfaceSocket mSocket;
@NonNull
private final Handler mHandler;
@NonNull
private final byte[] mPacketCreationBuffer;
+ @NonNull
+ private final SharedLog mSharedLog;
- public MdnsReplySender(@NonNull String interfaceTag, @NonNull Looper looper,
- @NonNull MdnsInterfaceSocket socket, @NonNull byte[] packetCreationBuffer) {
+ public MdnsReplySender(@NonNull Looper looper, @NonNull MdnsInterfaceSocket socket,
+ @NonNull byte[] packetCreationBuffer, @NonNull SharedLog sharedLog) {
mHandler = new SendHandler(looper);
- mLogTag = MdnsReplySender.class.getSimpleName() + "/" + interfaceTag;
mSocket = socket;
mPacketCreationBuffer = packetCreationBuffer;
+ mSharedLog = sharedLog;
}
/**
@@ -69,7 +69,7 @@
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SEND, reply), reply.sendDelayMs);
if (DBG) {
- Log.v(mLogTag, "Scheduling " + reply);
+ mSharedLog.v("Scheduling " + reply);
}
}
@@ -134,7 +134,7 @@
@Override
public void handleMessage(@NonNull Message msg) {
final ReplyInfo replyInfo = (ReplyInfo) msg.obj;
- if (DBG) Log.v(mLogTag, "Sending " + replyInfo);
+ if (DBG) mSharedLog.v("Sending " + replyInfo);
final int flags = 0x8400; // Response, authoritative (rfc6762 18.4)
final MdnsPacket packet = new MdnsPacket(flags,
@@ -146,7 +146,7 @@
try {
sendNow(packet, replyInfo.destination);
} catch (IOException e) {
- Log.e(mLogTag, "Error sending MDNS response", e);
+ mSharedLog.e("Error sending MDNS response", e);
}
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
index a0a538e..2f10bde 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
@@ -23,7 +23,6 @@
import android.util.ArraySet;
import android.util.Pair;
-import com.android.server.connectivity.mdns.util.MdnsLogger;
import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.EOFException;
@@ -35,7 +34,6 @@
public class MdnsResponseDecoder {
public static final int SUCCESS = 0;
private static final String TAG = "MdnsResponseDecoder";
- private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
private final boolean allowMultipleSrvRecordsPerHost =
MdnsConfigs.allowMultipleSrvRecordsPerHost();
@Nullable private final String[] serviceType;
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
index 7c19359..4c3cbc0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
@@ -32,8 +32,9 @@
* service records (PTR, SRV, TXT, A or AAAA) are received .
*
* @param serviceInfo The found mDNS service instance.
+ * @param isServiceFromCache Whether the found mDNS service is from cache.
*/
- void onServiceFound(@NonNull MdnsServiceInfo serviceInfo);
+ void onServiceFound(@NonNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache);
/**
* Called when an mDNS service instance is updated. This method would be called only if all
@@ -84,8 +85,9 @@
* record has been received.
*
* @param serviceInfo The discovered mDNS service instance.
+ * @param isServiceFromCache Whether the discovered mDNS service is from cache.
*/
- void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo);
+ void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache);
/**
* Called when a discovered mDNS service instance is no longer valid and removed.
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
index b5fd8a0..861d8d1 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -65,6 +65,7 @@
@NonNull private final SocketKey socketKey;
@NonNull private final SharedLog sharedLog;
@NonNull private final Handler handler;
+ @NonNull private final MdnsQueryScheduler mdnsQueryScheduler;
@NonNull private final Dependencies dependencies;
/**
* The service caches for each socket. It should be accessed from looper thread only.
@@ -82,9 +83,6 @@
// QueryTask for
// new subtypes. It stays the same between packets for same subtypes.
private long currentSessionId = 0;
-
- @Nullable
- private ScheduledQueryTaskArgs lastScheduledQueryTaskArgs;
private long lastSentTime;
private class QueryTaskHandler extends Handler {
@@ -97,7 +95,8 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_START_QUERYTASK: {
- final ScheduledQueryTaskArgs taskArgs = (ScheduledQueryTaskArgs) msg.obj;
+ final MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs =
+ (MdnsQueryScheduler.ScheduledQueryTaskArgs) msg.obj;
// QueryTask should be run immediately after being created (not be scheduled in
// advance). Because the result of "makeResponsesForResolve" depends on answers
// that were received before it is called, so to take into account all answers
@@ -126,15 +125,21 @@
tryRemoveServiceAfterTtlExpires();
- final QueryTaskConfig nextRunConfig =
- sentResult.taskArgs.config.getConfigForNextRun();
final long now = clock.elapsedRealtime();
lastSentTime = now;
final long minRemainingTtl = getMinRemainingTtl(now);
- final long timeToRun = calculateTimeToRun(lastScheduledQueryTaskArgs,
- nextRunConfig, now, minRemainingTtl, lastSentTime);
- scheduleNextRun(nextRunConfig, minRemainingTtl, now, timeToRun,
- lastScheduledQueryTaskArgs.sessionId);
+ MdnsQueryScheduler.ScheduledQueryTaskArgs args =
+ mdnsQueryScheduler.scheduleNextRun(
+ sentResult.taskArgs.config,
+ minRemainingTtl,
+ now,
+ lastSentTime,
+ sentResult.taskArgs.sessionId
+ );
+ dependencies.sendMessageDelayed(
+ handler,
+ handler.obtainMessage(EVENT_START_QUERYTASK, args),
+ calculateTimeToNextTask(args, now, sharedLog));
break;
}
default:
@@ -219,6 +224,7 @@
this.handler = new QueryTaskHandler(looper);
this.dependencies = dependencies;
this.serviceCache = serviceCache;
+ this.mdnsQueryScheduler = new MdnsQueryScheduler();
}
private static MdnsServiceInfo buildMdnsServiceInfoFromResponse(
@@ -291,15 +297,16 @@
if (!responseMatchesOptions(existingResponse, searchOptions)) continue;
final MdnsServiceInfo info =
buildMdnsServiceInfoFromResponse(existingResponse, serviceTypeLabels);
- listener.onServiceNameDiscovered(info);
+ listener.onServiceNameDiscovered(info, true /* isServiceFromCache */);
if (existingResponse.isComplete()) {
- listener.onServiceFound(info);
+ listener.onServiceFound(info, true /* isServiceFromCache */);
hadReply = true;
}
}
}
// Remove the next scheduled periodical task.
removeScheduledTask();
+ mdnsQueryScheduler.cancelScheduledRun();
// Keep tracking the ScheduledFuture for the task so we can cancel it if caller is not
// interested anymore.
final QueryTaskConfig taskConfig = new QueryTaskConfig(
@@ -312,18 +319,25 @@
if (lastSentTime == 0) {
lastSentTime = now;
}
+ final long minRemainingTtl = getMinRemainingTtl(now);
if (hadReply) {
- final QueryTaskConfig queryTaskConfig = taskConfig.getConfigForNextRun();
- final long minRemainingTtl = getMinRemainingTtl(now);
- final long timeToRun = now + queryTaskConfig.delayUntilNextTaskWithoutBackoffMs;
- scheduleNextRun(
- queryTaskConfig, minRemainingTtl, now, timeToRun, currentSessionId);
+ MdnsQueryScheduler.ScheduledQueryTaskArgs args =
+ mdnsQueryScheduler.scheduleNextRun(
+ taskConfig,
+ minRemainingTtl,
+ now,
+ lastSentTime,
+ currentSessionId
+ );
+ dependencies.sendMessageDelayed(
+ handler,
+ handler.obtainMessage(EVENT_START_QUERYTASK, args),
+ calculateTimeToNextTask(args, now, sharedLog));
} else {
final List<MdnsResponse> servicesToResolve = makeResponsesForResolve(socketKey);
- lastScheduledQueryTaskArgs = new ScheduledQueryTaskArgs(taskConfig, now /* timeToRun */,
- now + getMinRemainingTtl(now)/* minTtlExpirationTimeWhenScheduled */,
- currentSessionId);
- final QueryTask queryTask = new QueryTask(lastScheduledQueryTaskArgs, servicesToResolve,
+ final QueryTask queryTask = new QueryTask(
+ mdnsQueryScheduler.scheduleFirstRun(taskConfig, now,
+ minRemainingTtl, currentSessionId), servicesToResolve,
servicesToResolve.size() < listeners.size() /* sendDiscoveryQueries */);
executor.submit(queryTask);
}
@@ -341,7 +355,6 @@
sharedLog.log("Remove EVENT_START_QUERYTASK"
+ ", current session: " + currentSessionId);
++currentSessionId;
- lastScheduledQueryTaskArgs = null;
}
private boolean responseMatchesOptions(@NonNull MdnsResponse response,
@@ -378,6 +391,7 @@
}
if (listeners.isEmpty()) {
removeScheduledTask();
+ mdnsQueryScheduler.cancelScheduledRun();
}
return listeners.isEmpty();
}
@@ -421,18 +435,18 @@
serviceCache.addOrUpdateService(serviceType, socketKey, response);
}
}
- if (dependencies.hasMessages(handler, EVENT_START_QUERYTASK)
- && lastScheduledQueryTaskArgs != null
- && lastScheduledQueryTaskArgs.config.shouldUseQueryBackoff()) {
+ if (dependencies.hasMessages(handler, EVENT_START_QUERYTASK)) {
final long now = clock.elapsedRealtime();
final long minRemainingTtl = getMinRemainingTtl(now);
- final long timeToRun = calculateTimeToRun(lastScheduledQueryTaskArgs,
- lastScheduledQueryTaskArgs.config, now,
- minRemainingTtl, lastSentTime);
- if (timeToRun > lastScheduledQueryTaskArgs.timeToRun) {
- QueryTaskConfig lastTaskConfig = lastScheduledQueryTaskArgs.config;
+ MdnsQueryScheduler.ScheduledQueryTaskArgs args =
+ mdnsQueryScheduler.maybeRescheduleCurrentRun(now, minRemainingTtl,
+ lastSentTime, currentSessionId + 1);
+ if (args != null) {
removeScheduledTask();
- scheduleNextRun(lastTaskConfig, minRemainingTtl, now, timeToRun, currentSessionId);
+ dependencies.sendMessageDelayed(
+ handler,
+ handler.obtainMessage(EVENT_START_QUERYTASK, args),
+ calculateTimeToNextTask(args, now, sharedLog));
}
}
}
@@ -464,6 +478,7 @@
}
}
removeScheduledTask();
+ mdnsQueryScheduler.cancelScheduledRun();
}
private void onResponseModified(@NonNull MdnsResponse response) {
@@ -497,13 +512,13 @@
final MdnsServiceBrowserListener listener = listeners.keyAt(i);
if (newServiceFound) {
sharedLog.log("onServiceNameDiscovered: " + serviceInfo);
- listener.onServiceNameDiscovered(serviceInfo);
+ listener.onServiceNameDiscovered(serviceInfo, false /* isServiceFromCache */);
}
if (response.isComplete()) {
if (newServiceFound || serviceBecomesComplete) {
sharedLog.log("onServiceFound: " + serviceInfo);
- listener.onServiceFound(serviceInfo);
+ listener.onServiceFound(serviceInfo, false /* isServiceFromCache */);
} else {
sharedLog.log("onServiceUpdated: " + serviceInfo);
listener.onServiceUpdated(serviceInfo);
@@ -599,28 +614,14 @@
}
}
- private static class ScheduledQueryTaskArgs {
- private final QueryTaskConfig config;
- private final long timeToRun;
- private final long minTtlExpirationTimeWhenScheduled;
- private final long sessionId;
-
- ScheduledQueryTaskArgs(@NonNull QueryTaskConfig config, long timeToRun,
- long minTtlExpirationTimeWhenScheduled, long sessionId) {
- this.config = config;
- this.timeToRun = timeToRun;
- this.minTtlExpirationTimeWhenScheduled = minTtlExpirationTimeWhenScheduled;
- this.sessionId = sessionId;
- }
- }
private static class QuerySentArguments {
private final int transactionId;
private final List<String> subTypes = new ArrayList<>();
- private final ScheduledQueryTaskArgs taskArgs;
+ private final MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs;
QuerySentArguments(int transactionId, @NonNull List<String> subTypes,
- @NonNull ScheduledQueryTaskArgs taskArgs) {
+ @NonNull MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs) {
this.transactionId = transactionId;
this.subTypes.addAll(subTypes);
this.taskArgs = taskArgs;
@@ -629,12 +630,10 @@
// A FutureTask that enqueues a single query, and schedule a new FutureTask for the next task.
private class QueryTask implements Runnable {
-
- private final ScheduledQueryTaskArgs taskArgs;
+ private final MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs;
private final List<MdnsResponse> servicesToResolve = new ArrayList<>();
private final boolean sendDiscoveryQueries;
-
- QueryTask(@NonNull ScheduledQueryTaskArgs taskArgs,
+ QueryTask(@NonNull MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs,
@NonNull List<MdnsResponse> servicesToResolve, boolean sendDiscoveryQueries) {
this.taskArgs = taskArgs;
this.servicesToResolve.addAll(servicesToResolve);
@@ -657,7 +656,8 @@
taskArgs.config.onlyUseIpv6OnIpv6OnlyNetworks,
sendDiscoveryQueries,
servicesToResolve,
- clock)
+ clock,
+ sharedLog)
.call();
} catch (RuntimeException e) {
sharedLog.e(String.format("Failed to run EnqueueMdnsQueryCallable for subtype: %s",
@@ -670,27 +670,6 @@
}
}
- private static long calculateTimeToRun(@NonNull ScheduledQueryTaskArgs taskArgs,
- QueryTaskConfig queryTaskConfig, long now, long minRemainingTtl, long lastSentTime) {
- final long baseDelayInMs = queryTaskConfig.delayUntilNextTaskWithoutBackoffMs;
- if (!queryTaskConfig.shouldUseQueryBackoff()) {
- return lastSentTime + baseDelayInMs;
- }
- if (minRemainingTtl <= 0) {
- // There's no service, or there is an expired service. In any case, schedule for the
- // minimum time, which is the base delay.
- return lastSentTime + baseDelayInMs;
- }
- // If the next TTL expiration time hasn't changed, then use previous calculated timeToRun.
- if (lastSentTime < now
- && taskArgs.minTtlExpirationTimeWhenScheduled == now + minRemainingTtl) {
- // Use the original scheduling time if the TTL has not changed, to avoid continuously
- // rescheduling to 80% of the remaining TTL as time passes
- return taskArgs.timeToRun;
- }
- return Math.max(now + (long) (0.8 * minRemainingTtl), lastSentTime + baseDelayInMs);
- }
-
private long getMinRemainingTtl(long now) {
long minRemainingTtl = Long.MAX_VALUE;
for (MdnsResponse response : serviceCache.getCachedServices(serviceType, socketKey)) {
@@ -710,19 +689,11 @@
return minRemainingTtl == Long.MAX_VALUE ? 0 : minRemainingTtl;
}
- @NonNull
- private void scheduleNextRun(@NonNull QueryTaskConfig nextRunConfig,
- long minRemainingTtl,
- long timeWhenScheduled, long timeToRun, long sessionId) {
- lastScheduledQueryTaskArgs = new ScheduledQueryTaskArgs(nextRunConfig, timeToRun,
- minRemainingTtl + timeWhenScheduled, sessionId);
- // The timeWhenScheduled could be greater than the timeToRun if the Runnable is delayed.
- long timeToNextTasksWithBackoffInMs = Math.max(timeToRun - timeWhenScheduled, 0);
+ private static long calculateTimeToNextTask(MdnsQueryScheduler.ScheduledQueryTaskArgs args,
+ long now, SharedLog sharedLog) {
+ long timeToNextTasksWithBackoffInMs = Math.max(args.timeToRun - now, 0);
sharedLog.log(String.format("Next run: sessionId: %d, in %d ms",
- lastScheduledQueryTaskArgs.sessionId, timeToNextTasksWithBackoffInMs));
- dependencies.sendMessageDelayed(
- handler,
- handler.obtainMessage(EVENT_START_QUERYTASK, lastScheduledQueryTaskArgs),
- timeToNextTasksWithBackoffInMs);
+ args.sessionId, timeToNextTasksWithBackoffInMs));
+ return timeToNextTasksWithBackoffInMs;
}
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocket.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocket.java
index cdd9f76..d690032 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocket.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocket.java
@@ -21,7 +21,7 @@
import android.net.Network;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.connectivity.mdns.util.MdnsLogger;
+import com.android.net.module.util.SharedLog;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -37,8 +37,6 @@
* @see MulticastSocket for javadoc of each public method.
*/
public class MdnsSocket {
- private static final MdnsLogger LOGGER = new MdnsLogger("MdnsSocket");
-
static final int INTERFACE_INDEX_UNSPECIFIED = -1;
public static final InetSocketAddress MULTICAST_IPV4_ADDRESS =
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
@@ -47,19 +45,22 @@
private final MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider;
private final MulticastSocket multicastSocket;
private boolean isOnIPv6OnlyNetwork;
+ private final SharedLog sharedLog;
public MdnsSocket(
- @NonNull MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider, int port)
+ @NonNull MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider, int port,
+ SharedLog sharedLog)
throws IOException {
- this(multicastNetworkInterfaceProvider, new MulticastSocket(port));
+ this(multicastNetworkInterfaceProvider, new MulticastSocket(port), sharedLog);
}
@VisibleForTesting
MdnsSocket(@NonNull MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider,
- MulticastSocket multicastSocket) throws IOException {
+ MulticastSocket multicastSocket, SharedLog sharedLog) throws IOException {
this.multicastNetworkInterfaceProvider = multicastNetworkInterfaceProvider;
this.multicastNetworkInterfaceProvider.startWatchingConnectivityChanges();
this.multicastSocket = multicastSocket;
+ this.sharedLog = sharedLog;
// RFC Spec: https://tools.ietf.org/html/rfc6762
// Time to live is set 255, which is similar to the jMDNS implementation.
multicastSocket.setTimeToLive(255);
@@ -130,7 +131,7 @@
try {
return multicastSocket.getNetworkInterface().getIndex();
} catch (SocketException e) {
- LOGGER.e("Failed to retrieve interface index for socket.", e);
+ sharedLog.e("Failed to retrieve interface index for socket.", e);
return -1;
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
index 9c9812d..d18a19b 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
@@ -27,7 +27,7 @@
import android.text.format.DateUtils;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.connectivity.mdns.util.MdnsLogger;
+import com.android.net.module.util.SharedLog;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -57,7 +57,6 @@
private static final String CAST_SENDER_LOG_SOURCE = "CAST_SENDER_SDK";
private static final String CAST_PREFS_NAME = "google_cast";
private static final String PREF_CAST_SENDER_ID = "PREF_CAST_SENDER_ID";
- private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
private static final String MULTICAST_TYPE = "multicast";
private static final String UNICAST_TYPE = "unicast";
@@ -105,8 +104,11 @@
@Nullable private Timer logMdnsPacketTimer;
private AtomicInteger packetsCount;
@Nullable private Timer checkMulticastResponseTimer;
+ private final SharedLog sharedLog;
- public MdnsSocketClient(@NonNull Context context, @NonNull MulticastLock multicastLock) {
+ public MdnsSocketClient(@NonNull Context context, @NonNull MulticastLock multicastLock,
+ SharedLog sharedLog) {
+ this.sharedLog = sharedLog;
this.context = context;
this.multicastLock = multicastLock;
if (useSeparateSocketForUnicast) {
@@ -125,7 +127,7 @@
@Override
public synchronized void startDiscovery() throws IOException {
if (multicastSocket != null) {
- LOGGER.w("Discovery is already in progress.");
+ sharedLog.w("Discovery is already in progress.");
return;
}
@@ -136,11 +138,11 @@
shouldStopSocketLoop = false;
try {
// TODO (changed when importing code): consider setting thread stats tag
- multicastSocket = createMdnsSocket(MdnsConstants.MDNS_PORT);
+ multicastSocket = createMdnsSocket(MdnsConstants.MDNS_PORT, sharedLog);
multicastSocket.joinGroup();
if (useSeparateSocketForUnicast) {
// For unicast, use port 0 and the system will assign it with any available port.
- unicastSocket = createMdnsSocket(0);
+ unicastSocket = createMdnsSocket(0, sharedLog);
}
multicastLock.acquire();
} catch (IOException e) {
@@ -164,7 +166,7 @@
@RequiresPermission(permission.CHANGE_WIFI_MULTICAST_STATE)
@Override
public void stopDiscovery() {
- LOGGER.log("Stop discovery.");
+ sharedLog.log("Stop discovery.");
if (multicastSocket == null && unicastSocket == null) {
return;
}
@@ -233,7 +235,7 @@
private void sendMdnsPacket(DatagramPacket packet, Queue<DatagramPacket> packetQueueToUse,
boolean onlyUseIpv6OnIpv6OnlyNetworks) {
if (shouldStopSocketLoop && !MdnsConfigs.allowAddMdnsPacketAfterDiscoveryStops()) {
- LOGGER.w("sendMdnsPacket() is called after discovery already stopped");
+ sharedLog.w("sendMdnsPacket() is called after discovery already stopped");
return;
}
@@ -260,7 +262,7 @@
private void createAndStartSendThread() {
if (sendThread != null) {
- LOGGER.w("A socket thread already exists.");
+ sharedLog.w("A socket thread already exists.");
return;
}
sendThread = new Thread(this::sendThreadMain);
@@ -270,7 +272,7 @@
private void createAndStartReceiverThreads() {
if (multicastReceiveThread != null) {
- LOGGER.w("A multicast receiver thread already exists.");
+ sharedLog.w("A multicast receiver thread already exists.");
return;
}
multicastReceiveThread =
@@ -292,12 +294,12 @@
}
private void triggerSendThread() {
- LOGGER.log("Trigger send thread.");
+ sharedLog.log("Trigger send thread.");
Thread sendThread = this.sendThread;
if (sendThread != null) {
sendThread.interrupt();
} else {
- LOGGER.w("Socket thread is null");
+ sharedLog.w("Socket thread is null");
}
}
@@ -314,9 +316,9 @@
}
private void waitForSendThreadToStop() {
- LOGGER.log("wait For Send Thread To Stop");
+ sharedLog.log("wait For Send Thread To Stop");
if (sendThread == null) {
- LOGGER.w("socket thread is already dead.");
+ sharedLog.w("socket thread is already dead.");
return;
}
waitForThread(sendThread);
@@ -331,7 +333,7 @@
thread.interrupt();
thread.join(waitMs);
if (thread.isAlive()) {
- LOGGER.w("Failed to join thread: " + thread);
+ sharedLog.w("Failed to join thread: " + thread);
}
break;
} catch (InterruptedException e) {
@@ -390,13 +392,13 @@
}
}
} finally {
- LOGGER.log("Send thread stopped.");
+ sharedLog.log("Send thread stopped.");
try {
if (multicastSocket != null) {
multicastSocket.leaveGroup();
}
} catch (Exception t) {
- LOGGER.e("Failed to leave the group.", t);
+ sharedLog.e("Failed to leave the group.", t);
}
// Close the socket first. This is the only way to interrupt a blocking receive.
@@ -409,7 +411,7 @@
unicastSocket.close();
}
} catch (RuntimeException t) {
- LOGGER.e("Failed to close the mdns socket.", t);
+ sharedLog.e("Failed to close the mdns socket.", t);
}
}
}
@@ -439,11 +441,11 @@
}
} catch (IOException e) {
if (!shouldStopSocketLoop) {
- LOGGER.e("Failed to receive mDNS packets.", e);
+ sharedLog.e("Failed to receive mDNS packets.", e);
}
}
}
- LOGGER.log("Receive thread stopped.");
+ sharedLog.log("Receive thread stopped.");
}
private int processResponsePacket(@NonNull DatagramPacket packet, String responseType,
@@ -454,7 +456,7 @@
try {
response = MdnsResponseDecoder.parseResponse(packet.getData(), packet.getLength());
} catch (MdnsPacket.ParseException e) {
- LOGGER.w(String.format("Error while decoding %s packet (%d): %d",
+ sharedLog.w(String.format("Error while decoding %s packet (%d): %d",
responseType, packetNumber, e.code));
if (callback != null) {
callback.onFailedToParseMdnsResponse(packetNumber, e.code,
@@ -476,8 +478,9 @@
}
@VisibleForTesting
- MdnsSocket createMdnsSocket(int port) throws IOException {
- return new MdnsSocket(new MulticastNetworkInterfaceProvider(context), port);
+ MdnsSocket createMdnsSocket(int port, SharedLog sharedLog) throws IOException {
+ return new MdnsSocket(new MulticastNetworkInterfaceProvider(context, sharedLog), port,
+ sharedLog);
}
private void sendPackets(List<DatagramPacket> packets, MdnsSocket socket) {
@@ -487,7 +490,7 @@
break;
}
try {
- LOGGER.log("Sending a %s mDNS packet...", requestType);
+ sharedLog.log(String.format("Sending a %s mDNS packet...", requestType));
socket.send(packet);
// Start the timer task to monitor the response.
@@ -516,7 +519,7 @@
}
if ((!receivedMulticastResponse)
&& receivedUnicastResponse) {
- LOGGER.e(String.format(
+ sharedLog.e(String.format(
"Haven't received multicast response"
+ " in the last %d ms.",
checkMulticastResponseIntervalMs));
@@ -531,7 +534,7 @@
}
}
} catch (IOException e) {
- LOGGER.e(String.format("Failed to send a %s mDNS packet.", requestType), e);
+ sharedLog.e(String.format("Failed to send a %s mDNS packet.", requestType), e);
}
}
packets.clear();
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index 6925b49..23c5a4d 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -44,7 +44,6 @@
import android.os.Handler;
import android.os.Looper;
import android.util.ArrayMap;
-import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -118,7 +117,7 @@
if (mWifiP2pTetherInterface != null) {
if (newP2pIface != null) {
- Log.wtf(TAG, "Wifi p2p interface is changed from " + mWifiP2pTetherInterface
+ mSharedLog.wtf("Wifi p2p interface is changed from " + mWifiP2pTetherInterface
+ " to " + newP2pIface + " without null broadcast");
}
// Remove the socket.
@@ -133,7 +132,7 @@
if (newP2pIface != null && !socketAlreadyExists) {
// Create a socket for wifi p2p interface.
final int ifaceIndex =
- mDependencies.getNetworkInterfaceIndexByName(newP2pIface);
+ mDependencies.getNetworkInterfaceIndexByName(newP2pIface, mSharedLog);
createSocket(LOCAL_NET, createLPForTetheredInterface(newP2pIface, ifaceIndex));
}
}
@@ -233,21 +232,23 @@
/*** Create a MdnsInterfaceSocket */
public MdnsInterfaceSocket createMdnsInterfaceSocket(
@NonNull NetworkInterface networkInterface, int port, @NonNull Looper looper,
- @NonNull byte[] packetReadBuffer) throws IOException {
- return new MdnsInterfaceSocket(networkInterface, port, looper, packetReadBuffer);
+ @NonNull byte[] packetReadBuffer, @NonNull SharedLog sharedLog) throws IOException {
+ return new MdnsInterfaceSocket(networkInterface, port, looper, packetReadBuffer,
+ sharedLog);
}
/*** Get network interface by given interface name */
- public int getNetworkInterfaceIndexByName(@NonNull final String ifaceName) {
+ public int getNetworkInterfaceIndexByName(@NonNull final String ifaceName,
+ @NonNull SharedLog sharedLog) {
final NetworkInterface iface;
try {
iface = NetworkInterface.getByName(ifaceName);
} catch (SocketException e) {
- Log.e(TAG, "Error querying interface", e);
+ sharedLog.e("Error querying interface", e);
return IFACE_IDX_NOT_EXIST;
}
if (iface == null) {
- Log.e(TAG, "Interface not found: " + ifaceName);
+ sharedLog.e("Interface not found: " + ifaceName);
return IFACE_IDX_NOT_EXIST;
}
return iface.getIndex();
@@ -335,7 +336,7 @@
ensureRunningOnHandlerThread(mHandler);
mRequestStop = false; // Reset stop request flag.
if (mMonitoringSockets) {
- Log.d(TAG, "Already monitoring sockets.");
+ mSharedLog.v("Already monitoring sockets.");
return;
}
mSharedLog.i("Start monitoring sockets.");
@@ -390,7 +391,7 @@
public void requestStopWhenInactive() {
ensureRunningOnHandlerThread(mHandler);
if (!mMonitoringSockets) {
- Log.d(TAG, "Monitoring sockets hasn't been started.");
+ mSharedLog.v("Monitoring sockets hasn't been started.");
return;
}
mRequestStop = true;
@@ -410,7 +411,7 @@
mActiveNetworksLinkProperties.put(network, lp);
if (!matchRequestedNetwork(network)) {
if (DBG) {
- Log.d(TAG, "Ignore LinkProperties change. There is no request for the"
+ mSharedLog.v("Ignore LinkProperties change. There is no request for the"
+ " Network:" + network);
}
return;
@@ -428,7 +429,7 @@
@NonNull final List<LinkAddress> updatedAddresses) {
for (int i = 0; i < mTetherInterfaceSockets.size(); ++i) {
String tetheringInterfaceName = mTetherInterfaceSockets.keyAt(i);
- if (mDependencies.getNetworkInterfaceIndexByName(tetheringInterfaceName)
+ if (mDependencies.getNetworkInterfaceIndexByName(tetheringInterfaceName, mSharedLog)
== ifaceIndex) {
updateSocketInfoAddress(null /* network */,
mTetherInterfaceSockets.valueAt(i), updatedAddresses);
@@ -462,7 +463,7 @@
// tethering are only created if there is a request for all networks (interfaces).
// Therefore, only update the interface list and skip this change if no such request.
if (DBG) {
- Log.d(TAG, "Ignore tether interfaces change. There is no request for all"
+ mSharedLog.v("Ignore tether interfaces change. There is no request for all"
+ " networks.");
}
current.clear();
@@ -482,7 +483,7 @@
continue;
}
- int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(name);
+ int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(name, mSharedLog);
createSocket(LOCAL_NET, createLPForTetheredInterface(name, ifaceIndex));
}
for (String name : interfaceDiff.removed) {
@@ -495,7 +496,7 @@
private void createSocket(NetworkKey networkKey, LinkProperties lp) {
final String interfaceName = lp.getInterfaceName();
if (interfaceName == null) {
- Log.e(TAG, "Can not create socket with null interface name.");
+ mSharedLog.e("Can not create socket with null interface name.");
return;
}
@@ -514,7 +515,7 @@
if (knownTransports != null) {
transports = knownTransports;
} else {
- Log.wtf(TAG, "transports is missing for key: " + networkKey);
+ mSharedLog.wtf("transports is missing for key: " + networkKey);
transports = new int[0];
}
}
@@ -525,7 +526,8 @@
mSharedLog.log("Create socket on net:" + networkKey + ", ifName:" + interfaceName);
final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket(
networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
- mPacketReadBuffer);
+ mPacketReadBuffer, mSharedLog.forSubComponent(
+ MdnsInterfaceSocket.class.getSimpleName() + "/" + interfaceName));
final List<LinkAddress> addresses = lp.getLinkAddresses();
final Network network =
networkKey == LOCAL_NET ? null : ((NetworkAsKey) networkKey).mNetwork;
@@ -637,7 +639,7 @@
final LinkProperties lp = mActiveNetworksLinkProperties.get(network);
if (lp == null) {
// The requested network is not existed. Maybe wait for LinkProperties change later.
- if (DBG) Log.d(TAG, "There is no LinkProperties for this network:" + network);
+ if (DBG) mSharedLog.v("There is no LinkProperties for this network:" + network);
return;
}
createSocket(new NetworkAsKey(network), lp);
@@ -652,7 +654,8 @@
private void retrieveAndNotifySocketFromInterface(String interfaceName, SocketCallback cb) {
final SocketInfo socketInfo = mTetherInterfaceSockets.get(interfaceName);
if (socketInfo == null) {
- int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(interfaceName);
+ int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(interfaceName,
+ mSharedLog);
createSocket(
LOCAL_NET,
createLPForTetheredInterface(interfaceName, ifaceIndex));
diff --git a/service-t/src/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProvider.java b/service-t/src/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProvider.java
index f248c98..da82e96 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProvider.java
@@ -22,7 +22,7 @@
import android.net.Network;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.connectivity.mdns.util.MdnsLogger;
+import com.android.net.module.util.SharedLog;
import java.io.IOException;
import java.net.Inet4Address;
@@ -41,7 +41,7 @@
public class MulticastNetworkInterfaceProvider {
private static final String TAG = "MdnsNIProvider";
- private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
+ private final SharedLog sharedLog;
private static final boolean PREFER_IPV6 = MdnsConfigs.preferIpv6();
private final List<NetworkInterfaceWrapper> multicastNetworkInterfaces = new ArrayList<>();
@@ -51,10 +51,12 @@
private volatile boolean connectivityChanged = true;
@SuppressWarnings("nullness:methodref.receiver.bound")
- public MulticastNetworkInterfaceProvider(@NonNull Context context) {
+ public MulticastNetworkInterfaceProvider(@NonNull Context context,
+ @NonNull SharedLog sharedLog) {
+ this.sharedLog = sharedLog;
// IMPORT CHANGED
this.connectivityMonitor = new ConnectivityMonitorWithConnectivityManager(
- context, this::onConnectivityChanged);
+ context, this::onConnectivityChanged, sharedLog);
}
private synchronized void onConnectivityChanged() {
@@ -83,7 +85,7 @@
connectivityChanged = false;
updateMulticastNetworkInterfaces();
if (multicastNetworkInterfaces.isEmpty()) {
- LOGGER.log("No network interface available for mDNS scanning.");
+ sharedLog.log("No network interface available for mDNS scanning.");
}
}
return new ArrayList<>(multicastNetworkInterfaces);
@@ -93,7 +95,7 @@
multicastNetworkInterfaces.clear();
List<NetworkInterfaceWrapper> networkInterfaceWrappers = getNetworkInterfaces();
for (NetworkInterfaceWrapper interfaceWrapper : networkInterfaceWrappers) {
- if (canScanOnInterface(interfaceWrapper)) {
+ if (canScanOnInterface(interfaceWrapper, sharedLog)) {
multicastNetworkInterfaces.add(interfaceWrapper);
}
}
@@ -133,10 +135,10 @@
}
}
} catch (SocketException e) {
- LOGGER.e("Failed to get network interfaces.", e);
+ sharedLog.e("Failed to get network interfaces.", e);
} catch (NullPointerException e) {
// Android R has a bug that could lead to a NPE. See b/159277702.
- LOGGER.e("Failed to call getNetworkInterfaces API", e);
+ sharedLog.e("Failed to call getNetworkInterfaces API", e);
}
return networkInterfaceWrappers;
@@ -148,7 +150,8 @@
}
/*** Check whether given network interface can support mdns */
- private static boolean canScanOnInterface(@Nullable NetworkInterfaceWrapper networkInterface) {
+ private static boolean canScanOnInterface(@Nullable NetworkInterfaceWrapper networkInterface,
+ @NonNull SharedLog sharedLog) {
try {
if ((networkInterface == null)
|| networkInterface.isLoopback()
@@ -160,7 +163,7 @@
}
return hasInet4Address(networkInterface) || hasInet6Address(networkInterface);
} catch (IOException e) {
- LOGGER.e(String.format("Failed to check interface %s.",
+ sharedLog.e(String.format("Failed to check interface %s.",
networkInterface.getNetworkInterface().getDisplayName()), e);
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
index c21c903..6f16436 100644
--- a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
+++ b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
@@ -20,7 +20,6 @@
import android.net.LinkAddress;
import android.os.Handler;
import android.system.OsConstants;
-import android.util.Log;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.ip.NetlinkMonitor;
@@ -37,6 +36,8 @@
public class SocketNetlinkMonitor extends NetlinkMonitor implements AbstractSocketNetlinkMonitor {
public static final String TAG = SocketNetlinkMonitor.class.getSimpleName();
+ @NonNull
+ private final SharedLog mSharedLog;
@NonNull
private final MdnsSocketProvider.NetLinkMonitorCallBack mCb;
@@ -46,6 +47,7 @@
super(handler, log, TAG, OsConstants.NETLINK_ROUTE,
NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
mCb = cb;
+ mSharedLog = log;
}
@Override
public void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) {
@@ -71,7 +73,7 @@
mCb.deleteInterfaceAddress(ifaddrMsg.index, la);
break;
default:
- Log.e(TAG, "Unknown rtnetlink address msg type " + msg.getHeader().nlmsg_type);
+ mSharedLog.e("Unknown rtnetlink address msg type " + msg.getHeader().nlmsg_type);
}
}
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 8141350..48e86d8 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -325,7 +325,7 @@
protected void unicastInterfaceStateChange(@NonNull IEthernetServiceListener listener,
@NonNull String iface) {
ensureRunningOnEthernetServiceThread();
- final int state = mFactory.getInterfaceState(iface);
+ final int state = getInterfaceState(iface);
final int role = getInterfaceRole(iface);
final IpConfiguration config = getIpConfigurationForCallback(iface, state);
try {
diff --git a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
index ceae9ba..27c0f9f 100644
--- a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
+++ b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
@@ -41,7 +41,7 @@
// This is current path but may be changed soon.
private static final String IFACE_INDEX_NAME_MAP_PATH =
"/sys/fs/bpf/netd_shared/map_netd_iface_index_name_map";
- private final IBpfMap<S32, InterfaceMapValue> mBpfMap;
+ private final IBpfMap<S32, InterfaceMapValue> mIndexToIfaceBpfMap;
private final INetd mNetd;
private final Handler mHandler;
private final Dependencies mDeps;
@@ -53,7 +53,7 @@
@VisibleForTesting
public BpfInterfaceMapUpdater(Context ctx, Handler handler, Dependencies deps) {
mDeps = deps;
- mBpfMap = deps.getInterfaceMap();
+ mIndexToIfaceBpfMap = deps.getInterfaceMap();
mNetd = deps.getINetd(ctx);
mHandler = handler;
}
@@ -91,7 +91,7 @@
*/
public void start() {
mHandler.post(() -> {
- if (mBpfMap == null) {
+ if (mIndexToIfaceBpfMap == null) {
Log.wtf(TAG, "Fail to start: Null bpf map");
return;
}
@@ -126,7 +126,7 @@
}
try {
- mBpfMap.updateEntry(new S32(iface.index), new InterfaceMapValue(ifaceName));
+ mIndexToIfaceBpfMap.updateEntry(new S32(iface.index), new InterfaceMapValue(ifaceName));
} catch (ErrnoException e) {
Log.e(TAG, "Unable to update entry for " + ifaceName + ", " + e);
}
@@ -142,7 +142,7 @@
/** get interface name by interface index from bpf map */
public String getIfNameByIndex(final int index) {
try {
- final InterfaceMapValue value = mBpfMap.getValue(new S32(index));
+ final InterfaceMapValue value = mIndexToIfaceBpfMap.getValue(new S32(index));
if (value == null) {
Log.e(TAG, "No if name entry for index " + index);
return null;
@@ -162,11 +162,12 @@
public void dump(final IndentingPrintWriter pw) {
pw.println("BPF map status:");
pw.increaseIndent();
- BpfDump.dumpMapStatus(mBpfMap, pw, "IfaceIndexNameMap", IFACE_INDEX_NAME_MAP_PATH);
+ BpfDump.dumpMapStatus(mIndexToIfaceBpfMap, pw, "IfaceIndexNameMap",
+ IFACE_INDEX_NAME_MAP_PATH);
pw.decreaseIndent();
pw.println("BPF map content:");
pw.increaseIndent();
- BpfDump.dumpMap(mBpfMap, pw, "IfaceIndexNameMap",
+ BpfDump.dumpMap(mIndexToIfaceBpfMap, pw, "IfaceIndexNameMap",
(key, value) -> "ifaceIndex=" + key.val
+ " ifaceName=" + value.getInterfaceNameString());
pw.decreaseIndent();
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 6635fd3..c46eada 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -63,6 +63,7 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
@@ -3248,7 +3249,8 @@
* Default external settings that read from
* {@link android.provider.Settings.Global}.
*/
- private static class DefaultNetworkStatsSettings implements NetworkStatsSettings {
+ @VisibleForTesting(visibility = PRIVATE)
+ static class DefaultNetworkStatsSettings implements NetworkStatsSettings {
DefaultNetworkStatsSettings() {}
@Override
@@ -3303,6 +3305,7 @@
private static native long nativeGetTotalStat(int type);
private static native long nativeGetIfaceStat(String iface, int type);
+ private static native long nativeGetIfIndexStat(int ifindex, int type);
private static native long nativeGetUidStat(int uid, int type);
/** Initializes and registers the Perfetto Network Trace data source */
diff --git a/service/src/com/android/metrics/stats.proto b/service/src/com/android/metrics/stats.proto
index 006d20a..99afb90 100644
--- a/service/src/com/android/metrics/stats.proto
+++ b/service/src/com/android/metrics/stats.proto
@@ -61,6 +61,9 @@
// Record query service count before unregistered service
optional int32 replied_requests_count = 11;
+
+ // Record sent query count before stopped discovery
+ optional int32 sent_query_count = 12;
}
/**
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 7aff6a4..2842cc3 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -43,7 +43,6 @@
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
-import android.provider.DeviceConfig;
import android.system.ErrnoException;
import android.system.Os;
import android.util.ArraySet;
@@ -95,8 +94,8 @@
private static boolean sInitialized = false;
private static Boolean sEnableJavaBpfMap = null;
- private static final String BPF_NET_MAPS_ENABLE_JAVA_BPF_MAP =
- "bpf_net_maps_enable_java_bpf_map";
+ private static final String BPF_NET_MAPS_FORCE_DISABLE_JAVA_BPF_MAP =
+ "bpf_net_maps_force_disable_java_bpf_map";
// Lock for sConfigurationMap entry for UID_RULES_CONFIGURATION_KEY.
// This entry is not accessed by others.
@@ -283,9 +282,8 @@
if (sInitialized) return;
if (sEnableJavaBpfMap == null) {
sEnableJavaBpfMap = SdkLevel.isAtLeastU() ||
- DeviceConfigUtils.isFeatureEnabled(context,
- DeviceConfig.NAMESPACE_TETHERING, BPF_NET_MAPS_ENABLE_JAVA_BPF_MAP,
- DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultValue */);
+ DeviceConfigUtils.isTetheringFeatureNotChickenedOut(
+ BPF_NET_MAPS_FORCE_DISABLE_JAVA_BPF_MAP);
}
Log.d(TAG, "BpfNetMaps is initialized with sEnableJavaBpfMap=" + sEnableJavaBpfMap);
@@ -1143,7 +1141,7 @@
}
}
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private static native void native_init(boolean startSkDestroyListener);
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index bc703a9..ab5024c 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -1413,10 +1413,10 @@
}
/**
- * @see DeviceConfigUtils#isFeatureEnabled
+ * @see DeviceConfigUtils#isTetheringFeatureEnabled
*/
public boolean isFeatureEnabled(Context context, String name) {
- return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING, name,
+ return DeviceConfigUtils.isTetheringFeatureEnabled(context, NAMESPACE_TETHERING, name,
TETHERING_MODULE_NAME, false /* defaultValue */);
}
@@ -2985,19 +2985,17 @@
}
private void handleFrozenUids(int[] uids, int[] frozenStates) {
- final ArraySet<Range<Integer>> ranges = new ArraySet<>();
+ final ArraySet<Integer> ownerUids = new ArraySet<>();
for (int i = 0; i < uids.length; i++) {
if (frozenStates[i] == UID_FROZEN_STATE_FROZEN) {
- Integer uidAsInteger = Integer.valueOf(uids[i]);
- ranges.add(new Range(uidAsInteger, uidAsInteger));
+ ownerUids.add(uids[i]);
}
}
- if (!ranges.isEmpty()) {
- final Set<Integer> exemptUids = new ArraySet<>();
+ if (!ownerUids.isEmpty()) {
try {
- mDeps.destroyLiveTcpSockets(ranges, exemptUids);
+ mDeps.destroyLiveTcpSocketsByOwnerUids(ownerUids);
} catch (Exception e) {
loge("Exception in socket destroy: " + e);
}
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 2131597..3befcfa 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -20,7 +20,6 @@
import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
import static android.net.SocketKeepalive.SUCCESS;
import static android.net.SocketKeepalive.SUCCESS_PAUSED;
-import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.SOL_SOCKET;
@@ -92,8 +91,8 @@
private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET};
private static final long LOW_TCP_POLLING_INTERVAL_MS = 1_000L;
private static final int ADJUST_TCP_POLLING_DELAY_MS = 2000;
- private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION =
- "automatic_on_off_keepalive_version";
+ private static final String AUTOMATIC_ON_OFF_KEEPALIVE_DISABLE_FLAG =
+ "automatic_on_off_keepalive_disable_flag";
public static final long METRICS_COLLECTION_DURATION_MS = 24 * 60 * 60 * 1_000L;
// ConnectivityService parses message constants from itself and AutomaticOnOffKeepaliveTracker
@@ -219,31 +218,36 @@
// Reading DeviceConfig will check if the calling uid and calling package name are the
// same. Clear calling identity to align the calling uid and package
final boolean enabled = BinderUtils.withCleanCallingIdentity(
- () -> mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
- true /* defaultEnabled */));
+ () -> mDependencies.isTetheringFeatureNotChickenedOut(
+ AUTOMATIC_ON_OFF_KEEPALIVE_DISABLE_FLAG));
if (autoOnOff && enabled) {
mAutomaticOnOffState = STATE_ENABLED;
if (null == ki.mFd) {
throw new IllegalArgumentException("fd can't be null with automatic "
+ "on/off keepalives");
}
- try {
- mFd = Os.dup(ki.mFd);
- } catch (ErrnoException e) {
- Log.e(TAG, "Cannot dup fd: ", e);
- throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
- }
mAlarmListener = () -> mConnectivityServiceHandler.obtainMessage(
CMD_MONITOR_AUTOMATIC_KEEPALIVE, mCallback.asBinder())
.sendToTarget();
} else {
mAutomaticOnOffState = STATE_ALWAYS_ON;
- // A null fd is acceptable in KeepaliveInfo for backward compatibility of
- // PacketKeepalive API, but it must never happen with automatic keepalives.
- // TODO : remove mFd from KeepaliveInfo or from this class.
- mFd = ki.mFd;
mAlarmListener = null;
}
+
+ // A null fd is acceptable in KeepaliveInfo for backward compatibility of
+ // PacketKeepalive API, but it must never happen with automatic keepalives.
+ // TODO : remove mFd from KeepaliveInfo.
+ mFd = dupFd(ki.mFd);
+ }
+
+ private FileDescriptor dupFd(FileDescriptor fd) throws InvalidSocketException {
+ try {
+ if (fd == null) return null;
+ return Os.dup(fd);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot dup fd: ", e);
+ throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+ }
}
@VisibleForTesting
@@ -501,11 +505,7 @@
final AutomaticOnOffKeepalive autoKi;
try {
autoKi = target.withKeepaliveInfo(res.second);
- // Only automatic keepalives duplicate the fd.
- if (target.mAutomaticOnOffState != STATE_ALWAYS_ON) {
- // Close the duplicated fd.
- target.close();
- }
+ target.close();
} catch (InvalidSocketException e) {
Log.wtf(TAG, "Fail to create AutomaticOnOffKeepalive", e);
return;
@@ -699,8 +699,8 @@
// Clear calling identity to align the calling uid and package so that it won't fail if cts
// would like to call dump()
final boolean featureEnabled = BinderUtils.withCleanCallingIdentity(
- () -> mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
- true /* defaultEnabled */));
+ () -> mDependencies.isTetheringFeatureNotChickenedOut(
+ AUTOMATIC_ON_OFF_KEEPALIVE_DISABLE_FLAG));
pw.println("AutomaticOnOff enabled: " + featureEnabled);
pw.increaseIndent();
for (AutomaticOnOffKeepalive autoKi : mAutomaticOnOffKeepalives) {
@@ -968,16 +968,13 @@
}
/**
- * Find out if a feature is enabled from DeviceConfig.
+ * Find out if a feature is not disabled from DeviceConfig.
*
* @param name The name of the property to look up.
- * @param defaultEnabled whether to consider the feature enabled in the absence of
- * the flag. This MUST be a statically-known constant.
* @return whether the feature is enabled
*/
- public boolean isFeatureEnabled(@NonNull final String name, final boolean defaultEnabled) {
- return DeviceConfigUtils.isFeatureEnabled(mContext, NAMESPACE_TETHERING, name,
- DeviceConfigUtils.TETHERING_MODULE_NAME, defaultEnabled);
+ public boolean isTetheringFeatureNotChickenedOut(@NonNull final String name) {
+ return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(name);
}
/**
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index 2ce186f..feba821 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -34,6 +34,8 @@
import static android.net.SocketKeepalive.SUCCESS;
import static android.net.SocketKeepalive.SUCCESS_PAUSED;
+import static com.android.net.module.util.FeatureVersions.FEATURE_CLAT_ADDRESS_TRANSLATE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -59,6 +61,7 @@
import com.android.connectivity.resources.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.HexDump;
import com.android.net.module.util.IpUtils;
@@ -86,6 +89,9 @@
public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
+ private static final String CONFIG_DISABLE_CLAT_ADDRESS_TRANSLATE =
+ "disable_clat_address_translate";
+
/** Keeps track of keepalive requests. */
private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
new HashMap<> ();
@@ -113,7 +119,7 @@
}
@VisibleForTesting
- KeepaliveTracker(Context context, Handler handler, TcpKeepaliveController tcpController,
+ public KeepaliveTracker(Context context, Handler handler, TcpKeepaliveController tcpController,
Dependencies deps) {
mTcpController = tcpController;
mContext = context;
@@ -553,6 +559,8 @@
private KeepaliveInfo handleUpdateKeepaliveForClat(KeepaliveInfo ki)
throws InvalidSocketException, InvalidPacketException {
+ if (!mDependencies.isAddressTranslationEnabled(mContext)) return ki;
+
// Translation applies to only NAT-T keepalive
if (ki.mType != KeepaliveInfo.TYPE_NATT) return ki;
// Only try to translate address if the packet source address is the clat's source address.
@@ -973,5 +981,20 @@
public ConnectivityResources createConnectivityResources(@NonNull Context context) {
return new ConnectivityResources(context);
}
+
+ /**
+ * Return if keepalive address translation with clat feature is supported or not.
+ *
+ * This is controlled by both isFeatureSupported() and isFeatureEnabled(). The
+ * isFeatureSupported() checks whether device contains the minimal required module
+ * version for FEATURE_CLAT_ADDRESS_TRANSLATE. The isTetheringFeatureForceDisabled()
+ * checks the DeviceConfig flag that can be updated via DeviceConfig push to control
+ * the overall feature.
+ */
+ public boolean isAddressTranslationEnabled(@NonNull Context context) {
+ return DeviceConfigUtils.isFeatureSupported(context, FEATURE_CLAT_ADDRESS_TRANSLATE)
+ && DeviceConfigUtils.isTetheringFeatureNotChickenedOut(
+ CONFIG_DISABLE_CLAT_ADDRESS_TRANSLATE);
+ }
}
}
diff --git a/service/src/com/android/server/connectivity/NetworkDiagnostics.java b/service/src/com/android/server/connectivity/NetworkDiagnostics.java
index a367d9d..e1e2585 100644
--- a/service/src/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/service/src/com/android/server/connectivity/NetworkDiagnostics.java
@@ -24,9 +24,12 @@
import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MTU;
+import static com.android.net.module.util.NetworkStackConstants.IP_MTU;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.net.InetAddresses;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -35,6 +38,7 @@
import android.net.TrafficStats;
import android.net.shared.PrivateDnsConfig;
import android.net.util.NetworkConstants;
+import android.os.Build;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
@@ -213,14 +217,10 @@
mLinkProperties.addDnsServer(TEST_DNS6);
}
- final int lpMtu = mLinkProperties.getMtu();
- final int mtu = lpMtu > 0 ? lpMtu : ETHER_MTU;
for (RouteInfo route : mLinkProperties.getRoutes()) {
if (route.getType() == RouteInfo.RTN_UNICAST && route.hasGateway()) {
- InetAddress gateway = route.getGateway();
- // Use mtu in the route if exists. Otherwise, use the one in the link property.
- final int routeMtu = route.getMtu();
- prepareIcmpMeasurements(gateway, (routeMtu > 0) ? routeMtu : mtu);
+ final InetAddress gateway = route.getGateway();
+ prepareIcmpMeasurements(gateway);
if (route.isIPv6Default()) {
prepareExplicitSourceIcmpMeasurements(gateway);
}
@@ -228,7 +228,7 @@
}
for (InetAddress nameserver : mLinkProperties.getDnsServers()) {
- prepareIcmpMeasurements(nameserver, mtu);
+ prepareIcmpMeasurements(nameserver);
prepareDnsMeasurement(nameserver);
// Unlike the DnsResolver which doesn't do certificate validation in opportunistic mode,
@@ -285,24 +285,29 @@
// calculation.
if (addr instanceof Inet6Address) {
return IPV6_HEADER_LEN + ICMP_HEADER_LEN;
+ } else {
+ return IPV4_HEADER_MIN_LEN + ICMP_HEADER_LEN;
}
} catch (UnknownHostException e) {
- Log.e(TAG, "Create InetAddress fail(" + target + "): " + e);
+ throw new AssertionError("Create InetAddress fail(" + target + ")", e);
}
-
- return IPV4_HEADER_MIN_LEN + ICMP_HEADER_LEN;
}
- private void prepareIcmpMeasurements(@NonNull InetAddress target, int targetNetworkMtu) {
+ private void prepareIcmpMeasurements(@NonNull InetAddress target) {
+ int mtu = getMtuForTarget(target);
+ // If getMtuForTarget fails, it doesn't matter what mtu is used because connect can't
+ // succeed anyway
+ if (mtu <= 0) mtu = mLinkProperties.getMtu();
+ if (mtu <= 0) mtu = ETHER_MTU;
// Test with different size payload ICMP.
// 1. Test with 0 payload.
addPayloadIcmpMeasurement(target, 0);
final int header = getHeaderLen(target);
// 2. Test with full size MTU.
- addPayloadIcmpMeasurement(target, targetNetworkMtu - header);
+ addPayloadIcmpMeasurement(target, mtu - header);
// 3. If v6, make another measurement with the full v6 min MTU, unless that's what
// was done above.
- if ((target instanceof Inet6Address) && (targetNetworkMtu != IPV6_MIN_MTU)) {
+ if ((target instanceof Inet6Address) && (mtu != IPV6_MIN_MTU)) {
addPayloadIcmpMeasurement(target, IPV6_MIN_MTU - header);
}
}
@@ -321,6 +326,35 @@
}
}
+ /**
+ * Open a socket to the target address and return the mtu from that socket
+ *
+ * If the MTU can't be obtained for some reason (e.g. the target is unreachable) this will
+ * return -1.
+ *
+ * @param target the destination address
+ * @return the mtu to that destination, or -1
+ */
+ // getsockoptInt is S+, but this service code and only installs on S, so it's safe to ignore
+ // the lint warnings by using @TargetApi.
+ @TargetApi(Build.VERSION_CODES.S)
+ private int getMtuForTarget(InetAddress target) {
+ final int family = target instanceof Inet4Address ? AF_INET : AF_INET6;
+ try {
+ final FileDescriptor socket = Os.socket(family, SOCK_DGRAM, 0);
+ mNetwork.bindSocket(socket);
+ Os.connect(socket, target, 0);
+ if (family == AF_INET) {
+ return Os.getsockoptInt(socket, IPPROTO_IP, IP_MTU);
+ } else {
+ return Os.getsockoptInt(socket, IPPROTO_IPV6, IPV6_MTU);
+ }
+ } catch (ErrnoException | IOException e) {
+ Log.e(TAG, "Can't get MTU for destination " + target, e);
+ return -1;
+ }
+ }
+
private void prepareExplicitSourceIcmpMeasurements(InetAddress target) {
for (LinkAddress l : mLinkProperties.getLinkAddresses()) {
InetAddress source = l.getAddress();
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index c15f042..beaa174 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -1011,9 +1011,8 @@
* @param ranges The updated UID ranges under VPN Lockdown. This function does not treat the VPN
* app's UID in any special way. The caller is responsible for excluding the VPN
* app UID from the passed-in ranges.
- * Ranges can have duplications and/or contain the range that is already subject
- * to lockdown. However, ranges can not have overlaps with other ranges including
- * ranges that are currently subject to lockdown.
+ * Ranges can have duplications, overlaps, and/or contain the range that is
+ * already subject to lockdown.
*/
public synchronized void updateVpnLockdownUidRanges(boolean add, UidRange[] ranges) {
final Set<UidRange> affectedUidRanges = new HashSet<>();
@@ -1045,8 +1044,10 @@
// exclude privileged apps from the prohibit routing rules used to implement outgoing packet
// filtering, privileged apps can still bypass outgoing packet filtering because the
// prohibit rules observe the protected from VPN bit.
+ // If removing a UID, we ensure it is not present anywhere in the set first.
for (final int uid: affectedUids) {
- if (!hasRestrictedNetworksPermission(uid)) {
+ if (!hasRestrictedNetworksPermission(uid)
+ && (add || !UidRange.containsUid(mVpnLockdownUidRanges.getSet(), uid))) {
updateLockdownUidRule(uid, add);
}
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 6b21dac..1c99722 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -29,6 +29,7 @@
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackgroundInternal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -52,9 +53,9 @@
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Bundle;
+import android.os.PowerManager;
import android.os.RemoteCallback;
import android.os.SystemClock;
-import android.os.PowerManager;
import android.provider.DeviceConfig;
import android.service.notification.NotificationListenerService;
import android.util.Log;
@@ -180,6 +181,12 @@
mServiceClient.bind();
mPowerManager = mContext.getSystemService(PowerManager.class);
executeShellCommand("cmd netpolicy start-watching " + mUid);
+ // Some of the test cases assume that Data saver mode is initially disabled, which might not
+ // always be the case. Therefore, explicitly disable it before running the tests.
+ // Invoke setRestrictBackgroundInternal() directly instead of going through
+ // setRestrictBackground(), as some devices do not fully support the Data saver mode but
+ // still have certain parts of it enabled by default.
+ setRestrictBackgroundInternal(false);
setAppIdle(false);
mLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
index deca6a2..8c38b44 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -344,7 +344,7 @@
setRestrictBackgroundInternal(enabled);
}
- private static void setRestrictBackgroundInternal(boolean enabled) {
+ static void setRestrictBackgroundInternal(boolean enabled) {
executeShellCommand("cmd netpolicy set restrict-background " + enabled);
final String output = executeShellCommand("cmd netpolicy get restrict-background");
final String expectedSuffix = enabled ? "enabled" : "disabled";
diff --git a/tests/cts/net/native/dns/Android.bp b/tests/cts/net/native/dns/Android.bp
index 2469710..da4fe28 100644
--- a/tests/cts/net/native/dns/Android.bp
+++ b/tests/cts/net/native/dns/Android.bp
@@ -30,6 +30,7 @@
],
// To be compatible with R devices, the min_sdk_version must be 30.
min_sdk_version: "30",
+ host_required: ["net-tests-utils-host-common"],
}
cc_test {
diff --git a/tests/cts/net/native/dns/AndroidTest.xml b/tests/cts/net/native/dns/AndroidTest.xml
index 6d03c23..d49696b 100644
--- a/tests/cts/net/native/dns/AndroidTest.xml
+++ b/tests/cts/net/native/dns/AndroidTest.xml
@@ -24,6 +24,8 @@
<option name="push" value="CtsNativeNetDnsTestCases->/data/local/tmp/CtsNativeNetDnsTestCases" />
<option name="append-bitness" value="true" />
</target_preparer>
+ <target_preparer class="com.android.testutils.ConnectivityTestTargetPreparer">
+ </target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="CtsNativeNetDnsTestCases" />
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index e978664..bce08df 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2744,7 +2744,6 @@
*/
@AppModeFull(reason = "Instant apps cannot create test networks")
@Test
- @SkipMainlinePresubmit(reason = "Out of SLO flakiness")
public void testSetOemNetworkPreferenceForTestOnlyPref() throws Exception {
// Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
// shims, and @IgnoreUpTo does not check that.
@@ -2757,17 +2756,19 @@
final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+ final Network testNetwork = tnt.getNetwork();
testAndCleanup(() -> {
setOemNetworkPreferenceForMyPackage(
OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY);
registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
- waitForAvailable(defaultCallback, tnt.getNetwork());
+ waitForAvailable(defaultCallback, testNetwork);
systemDefaultCallback.eventuallyExpect(CallbackEntry.AVAILABLE,
NETWORK_CALLBACK_TIMEOUT_MS, cb -> wifiNetwork.equals(cb.getNetwork()));
}, /* cleanup */ () -> {
runWithShellPermissionIdentity(tnt::teardown);
- defaultCallback.expect(CallbackEntry.LOST, tnt, NETWORK_CALLBACK_TIMEOUT_MS);
+ defaultCallback.eventuallyExpect(CallbackEntry.LOST, NETWORK_CALLBACK_TIMEOUT_MS,
+ cb -> testNetwork.equals(cb.getNetwork()));
// This network preference should only ever use the test network therefore available
// should not trigger when the test network goes down (e.g. switch to cellular).
@@ -3205,7 +3206,6 @@
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
- @SkipPresubmit(reason = "Out of SLO flakiness")
public void testMobileDataPreferredUids() throws Exception {
assumeTrue(TestUtils.shouldTestSApis());
final boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 732a42b..76a955b 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -275,16 +275,17 @@
return events.poll(TIMEOUT_MS) ?: fail("Did not receive callback after ${TIMEOUT_MS}ms")
}
- fun eventuallyExpect(expected: CallbackEntry) = events.poll(TIMEOUT_MS) { it == expected }
+ fun eventuallyExpect(expected: CallbackEntry) {
+ val cb = events.poll(TIMEOUT_MS) { it == expected }
+ assertNotNull(cb, "Never received expected $expected. Received: ${events.backtrace()}")
+ }
fun eventuallyExpect(iface: EthernetTestInterface, state: Int, role: Int) {
- val event = createChangeEvent(iface.name, state, role)
- assertNotNull(eventuallyExpect(event), "Never received expected $event")
+ eventuallyExpect(createChangeEvent(iface.name, state, role))
}
fun eventuallyExpect(state: Int) {
- val event = EthernetStateChanged(state)
- assertNotNull(eventuallyExpect(event), "Never received expected $event")
+ eventuallyExpect(EthernetStateChanged(state))
}
fun assertNoCallback() {
@@ -652,9 +653,8 @@
val listener = EthernetStateListener()
addInterfaceStateListener(listener)
- // Note: using eventuallyExpect as there may be other interfaces present.
- listener.eventuallyExpect(InterfaceStateChanged(iface.name,
- STATE_LINK_UP, ROLE_SERVER, /* IpConfiguration */ null))
+ // TODO(b/295146844): do not report IpConfiguration for server mode interfaces.
+ listener.eventuallyExpect(iface, STATE_LINK_UP, ROLE_SERVER)
releaseTetheredInterface()
listener.eventuallyExpect(iface, STATE_LINK_UP, ROLE_CLIENT)
diff --git a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
index 81834a9..805dd65 100644
--- a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
+++ b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
@@ -71,8 +71,6 @@
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.RecorderCallback.CallbackEntry;
-import com.android.testutils.SkipMainlinePresubmit;
-import com.android.testutils.SkipPresubmit;
import com.android.testutils.TestableNetworkCallback;
import org.bouncycastle.x509.X509V1CertificateGenerator;
@@ -642,7 +640,6 @@
}
@Test
- @SkipMainlinePresubmit(reason = "Out of SLO flakiness")
public void testStartStopVpnProfileV4() throws Exception {
doTestStartStopVpnProfile(false /* testIpv6Only */, false /* requiresValidation */,
false /* testSessionKey */, false /* testIkeTunConnParams */);
@@ -656,14 +653,12 @@
}
@Test
- @SkipMainlinePresubmit(reason = "Out of SLO flakiness")
public void testStartStopVpnProfileV6() throws Exception {
doTestStartStopVpnProfile(true /* testIpv6Only */, false /* requiresValidation */,
false /* testSessionKey */, false /* testIkeTunConnParams */);
}
@Test @IgnoreUpTo(SC_V2)
- @SkipPresubmit(reason = "Out of SLO flakiness")
public void testStartStopVpnProfileV6WithValidation() throws Exception {
assumeTrue(TestUtils.shouldTestTApis());
doTestStartStopVpnProfile(true /* testIpv6Only */, true /* requiresValidation */,
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 49620b0..7be4f78 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -16,6 +16,7 @@
package android.net.cts
import android.Manifest.permission.MANAGE_TEST_NETWORKS
+import android.Manifest.permission.NETWORK_SETTINGS
import android.app.compat.CompatChanges
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
@@ -24,6 +25,7 @@
import android.net.LinkProperties
import android.net.LocalSocket
import android.net.LocalSocketAddress
+import android.net.MacAddress
import android.net.Network
import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities
@@ -60,6 +62,8 @@
import android.net.nsd.NsdManager.RegistrationListener
import android.net.nsd.NsdManager.ResolveListener
import android.net.nsd.NsdServiceInfo
+import android.net.nsd.OffloadEngine
+import android.net.nsd.OffloadServiceInfo
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
@@ -70,6 +74,8 @@
import android.system.OsConstants.AF_INET6
import android.system.OsConstants.EADDRNOTAVAIL
import android.system.OsConstants.ENETUNREACH
+import android.system.OsConstants.ETH_P_IPV6
+import android.system.OsConstants.IPPROTO_IPV6
import android.system.OsConstants.IPPROTO_UDP
import android.system.OsConstants.SOCK_DGRAM
import android.util.Log
@@ -79,13 +85,21 @@
import com.android.compatibility.common.util.PropertyUtil
import com.android.modules.utils.build.SdkLevel.isAtLeastU
import com.android.net.module.util.ArrayTrackRecord
+import com.android.net.module.util.DnsPacket
+import com.android.net.module.util.HexDump
+import com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN
+import com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN
+import com.android.net.module.util.NetworkStackConstants.UDP_HEADER_LEN
+import com.android.net.module.util.PacketBuilder
import com.android.net.module.util.TrackRecord
import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.IPv6UdpFilter
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
+import com.android.testutils.TapPacketReader
import com.android.testutils.TestableNetworkAgent
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
import com.android.testutils.TestableNetworkCallback
@@ -100,6 +114,7 @@
import java.net.InetAddress
import java.net.NetworkInterface
import java.net.ServerSocket
+import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import java.util.Random
import java.util.concurrent.Executor
@@ -130,6 +145,8 @@
private const val REGISTRATION_TIMEOUT_MS = 10_000L
private const val DBG = false
private const val TEST_PORT = 12345
+private const val MDNS_PORT = 5353.toShort()
+private val multicastIpv6Addr = parseNumericAddress("ff02::fb") as Inet6Address
@AppModeFull(reason = "Socket cannot bind in instant app mode")
@RunWith(DevSdkIgnoreRunner::class)
@@ -191,8 +208,8 @@
inline fun <reified V : NsdEvent> expectCallback(timeoutMs: Long = TIMEOUT_MS): V {
val nextEvent = nextEvents.poll(timeoutMs)
- assertNotNull(nextEvent, "No callback received after $timeoutMs ms, expected " +
- "${V::class.java.simpleName}")
+ assertNotNull(nextEvent, "No callback received after $timeoutMs ms, " +
+ "expected ${V::class.java.simpleName}")
assertTrue(nextEvent is V, "Expected ${V::class.java.simpleName} but got " +
nextEvent.javaClass.simpleName)
return nextEvent
@@ -353,6 +370,22 @@
}
}
+ private class TestNsdOffloadEngine : OffloadEngine,
+ NsdRecord<TestNsdOffloadEngine.OffloadEvent>() {
+ sealed class OffloadEvent : NsdEvent {
+ data class AddOrUpdateEvent(val info: OffloadServiceInfo) : OffloadEvent()
+ data class RemoveEvent(val info: OffloadServiceInfo) : OffloadEvent()
+ }
+
+ override fun onOffloadServiceUpdated(info: OffloadServiceInfo) {
+ add(OffloadEvent.AddOrUpdateEvent(info))
+ }
+
+ override fun onOffloadServiceRemoved(info: OffloadServiceInfo) {
+ add(OffloadEvent.RemoveEvent(info))
+ }
+ }
+
@Before
fun setUp() {
handlerThread.start()
@@ -392,7 +425,6 @@
val lp = LinkProperties().apply {
interfaceName = ifaceName
}
-
val agent = TestableNetworkAgent(context, handlerThread.looper,
NetworkCapabilities().apply {
removeCapability(NET_CAPABILITY_TRUSTED)
@@ -858,6 +890,56 @@
}
}
+ fun checkOffloadServiceInfo(serviceInfo: OffloadServiceInfo) {
+ assertEquals(serviceName, serviceInfo.key.serviceName)
+ assertEquals(serviceType, serviceInfo.key.serviceType)
+ assertEquals(listOf<String>("_subtype"), serviceInfo.subtypes)
+ assertTrue(serviceInfo.hostname.startsWith("Android_"))
+ assertTrue(serviceInfo.hostname.endsWith("local"))
+ assertEquals(0, serviceInfo.priority)
+ assertEquals(OffloadEngine.OFFLOAD_TYPE_REPLY.toLong(), serviceInfo.offloadType)
+ }
+
+ @Test
+ fun testNsdManager_registerOffloadEngine() {
+ val targetSdkVersion = context.packageManager
+ .getTargetSdkVersion(context.applicationInfo.packageName)
+ // The offload callbacks are only supported with the new backend,
+ // enabled with target SDK U+.
+ assumeTrue(isAtLeastU() || targetSdkVersion > Build.VERSION_CODES.TIRAMISU)
+ val offloadEngine = TestNsdOffloadEngine()
+ runAsShell(NETWORK_SETTINGS) {
+ nsdManager.registerOffloadEngine(testNetwork1.iface.interfaceName,
+ OffloadEngine.OFFLOAD_TYPE_REPLY.toLong(),
+ OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK.toLong(),
+ { it.run() }, offloadEngine)
+ }
+
+ val si = NsdServiceInfo()
+ si.serviceType = "$serviceType,_subtype"
+ si.serviceName = serviceName
+ si.network = testNetwork1.network
+ si.port = 12345
+ val record = NsdRegistrationRecord()
+ nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, record)
+ val addOrUpdateEvent = offloadEngine
+ .expectCallbackEventually<TestNsdOffloadEngine.OffloadEvent.AddOrUpdateEvent> {
+ it.info.key.serviceName == serviceName
+ }
+ checkOffloadServiceInfo(addOrUpdateEvent.info)
+
+ nsdManager.unregisterService(record)
+ val unregisterEvent = offloadEngine
+ .expectCallbackEventually<TestNsdOffloadEngine.OffloadEvent.RemoveEvent> {
+ it.info.key.serviceName == serviceName
+ }
+ checkOffloadServiceInfo(unregisterEvent.info)
+
+ runAsShell(NETWORK_SETTINGS) {
+ nsdManager.unregisterOffloadEngine(offloadEngine)
+ }
+ }
+
private fun checkConnectSocketToMdnsd(shouldFail: Boolean) {
val discoveryRecord = NsdDiscoveryRecord()
val localSocket = LocalSocket()
@@ -1075,6 +1157,176 @@
}
}
+ @Test
+ fun testRegisterWithConflictDuringProbing() {
+ // This test requires shims supporting T+ APIs (NsdServiceInfo.network)
+ assumeTrue(TestUtils.shouldTestTApis())
+
+ val si = NsdServiceInfo()
+ si.serviceType = serviceType
+ si.serviceName = serviceName
+ si.network = testNetwork1.network
+ si.port = 12345 // Test won't try to connect so port does not matter
+
+ val packetReader = TapPacketReader(Handler(handlerThread.looper),
+ testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
+ packetReader.startAsyncForTest()
+ handlerThread.waitForIdle(TIMEOUT_MS)
+
+ // Register service on testNetwork1
+ val registrationRecord = NsdRegistrationRecord()
+ nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, { it.run() },
+ registrationRecord)
+
+ tryTest {
+ assertNotNull(packetReader.pollForProbe(serviceName, serviceType),
+ "Did not find a probe for the service")
+ packetReader.sendResponse(buildConflictingAnnouncement())
+
+ // Registration must use an updated name to avoid the conflict
+ val cb = registrationRecord.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
+ cb.serviceInfo.serviceName.let {
+ assertTrue("Unexpected registered name: $it",
+ it.startsWith(serviceName) && it != serviceName)
+ }
+ } cleanupStep {
+ nsdManager.unregisterService(registrationRecord)
+ registrationRecord.expectCallback<ServiceUnregistered>()
+ } cleanup {
+ packetReader.handler.post { packetReader.stop() }
+ handlerThread.waitForIdle(TIMEOUT_MS)
+ }
+ }
+
+ @Test
+ fun testRegisterWithConflictAfterProbing() {
+ // This test requires shims supporting T+ APIs (NsdServiceInfo.network)
+ assumeTrue(TestUtils.shouldTestTApis())
+
+ val si = NsdServiceInfo()
+ si.serviceType = serviceType
+ si.serviceName = serviceName
+ si.network = testNetwork1.network
+ si.port = 12345 // Test won't try to connect so port does not matter
+
+ // Register service on testNetwork1
+ val registrationRecord = NsdRegistrationRecord()
+ val discoveryRecord = NsdDiscoveryRecord()
+ val registeredService = registerService(registrationRecord, si)
+ val packetReader = TapPacketReader(Handler(handlerThread.looper),
+ testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
+ packetReader.startAsyncForTest()
+ handlerThread.waitForIdle(TIMEOUT_MS)
+
+ tryTest {
+ assertNotNull(packetReader.pollForAdvertisement(serviceName, serviceType),
+ "No announcements sent after initial probing")
+
+ assertEquals(si.serviceName, registeredService.serviceName)
+
+ nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
+ testNetwork1.network, { it.run() }, discoveryRecord)
+ discoveryRecord.waitForServiceDiscovered(si.serviceName, serviceType)
+
+ // Send a conflicting announcement
+ val conflictingAnnouncement = buildConflictingAnnouncement()
+ packetReader.sendResponse(conflictingAnnouncement)
+
+ // Expect to see probes (RFC6762 9., service is reset to probing state)
+ assertNotNull(packetReader.pollForProbe(serviceName, serviceType),
+ "Probe not received within timeout after conflict")
+
+ // Send the conflicting packet again to reply to the probe
+ packetReader.sendResponse(conflictingAnnouncement)
+
+ // Note the legacy mdnsresponder would send an exit announcement here (a 0-lifetime
+ // advertisement just for the PTR record), but not the new advertiser. This probably
+ // follows RFC 6762 8.4, saying that when a record rdata changed, "In the case of shared
+ // records, a host MUST send a "goodbye" announcement with RR TTL zero [...] for the old
+ // rdata, to cause it to be deleted from peer caches, before announcing the new rdata".
+ //
+ // This should be implemented by the new advertiser, but in the case of conflicts it is
+ // not very valuable since an identical PTR record would be used by the conflicting
+ // service (except for subtypes). In that case the exit announcement may be
+ // counter-productive as it conflicts with announcements done by the conflicting
+ // service.
+
+ // Note that before sending the following ServiceRegistered callback for the renamed
+ // service, the legacy mdnsresponder-based implementation would first send a
+ // Service*Registered* callback for the original service name being *unregistered*; it
+ // should have been a ServiceUnregistered callback instead (bug in NsdService
+ // interpretation of the callback).
+ val newRegistration = registrationRecord.expectCallbackEventually<ServiceRegistered>(
+ REGISTRATION_TIMEOUT_MS) {
+ it.serviceInfo.serviceName.startsWith(serviceName) &&
+ it.serviceInfo.serviceName != serviceName
+ }
+
+ discoveryRecord.expectCallbackEventually<ServiceFound> {
+ it.serviceInfo.serviceName == newRegistration.serviceInfo.serviceName
+ }
+ } cleanupStep {
+ nsdManager.stopServiceDiscovery(discoveryRecord)
+ discoveryRecord.expectCallback<DiscoveryStopped>()
+ } cleanupStep {
+ nsdManager.unregisterService(registrationRecord)
+ registrationRecord.expectCallback<ServiceUnregistered>()
+ } cleanup {
+ packetReader.handler.post { packetReader.stop() }
+ handlerThread.waitForIdle(TIMEOUT_MS)
+ }
+ }
+
+ private fun buildConflictingAnnouncement(): ByteBuffer {
+ /*
+ Generated with:
+ scapy.raw(scapy.DNS(rd=0, qr=1, aa=1, qd = None, an =
+ scapy.DNSRRSRV(rrname='NsdTest123456789._nmt123456789._tcp.local',
+ rclass=0x8001, port=31234, target='conflict.local', ttl=120)
+ )).hex()
+ */
+ val mdnsPayload = HexDump.hexStringToByteArray("000084000000000100000000104e736454657" +
+ "3743132333435363738390d5f6e6d74313233343536373839045f746370056c6f63616c00002" +
+ "18001000000780016000000007a0208636f6e666c696374056c6f63616c00")
+ val packetBuffer = ByteBuffer.wrap(mdnsPayload)
+ // Replace service name and types in the packet with the random ones used in the test.
+ // Test service name and types have consistent length and are always ASCII
+ val testPacketName = "NsdTest123456789".encodeToByteArray()
+ val testPacketTypePrefix = "_nmt123456789".encodeToByteArray()
+ val encodedServiceName = serviceName.encodeToByteArray()
+ val encodedTypePrefix = serviceType.split('.')[0].encodeToByteArray()
+ assertEquals(testPacketName.size, encodedServiceName.size)
+ assertEquals(testPacketTypePrefix.size, encodedTypePrefix.size)
+ packetBuffer.position(mdnsPayload.indexOf(testPacketName))
+ packetBuffer.put(encodedServiceName)
+ packetBuffer.position(mdnsPayload.indexOf(testPacketTypePrefix))
+ packetBuffer.put(encodedTypePrefix)
+
+ return buildMdnsPacket(mdnsPayload)
+ }
+
+ private fun buildMdnsPacket(mdnsPayload: ByteArray): ByteBuffer {
+ val packetBuffer = PacketBuilder.allocate(true /* hasEther */, IPPROTO_IPV6,
+ IPPROTO_UDP, mdnsPayload.size)
+ val packetBuilder = PacketBuilder(packetBuffer)
+ // Multicast ethernet address for IPv6 to ff02::fb
+ val multicastEthAddr = MacAddress.fromBytes(
+ byteArrayOf(0x33, 0x33, 0, 0, 0, 0xfb.toByte()))
+ packetBuilder.writeL2Header(
+ MacAddress.fromBytes(byteArrayOf(1, 2, 3, 4, 5, 6)) /* srcMac */,
+ multicastEthAddr,
+ ETH_P_IPV6.toShort())
+ packetBuilder.writeIpv6Header(
+ 0x60000000, // version=6, traffic class=0x0, flowlabel=0x0
+ IPPROTO_UDP.toByte(),
+ 64 /* hop limit */,
+ parseNumericAddress("2001:db8::123") as Inet6Address /* srcIp */,
+ multicastIpv6Addr /* dstIp */)
+ packetBuilder.writeUdpHeader(MDNS_PORT /* srcPort */, MDNS_PORT /* dstPort */)
+ packetBuffer.put(mdnsPayload)
+ return packetBuilder.finalizePacket()
+ }
+
/**
* Register a service and return its registration record.
*/
@@ -1100,7 +1352,65 @@
}
}
+private fun TapPacketReader.pollForMdnsPacket(
+ timeoutMs: Long = REGISTRATION_TIMEOUT_MS,
+ predicate: (TestDnsPacket) -> Boolean
+): ByteArray? {
+ val mdnsProbeFilter = IPv6UdpFilter(srcPort = MDNS_PORT, dstPort = MDNS_PORT).and {
+ val mdnsPayload = it.copyOfRange(
+ ETHER_HEADER_LEN + IPV6_HEADER_LEN + UDP_HEADER_LEN, it.size)
+ try {
+ predicate(TestDnsPacket(mdnsPayload))
+ } catch (e: DnsPacket.ParseException) {
+ false
+ }
+ }
+ return poll(timeoutMs, mdnsProbeFilter)
+}
+
+private fun TapPacketReader.pollForProbe(
+ serviceName: String,
+ serviceType: String,
+ timeoutMs: Long = REGISTRATION_TIMEOUT_MS
+): ByteArray? = pollForMdnsPacket(timeoutMs) { it.isProbeFor("$serviceName.$serviceType.local") }
+
+private fun TapPacketReader.pollForAdvertisement(
+ serviceName: String,
+ serviceType: String,
+ timeoutMs: Long = REGISTRATION_TIMEOUT_MS
+): ByteArray? = pollForMdnsPacket(timeoutMs) { it.isReplyFor("$serviceName.$serviceType.local") }
+
+private class TestDnsPacket(data: ByteArray) : DnsPacket(data) {
+ fun isProbeFor(name: String): Boolean = mRecords[QDSECTION].any {
+ it.dName == name && it.nsType == 0xff /* ANY */
+ }
+
+ fun isReplyFor(name: String): Boolean = mRecords[ANSECTION].any {
+ it.dName == name && it.nsType == 0x21 /* SRV */
+ }
+}
+
private fun ByteArray?.utf8ToString(): String {
if (this == null) return ""
return String(this, StandardCharsets.UTF_8)
}
+
+private fun ByteArray.indexOf(sub: ByteArray): Int {
+ var subIndex = 0
+ forEachIndexed { i, b ->
+ when (b) {
+ // Still matching: continue comparing with next byte
+ sub[subIndex] -> {
+ subIndex++
+ if (subIndex == sub.size) {
+ return i - sub.size + 1
+ }
+ }
+ // Not matching next byte but matches first byte: continue comparing with 2nd byte
+ sub[0] -> subIndex = 1
+ // No matches: continue comparing from first byte
+ else -> subIndex = 0
+ }
+ }
+ return -1
+}
diff --git a/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java b/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java
index 0eb5644..1b22f42 100644
--- a/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java
+++ b/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java
@@ -95,14 +95,17 @@
testIface.getFileDescriptor().close();
}
- if (tunNetworkCallback != null) {
- sCm.unregisterNetworkCallback(tunNetworkCallback);
- }
final Network testNetwork = tunNetworkCallback.currentNetwork;
if (testNetwork != null) {
tnm.teardownTestNetwork(testNetwork);
}
+ // Ensure test network being torn down.
+ tunNetworkCallback.waitForLost();
+
+ if (tunNetworkCallback != null) {
+ sCm.unregisterNetworkCallback(tunNetworkCallback);
+ }
}
}
diff --git a/tests/mts/bpf_existence_test.cpp b/tests/mts/bpf_existence_test.cpp
index c294e7b..442d69f 100644
--- a/tests/mts/bpf_existence_test.cpp
+++ b/tests/mts/bpf_existence_test.cpp
@@ -33,6 +33,8 @@
using android::modules::sdklevel::IsAtLeastR;
using android::modules::sdklevel::IsAtLeastS;
using android::modules::sdklevel::IsAtLeastT;
+using android::modules::sdklevel::IsAtLeastU;
+using android::modules::sdklevel::IsAtLeastV;
#define PLATFORM "/sys/fs/bpf/"
#define TETHERING "/sys/fs/bpf/tethering/"
@@ -147,10 +149,14 @@
// so we should only test for the removal of stuff that was mainline'd,
// and for the presence of mainline stuff.
+ // Note: Q is no longer supported by mainline
+ ASSERT_TRUE(IsAtLeastR());
+
// R can potentially run on pre-4.9 kernel non-eBPF capable devices.
DO_EXPECT(IsAtLeastR() && !IsAtLeastS() && isAtLeastKernelVersion(4, 9, 0), PLATFORM_ONLY_IN_R);
// S requires Linux Kernel 4.9+ and thus requires eBPF support.
+ if (IsAtLeastS()) ASSERT_TRUE(isAtLeastKernelVersion(4, 9, 0));
DO_EXPECT(IsAtLeastS(), MAINLINE_FOR_S_PLUS);
DO_EXPECT(IsAtLeastS() && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_S_5_10_PLUS);
@@ -163,6 +169,10 @@
DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 15, 0), MAINLINE_FOR_T_5_15_PLUS);
// U requires Linux Kernel 4.14+, but nothing (as yet) added or removed in U.
+ if (IsAtLeastU()) ASSERT_TRUE(isAtLeastKernelVersion(4, 14, 0));
+
+ // V requires Linux Kernel 4.19+, but nothing (as yet) added or removed in V.
+ if (IsAtLeastV()) ASSERT_TRUE(isAtLeastKernelVersion(4, 19, 0));
for (const auto& file : mustExist) {
EXPECT_EQ(0, access(file.c_str(), R_OK)) << file << " does not exist";
diff --git a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
index 961c422..97aa575 100644
--- a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
+++ b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
@@ -94,4 +94,131 @@
assertEquals(durationMs, it.eventDurationMillisec)
}
}
+
+ @Test
+ fun testReportServiceDiscoveryStarted() {
+ val clientId = 99
+ val transactionId = 100
+ val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
+ metrics.reportServiceDiscoveryStarted(transactionId)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertTrue(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_DISCOVER, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_DISCOVERY_STARTED, it.queryResult)
+ }
+ }
+
+ @Test
+ fun testReportServiceDiscoveryFailed() {
+ val clientId = 99
+ val transactionId = 100
+ val durationMs = 10L
+ val metrics = NetworkNsdReportedMetrics(false /* isLegacy */, clientId, deps)
+ metrics.reportServiceDiscoveryFailed(transactionId, durationMs)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertFalse(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_DISCOVER, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_DISCOVERY_FAILED, it.queryResult)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ }
+ }
+
+ @Test
+ fun testReportServiceDiscoveryStop() {
+ val clientId = 99
+ val transactionId = 100
+ val durationMs = 10L
+ val foundCallbackCount = 100
+ val lostCallbackCount = 49
+ val servicesCount = 75
+ val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
+ metrics.reportServiceDiscoveryStop(
+ transactionId, durationMs, foundCallbackCount, lostCallbackCount, servicesCount)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertTrue(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_DISCOVER, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_DISCOVERY_STOP, it.queryResult)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ assertEquals(foundCallbackCount, it.foundCallbackCount)
+ assertEquals(lostCallbackCount, it.lostCallbackCount)
+ assertEquals(servicesCount, it.foundServiceCount)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ }
+ }
+
+ @Test
+ fun testReportServiceResolved() {
+ val clientId = 99
+ val transactionId = 100
+ val durationMs = 10L
+ val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
+ metrics.reportServiceResolved(transactionId, durationMs, true /* isServiceFromCache */)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertTrue(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_RESOLVE, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_RESOLVED, it.queryResult)
+ assertTrue(it.isKnownService)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ }
+ }
+
+ @Test
+ fun testReportServiceResolutionFailed() {
+ val clientId = 99
+ val transactionId = 100
+ val durationMs = 10L
+ val metrics = NetworkNsdReportedMetrics(false /* isLegacy */, clientId, deps)
+ metrics.reportServiceResolutionFailed(transactionId, durationMs)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertFalse(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_RESOLVE, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_RESOLUTION_FAILED, it.queryResult)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ }
+ }
+
+ @Test
+ fun testReportServiceResolutionStop() {
+ val clientId = 99
+ val transactionId = 100
+ val durationMs = 10L
+ val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
+ metrics.reportServiceResolutionStop(transactionId, durationMs)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertTrue(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_RESOLVE, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_RESOLUTION_STOP, it.queryResult)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ }
+ }
}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 4c2cd59..708697c 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -402,6 +402,7 @@
import com.android.server.connectivity.ClatCoordinator;
import com.android.server.connectivity.ConnectivityFlags;
import com.android.server.connectivity.ConnectivityResources;
+import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.MultinetworkPolicyTracker;
import com.android.server.connectivity.MultinetworkPolicyTrackerTestDependencies;
import com.android.server.connectivity.Nat464Xlat;
@@ -410,6 +411,7 @@
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
+import com.android.server.connectivity.TcpKeepaliveController;
import com.android.server.connectivity.UidRangeUtils;
import com.android.server.connectivity.Vpn;
import com.android.server.connectivity.VpnProfileStore;
@@ -627,6 +629,7 @@
@Mock ActivityManager mActivityManager;
@Mock DestroySocketsWrapper mDestroySocketsWrapper;
@Mock SubscriptionManager mSubscriptionManager;
+ @Mock KeepaliveTracker.Dependencies mMockKeepaliveTrackerDependencies;
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
// underlying binder calls.
@@ -1898,6 +1901,12 @@
doReturn(mResources).when(mockResContext).getResources();
ConnectivityResources.setResourcesContextForTest(mockResContext);
mDeps = new ConnectivityServiceDependencies(mockResContext);
+ doReturn(true).when(mMockKeepaliveTrackerDependencies)
+ .isAddressTranslationEnabled(mServiceContext);
+ doReturn(new ConnectivityResources(mockResContext)).when(mMockKeepaliveTrackerDependencies)
+ .createConnectivityResources(mServiceContext);
+ doReturn(new int[] {1, 3, 0, 0}).when(mMockKeepaliveTrackerDependencies)
+ .getSupportedKeepalives(mServiceContext);
mAutoOnOffKeepaliveDependencies =
new AutomaticOnOffKeepaliveTrackerDependencies(mServiceContext);
mService = new ConnectivityService(mServiceContext,
@@ -2292,11 +2301,17 @@
}
@Override
- public boolean isFeatureEnabled(@NonNull final String name, final boolean defaultEnabled) {
+ public boolean isTetheringFeatureNotChickenedOut(@NonNull final String name) {
// Tests for enabling the feature are verified in AutomaticOnOffKeepaliveTrackerTest.
// Assuming enabled here to focus on ConnectivityService tests.
return true;
}
+ public KeepaliveTracker newKeepaliveTracker(@NonNull Context context,
+ @NonNull Handler connectivityserviceHander) {
+ return new KeepaliveTracker(context, connectivityserviceHander,
+ new TcpKeepaliveController(connectivityserviceHander),
+ mMockKeepaliveTrackerDependencies);
+ }
}
private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) {
@@ -9038,6 +9053,18 @@
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
+ // Lingering timer is short and cell might be disconnected if the device is particularly
+ // slow running the test, unless it's requested. Make sure the networks the test needs
+ // are all requested.
+ final NetworkCallback cellCallback = new NetworkCallback() {};
+ final NetworkCallback wifiCallback = new NetworkCallback() {};
+ mCm.requestNetwork(
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build(),
+ cellCallback);
+ mCm.requestNetwork(
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(),
+ wifiCallback);
+
mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* privateDnsProbeSent */);
assertUidRangesUpdatedForMyUid(true);
@@ -9194,6 +9221,8 @@
assertDefaultNetworkCapabilities(userId /* no networks */);
mMockVpn.disconnect();
+ mCm.unregisterNetworkCallback(cellCallback);
+ mCm.unregisterNetworkCallback(wifiCallback);
}
@Test
@@ -18518,12 +18547,7 @@
waitForIdle();
- final Set<Integer> exemptUids = new ArraySet();
- final UidRange frozenUidRange = new UidRange(TEST_FROZEN_UID, TEST_FROZEN_UID);
- final Set<UidRange> ranges = Collections.singleton(frozenUidRange);
-
- verify(mDestroySocketsWrapper).destroyLiveTcpSockets(eq(UidRange.toIntRanges(ranges)),
- eq(exemptUids));
+ verify(mDestroySocketsWrapper).destroyLiveTcpSocketsByOwnerUids(Set.of(TEST_FROZEN_UID));
}
@Test
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 55384b3..dbd4e4e 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -31,6 +31,8 @@
import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING;
import static com.android.server.NsdService.DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF;
+import static com.android.server.NsdService.MdnsListener;
+import static com.android.server.NsdService.NO_TRANSACTION;
import static com.android.server.NsdService.parseTypeAndSubtype;
import static com.android.testutils.ContextUtils.mockService;
@@ -45,7 +47,6 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.any;
@@ -235,8 +236,8 @@
@After
public void tearDown() throws Exception {
if (mThread != null) {
- mThread.quit();
- mThread = null;
+ mThread.quitSafely();
+ mThread.join();
}
}
@@ -401,9 +402,11 @@
// NsdManager uses a separate HandlerThread to dispatch callbacks (on ServiceHandler), so
// this needs to use a timeout
verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
+ final int discId = discIdCaptor.getValue();
+ verify(mMetrics).reportServiceDiscoveryStarted(discId);
final DiscoveryInfo discoveryInfo = new DiscoveryInfo(
- discIdCaptor.getValue(),
+ discId,
IMDnsEventListener.SERVICE_FOUND,
SERVICE_NAME,
SERVICE_TYPE,
@@ -454,19 +457,24 @@
eq(interfaceIdx));
final String serviceAddress = "192.0.2.123";
+ final int getAddrId = getAddrIdCaptor.getValue();
final GetAddressInfo addressInfo = new GetAddressInfo(
- getAddrIdCaptor.getValue(),
+ getAddrId,
IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS,
SERVICE_FULL_NAME,
serviceAddress,
interfaceIdx,
INetd.LOCAL_NET_ID);
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
eventListener.onGettingServiceAddressStatus(addressInfo);
waitForIdle();
final ArgumentCaptor<NsdServiceInfo> resInfoCaptor =
ArgumentCaptor.forClass(NsdServiceInfo.class);
verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(resInfoCaptor.capture());
+ verify(mMetrics).reportServiceResolved(
+ getAddrId, 10L /* durationMs */, false /* isServiceFromCache */);
+
final NsdServiceInfo resolvedService = resInfoCaptor.getValue();
assertEquals(SERVICE_NAME, resolvedService.getServiceName());
assertEquals("." + SERVICE_TYPE, resolvedService.getServiceType());
@@ -491,9 +499,11 @@
// NsdManager uses a separate HandlerThread to dispatch callbacks (on ServiceHandler), so
// this needs to use a timeout
verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
+ final int discId = discIdCaptor.getValue();
+ verify(mMetrics).reportServiceDiscoveryStarted(discId);
final DiscoveryInfo discoveryInfo = new DiscoveryInfo(
- discIdCaptor.getValue(),
+ discId,
IMDnsEventListener.SERVICE_FOUND,
SERVICE_NAME,
SERVICE_TYPE,
@@ -570,19 +580,23 @@
final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class);
verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE), eq(IFACE_IDX_ANY));
verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
+ final int discId = discIdCaptor.getValue();
+ verify(mMetrics).reportServiceDiscoveryStarted(discId);
// Fail to discover service.
final DiscoveryInfo discoveryFailedInfo = new DiscoveryInfo(
- discIdCaptor.getValue(),
+ discId,
IMDnsEventListener.SERVICE_DISCOVERY_FAILED,
null /* serviceName */,
null /* registrationType */,
null /* domainName */,
IFACE_IDX_ANY,
0 /* netId */);
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
eventListener.onServiceDiscoveryStatus(discoveryFailedInfo);
verify(discListener, timeout(TIMEOUT_MS))
.onStartDiscoveryFailed(SERVICE_TYPE, FAILURE_INTERNAL_ERROR);
+ verify(mMetrics).reportServiceDiscoveryFailed(discId, 10L /* durationMs */);
}
@Test
@@ -600,8 +614,9 @@
eq("local.") /* domain */, eq(IFACE_IDX_ANY));
// Fail to resolve service.
+ final int resolvId = resolvIdCaptor.getValue();
final ResolutionInfo resolutionFailedInfo = new ResolutionInfo(
- resolvIdCaptor.getValue(),
+ resolvId,
IMDnsEventListener.SERVICE_RESOLUTION_FAILED,
null /* serviceName */,
null /* serviceType */,
@@ -611,9 +626,11 @@
0 /* port */,
new byte[0] /* txtRecord */,
IFACE_IDX_ANY);
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
eventListener.onServiceResolutionStatus(resolutionFailedInfo);
verify(resolveListener, timeout(TIMEOUT_MS))
.onResolveFailed(any(), eq(FAILURE_INTERNAL_ERROR));
+ verify(mMetrics).reportServiceResolutionFailed(resolvId, 10L /* durationMs */);
}
@Test
@@ -651,16 +668,19 @@
eq(IFACE_IDX_ANY));
// Fail to get service address.
+ final int getAddrId = getAddrIdCaptor.getValue();
final GetAddressInfo gettingAddrFailedInfo = new GetAddressInfo(
- getAddrIdCaptor.getValue(),
+ getAddrId,
IMDnsEventListener.SERVICE_GET_ADDR_FAILED,
null /* hostname */,
null /* address */,
IFACE_IDX_ANY,
0 /* netId */);
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
eventListener.onGettingServiceAddressStatus(gettingAddrFailedInfo);
verify(resolveListener, timeout(TIMEOUT_MS))
.onResolveFailed(any(), eq(FAILURE_INTERNAL_ERROR));
+ verify(mMetrics).reportServiceResolutionFailed(getAddrId, 10L /* durationMs */);
}
@Test
@@ -697,6 +717,7 @@
eq("local.") /* domain */, eq(IFACE_IDX_ANY));
final int resolveId = resolvIdCaptor.getValue();
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
client.stopServiceResolution(resolveListener);
waitForIdle();
@@ -704,6 +725,7 @@
verify(resolveListener, timeout(TIMEOUT_MS)).onResolutionStopped(argThat(ns ->
request.getServiceName().equals(ns.getServiceName())
&& request.getServiceType().equals(ns.getServiceType())));
+ verify(mMetrics).reportServiceResolutionStop(resolveId, 10L /* durationMs */);
}
@Test
@@ -766,6 +788,7 @@
eq(IFACE_IDX_ANY));
final int getAddrId = getAddrIdCaptor.getValue();
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
client.stopServiceResolution(resolveListener);
waitForIdle();
@@ -773,6 +796,7 @@
verify(resolveListener, timeout(TIMEOUT_MS)).onResolutionStopped(argThat(ns ->
request.getServiceName().equals(ns.getServiceName())
&& request.getServiceType().equals(ns.getServiceType())));
+ verify(mMetrics).reportServiceResolutionStop(getAddrId, 10L /* durationMs */);
}
private void verifyUpdatedServiceInfo(NsdServiceInfo info, String serviceName,
@@ -819,7 +843,7 @@
network);
// Verify onServiceFound callback
- listener.onServiceFound(mdnsServiceInfo);
+ listener.onServiceFound(mdnsServiceInfo, false /* isServiceFromCache */);
final ArgumentCaptor<NsdServiceInfo> updateInfoCaptor =
ArgumentCaptor.forClass(NsdServiceInfo.class);
verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(1))
@@ -936,8 +960,8 @@
final Network network = new Network(999);
final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
// Verify the discovery start / stop.
- final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor =
- ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
+ final ArgumentCaptor<MdnsListener> listenerCaptor =
+ ArgumentCaptor.forClass(MdnsListener.class);
client.discoverServices(SERVICE_TYPE, PROTOCOL, network, r -> r.run(), discListener);
waitForIdle();
verify(mSocketProvider).startMonitoringSockets();
@@ -945,7 +969,10 @@
listenerCaptor.capture(), argThat(options -> network.equals(options.getNetwork())));
verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
- final MdnsServiceBrowserListener listener = listenerCaptor.getValue();
+ final MdnsListener listener = listenerCaptor.getValue();
+ final int discId = listener.mTransactionId;
+ verify(mMetrics).reportServiceDiscoveryStarted(discId);
+
final MdnsServiceInfo foundInfo = new MdnsServiceInfo(
SERVICE_NAME, /* serviceInstanceName */
serviceTypeWithLocalDomain.split("\\."), /* serviceType */
@@ -960,7 +987,7 @@
network);
// Verify onServiceNameDiscovered callback
- listener.onServiceNameDiscovered(foundInfo);
+ listener.onServiceNameDiscovered(foundInfo, false /* isServiceFromCache */);
verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(argThat(info ->
info.getServiceName().equals(SERVICE_NAME)
// Service type in discovery callbacks has a dot at the end
@@ -987,11 +1014,14 @@
&& info.getServiceType().equals(SERVICE_TYPE + ".")
&& info.getNetwork().equals(network)));
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
client.stopServiceDiscovery(discListener);
waitForIdle();
verify(mDiscoveryManager).unregisterListener(eq(serviceTypeWithLocalDomain), any());
verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStopped(SERVICE_TYPE);
verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive();
+ verify(mMetrics).reportServiceDiscoveryStop(discId, 10L /* durationMs */,
+ 1 /* foundCallbackCount */, 1 /* lostCallbackCount */, 1 /* servicesCount */);
}
@Test
@@ -1007,6 +1037,8 @@
waitForIdle();
verify(discListener, timeout(TIMEOUT_MS))
.onStartDiscoveryFailed(invalidServiceType, FAILURE_INTERNAL_ERROR);
+ verify(mMetrics, times(1))
+ .reportServiceDiscoveryFailed(NO_TRANSACTION, 0L /* durationMs */);
final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
client.discoverServices(
@@ -1014,6 +1046,8 @@
waitForIdle();
verify(discListener, timeout(TIMEOUT_MS))
.onStartDiscoveryFailed(serviceTypeWithLocalDomain, FAILURE_INTERNAL_ERROR);
+ verify(mMetrics, times(2))
+ .reportServiceDiscoveryFailed(NO_TRANSACTION, 0L /* durationMs */);
final String serviceTypeWithoutTcpOrUdpEnding = "_test._com";
client.discoverServices(
@@ -1021,6 +1055,8 @@
waitForIdle();
verify(discListener, timeout(TIMEOUT_MS))
.onStartDiscoveryFailed(serviceTypeWithoutTcpOrUdpEnding, FAILURE_INTERNAL_ERROR);
+ verify(mMetrics, times(3))
+ .reportServiceDiscoveryFailed(NO_TRANSACTION, 0L /* durationMs */);
}
@Test
@@ -1060,8 +1096,8 @@
final Network network = new Network(999);
final String serviceType = "_nsd._service._tcp";
final String constructedServiceType = "_service._tcp.local";
- final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor =
- ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
+ final ArgumentCaptor<MdnsListener> listenerCaptor =
+ ArgumentCaptor.forClass(MdnsListener.class);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, serviceType);
request.setNetwork(network);
client.resolveService(request, resolveListener);
@@ -1076,7 +1112,7 @@
// Subtypes are not used for resolution, only for discovery
assertEquals(Collections.emptyList(), optionsCaptor.getValue().getSubtypes());
- final MdnsServiceBrowserListener listener = listenerCaptor.getValue();
+ final MdnsListener listener = listenerCaptor.getValue();
final MdnsServiceInfo mdnsServiceInfo = new MdnsServiceInfo(
SERVICE_NAME,
constructedServiceType.split("\\."),
@@ -1092,10 +1128,14 @@
network);
// Verify onServiceFound callback
- listener.onServiceFound(mdnsServiceInfo);
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
+ listener.onServiceFound(mdnsServiceInfo, true /* isServiceFromCache */);
final ArgumentCaptor<NsdServiceInfo> infoCaptor =
ArgumentCaptor.forClass(NsdServiceInfo.class);
verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(infoCaptor.capture());
+ verify(mMetrics).reportServiceResolved(
+ listener.mTransactionId, 10 /* durationMs */, true /* isServiceFromCache */);
+
final NsdServiceInfo info = infoCaptor.getValue();
assertEquals(SERVICE_NAME, info.getServiceName());
assertEquals("._service._tcp", info.getServiceType());
@@ -1271,7 +1311,7 @@
verify(regListener, timeout(TIMEOUT_MS)).onRegistrationFailed(
argThat(info -> matches(info, regInfo)), eq(FAILURE_INTERNAL_ERROR));
- verify(mMetrics).reportServiceRegistrationFailed(anyInt(), anyLong());
+ verify(mMetrics).reportServiceRegistrationFailed(NO_TRANSACTION, 0L /* durationMs */);
}
@Test
@@ -1319,8 +1359,8 @@
final Network network = new Network(999);
final String serviceType = "_nsd._service._tcp";
final String constructedServiceType = "_service._tcp.local";
- final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor =
- ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
+ final ArgumentCaptor<MdnsListener> listenerCaptor =
+ ArgumentCaptor.forClass(MdnsListener.class);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, serviceType);
request.setNetwork(network);
client.resolveService(request, resolveListener);
@@ -1335,16 +1375,19 @@
// Subtypes are not used for resolution, only for discovery
assertEquals(Collections.emptyList(), optionsCaptor.getValue().getSubtypes());
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
client.stopServiceResolution(resolveListener);
waitForIdle();
// Verify the listener has been unregistered.
+ final MdnsListener listener = listenerCaptor.getValue();
verify(mDiscoveryManager, timeout(TIMEOUT_MS))
- .unregisterListener(eq(constructedServiceType), eq(listenerCaptor.getValue()));
+ .unregisterListener(eq(constructedServiceType), eq(listener));
verify(resolveListener, timeout(TIMEOUT_MS)).onResolutionStopped(argThat(ns ->
request.getServiceName().equals(ns.getServiceName())
&& request.getServiceType().equals(ns.getServiceType())));
verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive();
+ verify(mMetrics).reportServiceResolutionStop(listener.mTransactionId, 10L /* durationMs */);
}
@Test
@@ -1585,6 +1628,20 @@
lockOrder.verify(mMulticastLock).release();
}
+ @Test
+ public void testNullINsdManagerCallback() {
+ final NsdService service = new NsdService(mContext, mHandler, CLEANUP_DELAY_MS, mDeps) {
+ @Override
+ public INsdServiceConnector connect(INsdManagerCallback baseCb,
+ boolean runNewMdnsBackend) {
+ // Pass null INsdManagerCallback
+ return super.connect(null /* cb */, runNewMdnsBackend);
+ }
+ };
+
+ assertThrows(IllegalArgumentException.class, () -> new NsdManager(mContext, service));
+ }
+
private void waitForIdle() {
HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
}
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index f4d3915..986c389 100644
--- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -32,7 +32,6 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.longThat;
@@ -78,7 +77,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.connectivity.resources.R;
import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker.AutomaticOnOffKeepalive;
import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo;
import com.android.testutils.DevSdkIgnoreRule;
@@ -96,6 +94,7 @@
import org.mockito.MockitoAnnotations;
import java.io.FileDescriptor;
+import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
@@ -128,7 +127,7 @@
@Mock AlarmManager mAlarmManager;
@Mock NetworkAgentInfo mNai;
@Mock SubscriptionManager mSubscriptionManager;
-
+ @Mock KeepaliveTracker.Dependencies mKeepaliveTrackerDeps;
KeepaliveStatsTracker mKeepaliveStatsTracker;
TestKeepaliveTracker mKeepaliveTracker;
AOOTestHandler mTestHandler;
@@ -267,7 +266,7 @@
TestKeepaliveTracker(@NonNull final Context context, @NonNull final Handler handler,
@NonNull final TcpKeepaliveController tcpController) {
- super(context, handler, tcpController, new Dependencies());
+ super(context, handler, tcpController, mKeepaliveTrackerDeps);
}
public void setReturnedKeepaliveInfo(@NonNull final KeepaliveInfo ki) {
@@ -336,8 +335,6 @@
anyInt() /* pid */, anyInt() /* uid */);
ConnectivityResources.setResourcesContextForTest(mCtx);
final Resources mockResources = mock(Resources.class);
- doReturn(new String[] { "0,3", "3,3" }).when(mockResources)
- .getStringArray(R.array.config_networkSupportedKeepaliveCount);
doReturn(mockResources).when(mCtx).getResources();
doReturn(mNetd).when(mDependencies).getNetd();
doReturn(mAlarmManager).when(mDependencies).getAlarmManager(any());
@@ -345,6 +342,10 @@
.getFwmarkForNetwork(TEST_NETID);
doNothing().when(mDependencies).sendRequest(any(), any());
+ doReturn(true).when(mKeepaliveTrackerDeps).isAddressTranslationEnabled(mCtx);
+ doReturn(new ConnectivityResources(mCtx)).when(mKeepaliveTrackerDeps)
+ .createConnectivityResources(mCtx);
+ doReturn(new int[] {3, 0, 0, 3}).when(mKeepaliveTrackerDeps).getSupportedKeepalives(mCtx);
mHandlerThread = new HandlerThread("KeepaliveTrackerTest");
mHandlerThread.start();
@@ -357,7 +358,7 @@
.when(mDependencies)
.newKeepaliveStatsTracker(mCtx, mTestHandler);
- doReturn(true).when(mDependencies).isFeatureEnabled(any(), anyBoolean());
+ doReturn(true).when(mDependencies).isTetheringFeatureNotChickenedOut(any());
doReturn(0L).when(mDependencies).getElapsedRealtime();
mAOOKeepaliveTracker =
new AutomaticOnOffKeepaliveTracker(mCtx, mTestHandler, mDependencies);
@@ -366,6 +367,10 @@
@After
public void teardown() throws Exception {
TestKeepaliveInfo.closeAllSockets();
+ if (mHandlerThread != null) {
+ mHandlerThread.quitSafely();
+ mHandlerThread.join();
+ }
}
private final class AOOTestHandler extends Handler {
@@ -659,6 +664,25 @@
}
@Test
+ public void testStartNattKeepalive_addressTranslationOnClatNotSupported() throws Exception {
+ // Disable address translation feature and verify the behavior
+ doReturn(false).when(mKeepaliveTrackerDeps).isAddressTranslationEnabled(mCtx);
+
+ setupTestNaiForClat(InetAddresses.parseNumericAddress("2001:db8::1"),
+ InetAddresses.parseNumericAddress("2001:db8::2"));
+
+ doStartNattKeepalive();
+ final ArgumentCaptor<NattKeepalivePacketData> kpdCaptor =
+ ArgumentCaptor.forClass(NattKeepalivePacketData.class);
+ verify(mNai).onStartNattSocketKeepalive(
+ eq(TEST_SLOT), eq(TEST_KEEPALIVE_INTERVAL_SEC), kpdCaptor.capture());
+ // Verify that address translation is not triggered so the addresses are still v4.
+ final NattKeepalivePacketData kpd = kpdCaptor.getValue();
+ assertTrue(kpd.getSrcAddress() instanceof Inet4Address);
+ assertTrue(kpd.getDstAddress() instanceof Inet4Address);
+ }
+
+ @Test
public void testStartNattKeepalive_addressTranslationOnClat() throws Exception {
final InetAddress v6AddrSrc = InetAddresses.parseNumericAddress("2001:db8::1");
final InetAddress v6AddrDst = InetAddresses.parseNumericAddress("2001:db8::2");
diff --git a/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
index fa703eb..90a0edd 100644
--- a/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
@@ -67,6 +67,7 @@
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -240,6 +241,14 @@
HandlerUtils.waitForIdle(mTestHandler, TIMEOUT_MS);
}
+ @After
+ public void tearDown() throws Exception {
+ if (mHandlerThread != null) {
+ mHandlerThread.quitSafely();
+ mHandlerThread.join();
+ }
+ }
+
private void setElapsedRealtime(long time) {
doReturn(time).when(mDependencies).getElapsedRealtime();
}
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index cf02e3a..5bde31a 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -55,6 +55,7 @@
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.intThat;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
@@ -99,6 +100,7 @@
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -126,12 +128,14 @@
private static final int MOCK_APPID1 = 10001;
private static final int MOCK_APPID2 = 10086;
private static final int MOCK_APPID3 = 10110;
+ private static final int MOCK_APPID4 = 10111;
private static final int SYSTEM_APPID1 = 1100;
private static final int SYSTEM_APPID2 = 1108;
private static final int VPN_APPID = 10002;
private static final int MOCK_UID11 = MOCK_USER1.getUid(MOCK_APPID1);
private static final int MOCK_UID12 = MOCK_USER1.getUid(MOCK_APPID2);
private static final int MOCK_UID13 = MOCK_USER1.getUid(MOCK_APPID3);
+ private static final int MOCK_UID14 = MOCK_USER1.getUid(MOCK_APPID4);
private static final int SYSTEM_APP_UID11 = MOCK_USER1.getUid(SYSTEM_APPID1);
private static final int VPN_UID = MOCK_USER1.getUid(VPN_APPID);
private static final int MOCK_UID21 = MOCK_USER2.getUid(MOCK_APPID1);
@@ -211,6 +215,14 @@
onUserAdded(MOCK_USER1);
}
+ @After
+ public void tearDown() throws Exception {
+ if (mHandlerThread != null) {
+ mHandlerThread.quitSafely();
+ mHandlerThread.join();
+ }
+ }
+
private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion,
String packageName, int uid, String... permissions) {
final PackageInfo packageInfo =
@@ -965,6 +977,66 @@
}
@Test
+ public void testLockdownUidFilteringWithLockdownEnableDisableWithMultiAddAndOverlap() {
+ doReturn(List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+ buildPackageInfo(MOCK_PACKAGE1, MOCK_UID13),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID14),
+ buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
+ startMonitoring();
+ // MOCK_UID13 is subject to the VPN.
+ final UidRange range1 = new UidRange(MOCK_UID13, MOCK_UID13);
+ final UidRange[] lockdownRange1 = {range1};
+
+ // Add Lockdown uid range at 1st time, expect a rule to be set up
+ mPermissionMonitor.updateVpnLockdownUidRanges(true /* add */, lockdownRange1);
+ verify(mBpfNetMaps).updateUidLockdownRule(anyInt(), eq(true) /* add */);
+ verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID13, true /* add */);
+
+ reset(mBpfNetMaps);
+
+ // MOCK_UID13 and MOCK_UID14 are sequential and subject to the VPN in a separate range.
+ final UidRange range2 = new UidRange(MOCK_UID13, MOCK_UID14);
+ final UidRange[] lockdownRange2 = {range2};
+
+ // Add overlapping multiple-UID range. Rule may be set again, which is functionally
+ // a no-op, so it is fine.
+ mPermissionMonitor.updateVpnLockdownUidRanges(true /* add */, lockdownRange2);
+ verify(mBpfNetMaps, atLeast(1)).updateUidLockdownRule(anyInt(), eq(true) /* add */);
+ verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID14, true /* add */);
+
+ reset(mBpfNetMaps);
+
+ // Remove the multiple-UID range. UID from first rule should not be removed.
+ mPermissionMonitor.updateVpnLockdownUidRanges(false /* false */, lockdownRange2);
+ verify(mBpfNetMaps, times(1)).updateUidLockdownRule(anyInt(), eq(false) /* add */);
+ verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID14, false /* add */);
+
+ reset(mBpfNetMaps);
+
+ // Add the multiple-UID range back again to be able to test removing the first range, too.
+ mPermissionMonitor.updateVpnLockdownUidRanges(true /* add */, lockdownRange2);
+ verify(mBpfNetMaps, atLeast(1)).updateUidLockdownRule(anyInt(), eq(true) /* add */);
+ verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID14, true /* add */);
+
+ reset(mBpfNetMaps);
+
+ // Remove the single-UID range. The rule for MOCK_UID11 should not change because it is
+ // still covered by the second, multiple-UID range rule.
+ mPermissionMonitor.updateVpnLockdownUidRanges(false /* false */, lockdownRange1);
+ verify(mBpfNetMaps, never()).updateUidLockdownRule(anyInt(), anyBoolean());
+
+ reset(mBpfNetMaps);
+
+ // Remove the multiple-UID range. Expect both UID rules to be torn down.
+ mPermissionMonitor.updateVpnLockdownUidRanges(false /* false */, lockdownRange2);
+ verify(mBpfNetMaps, times(2)).updateUidLockdownRule(anyInt(), eq(false) /* add */);
+ verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID13, false /* add */);
+ verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID14, false /* add */);
+ }
+
+ @Test
public void testLockdownUidFilteringWithLockdownEnableDisableWithDuplicates() {
doReturn(List.of(
buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index ad178c0..385f831 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -182,7 +182,6 @@
import com.android.server.VpnTestBase;
import com.android.server.vcn.util.PersistableBundleUtils;
import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.SkipPresubmit;
import org.junit.Before;
import org.junit.Rule;
@@ -1712,7 +1711,6 @@
errorCode);
}
- @SkipPresubmit(reason = "Out of SLO flakiness")
@Test
public void testStartPlatformVpnFailedWithRecoverableError() throws Exception {
final IkeProtocolException exception = mock(IkeProtocolException.class);
@@ -1722,7 +1720,6 @@
VpnManager.CATEGORY_EVENT_IKE_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE, errorCode);
}
- @SkipPresubmit(reason = "Out of SLO flakiness")
@Test
public void testStartPlatformVpnFailedWithUnknownHostException() throws Exception {
final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
@@ -1734,7 +1731,6 @@
errorCode);
}
- @SkipPresubmit(reason = "Out of SLO flakiness")
@Test
public void testStartPlatformVpnFailedWithIkeTimeoutException() throws Exception {
final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
@@ -1756,7 +1752,6 @@
VpnManager.ERROR_CODE_NETWORK_LOST);
}
- @SkipPresubmit(reason = "Out of SLO flakiness")
@Test
public void testStartPlatformVpnFailedWithIOException() throws Exception {
final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
@@ -2389,7 +2384,6 @@
true /* areLongLivedTcpConnectionsExpensive */);
}
- @SkipPresubmit(reason = "Out of SLO flakiness")
@Test
public void testPreferredIpProtocolFromCarrierConfig_v4UDP() throws Exception {
doTestReadCarrierConfig(createTestCellNc(),
@@ -2402,7 +2396,6 @@
false /* areLongLivedTcpConnectionsExpensive */);
}
- @SkipPresubmit(reason = "Out of SLO flakiness")
@Test
public void testPreferredIpProtocolFromCarrierConfig_v6ESP() throws Exception {
doTestReadCarrierConfig(createTestCellNc(),
@@ -2415,7 +2408,6 @@
false /* areLongLivedTcpConnectionsExpensive */);
}
- @SkipPresubmit(reason = "Out of SLO flakiness")
@Test
public void testPreferredIpProtocolFromCarrierConfig_v6UDP() throws Exception {
doTestReadCarrierConfig(createTestCellNc(),
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/ConnectivityMonitorWithConnectivityManagerTests.java b/tests/unit/java/com/android/server/connectivity/mdns/ConnectivityMonitorWithConnectivityManagerTests.java
index 8fb7be1..bb59e0d 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/ConnectivityMonitorWithConnectivityManagerTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/ConnectivityMonitorWithConnectivityManagerTests.java
@@ -31,6 +31,7 @@
import android.net.Network;
import android.net.NetworkRequest;
+import com.android.net.module.util.SharedLog;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -49,6 +50,7 @@
@Mock private Context mContext;
@Mock private ConnectivityMonitor.Listener mockListener;
@Mock private ConnectivityManager mConnectivityManager;
+ @Mock private SharedLog sharedLog;
private ConnectivityMonitorWithConnectivityManager monitor;
@@ -57,7 +59,7 @@
MockitoAnnotations.initMocks(this);
doReturn(mConnectivityManager).when(mContext)
.getSystemService(Context.CONNECTIVITY_SERVICE);
- monitor = new ConnectivityMonitorWithConnectivityManager(mContext, mockListener);
+ monitor = new ConnectivityMonitorWithConnectivityManager(mContext, mockListener, sharedLog);
}
@Test
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
index 6a0334f..9b38fea 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
@@ -20,6 +20,8 @@
import android.net.LinkAddress
import android.net.Network
import android.net.nsd.NsdServiceInfo
+import android.net.nsd.OffloadEngine
+import android.net.nsd.OffloadServiceInfo
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
@@ -60,6 +62,8 @@
private val TEST_SOCKETKEY_2 = SocketKey(1002 /* interfaceIndex */)
private val TEST_HOSTNAME = arrayOf("Android_test", "local")
private const val TEST_SUBTYPE = "_subtype"
+private val TEST_INTERFACE1 = "test_iface1"
+private val TEST_INTERFACE2 = "test_iface2"
private val SERVICE_1 = NsdServiceInfo("TestServiceName", "_advertisertest._tcp").apply {
port = 12345
@@ -94,6 +98,24 @@
network = null
}
+private val OFFLOAD_SERVICEINFO = OffloadServiceInfo(
+ OffloadServiceInfo.Key("TestServiceName", "_advertisertest._tcp"),
+ listOf(TEST_SUBTYPE),
+ "Android_test.local",
+ null, /* rawOffloadPacket */
+ 0, /* priority */
+ OffloadEngine.OFFLOAD_TYPE_REPLY.toLong()
+)
+
+private val OFFLOAD_SERVICEINFO_NO_SUBTYPE = OffloadServiceInfo(
+ OffloadServiceInfo.Key("TestServiceName", "_advertisertest._tcp"),
+ listOf(),
+ "Android_test.local",
+ null, /* rawOffloadPacket */
+ 0, /* priority */
+ OffloadEngine.OFFLOAD_TYPE_REPLY.toLong()
+)
+
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
class MdnsAdvertiserTest {
@@ -123,6 +145,8 @@
doReturn(true).`when`(mockInterfaceAdvertiser2).isProbing(anyInt())
doReturn(createEmptyNetworkInterface()).`when`(mockSocket1).getInterface()
doReturn(createEmptyNetworkInterface()).`when`(mockSocket2).getInterface()
+ doReturn(TEST_INTERFACE1).`when`(mockInterfaceAdvertiser1).socketInterfaceName
+ doReturn(TEST_INTERFACE2).`when`(mockInterfaceAdvertiser2).socketInterfaceName
}
@After
@@ -160,12 +184,15 @@
)
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
- postSync { intAdvCbCaptor.value.onRegisterServiceSucceeded(
+ postSync { intAdvCbCaptor.value.onServiceProbingSucceeded(
mockInterfaceAdvertiser1, SERVICE_ID_1) }
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), argThat { it.matches(SERVICE_1) })
+ verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO_NO_SUBTYPE))
postSync { socketCb.onInterfaceDestroyed(TEST_SOCKETKEY_1, mockSocket1) }
verify(mockInterfaceAdvertiser1).destroyNow()
+ postSync { intAdvCbCaptor.value.onDestroyed(mockSocket1) }
+ verify(cb).onOffloadStop(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO_NO_SUBTYPE))
}
@Test
@@ -195,14 +222,16 @@
anyInt(), eq(ALL_NETWORKS_SERVICE), eq(TEST_SUBTYPE))
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
- postSync { intAdvCbCaptor1.value.onRegisterServiceSucceeded(
+ postSync { intAdvCbCaptor1.value.onServiceProbingSucceeded(
mockInterfaceAdvertiser1, SERVICE_ID_1) }
+ verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO))
// Need both advertisers to finish probing and call onRegisterServiceSucceeded
verify(cb, never()).onRegisterServiceSucceeded(anyInt(), any())
doReturn(false).`when`(mockInterfaceAdvertiser2).isProbing(SERVICE_ID_1)
- postSync { intAdvCbCaptor2.value.onRegisterServiceSucceeded(
+ postSync { intAdvCbCaptor2.value.onServiceProbingSucceeded(
mockInterfaceAdvertiser2, SERVICE_ID_1) }
+ verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE2), eq(OFFLOAD_SERVICEINFO))
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1),
argThat { it.matches(ALL_NETWORKS_SERVICE) })
@@ -210,6 +239,8 @@
postSync { advertiser.removeService(SERVICE_ID_1) }
verify(mockInterfaceAdvertiser1).removeService(SERVICE_ID_1)
verify(mockInterfaceAdvertiser2).removeService(SERVICE_ID_1)
+ verify(cb).onOffloadStop(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO))
+ verify(cb).onOffloadStop(eq(TEST_INTERFACE2), eq(OFFLOAD_SERVICEINFO))
// Interface advertisers call onDestroyed after sending exit announcements
postSync { intAdvCbCaptor1.value.onDestroyed(mockSocket1) }
@@ -285,12 +316,12 @@
argThat { it.matches(expectedCaseInsensitiveRenamed) }, eq(null))
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
- postSync { intAdvCbCaptor.value.onRegisterServiceSucceeded(
+ postSync { intAdvCbCaptor.value.onServiceProbingSucceeded(
mockInterfaceAdvertiser1, SERVICE_ID_1) }
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), argThat { it.matches(SERVICE_1) })
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_2)
- postSync { intAdvCbCaptor.value.onRegisterServiceSucceeded(
+ postSync { intAdvCbCaptor.value.onServiceProbingSucceeded(
mockInterfaceAdvertiser1, SERVICE_ID_2) }
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_2),
argThat { it.matches(expectedRenamed) })
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
index 7c6cb3e..12faa50 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
@@ -21,6 +21,7 @@
import android.os.HandlerThread
import android.os.SystemClock
import com.android.internal.util.HexDump
+import com.android.net.module.util.SharedLog
import com.android.server.connectivity.mdns.MdnsAnnouncer.AnnouncementInfo
import com.android.server.connectivity.mdns.MdnsAnnouncer.BaseAnnouncementInfo
import com.android.server.connectivity.mdns.MdnsRecordRepository.getReverseDnsAddress
@@ -52,6 +53,7 @@
private val thread = HandlerThread(MdnsAnnouncerTest::class.simpleName)
private val socket = mock(MdnsInterfaceSocket::class.java)
+ private val sharedLog = mock(SharedLog::class.java)
private val buffer = ByteArray(1500)
@Before
@@ -80,11 +82,11 @@
@Test
fun testAnnounce() {
- val replySender = MdnsReplySender("testiface", thread.looper, socket, buffer)
+ val replySender = MdnsReplySender( thread.looper, socket, buffer, sharedLog)
@Suppress("UNCHECKED_CAST")
val cb = mock(MdnsPacketRepeater.PacketRepeaterCallback::class.java)
as MdnsPacketRepeater.PacketRepeaterCallback<BaseAnnouncementInfo>
- val announcer = MdnsAnnouncer("testiface", thread.looper, replySender, cb)
+ val announcer = MdnsAnnouncer(thread.looper, replySender, cb, sharedLog)
/*
The expected packet replicates records announced when registering a service, as observed in
the legacy mDNS implementation (some ordering differs to be more readable).
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
index dd458b8..c19747e 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
@@ -108,9 +108,9 @@
doReturn(repository).`when`(deps).makeRecordRepository(any(),
eq(TEST_HOSTNAME)
)
- doReturn(replySender).`when`(deps).makeReplySender(anyString(), any(), any(), any())
- doReturn(announcer).`when`(deps).makeMdnsAnnouncer(anyString(), any(), any(), any())
- doReturn(prober).`when`(deps).makeMdnsProber(anyString(), any(), any(), any())
+ doReturn(replySender).`when`(deps).makeReplySender(anyString(), any(), any(), any(), any())
+ doReturn(announcer).`when`(deps).makeMdnsAnnouncer(anyString(), any(), any(), any(), any())
+ doReturn(prober).`when`(deps).makeMdnsProber(anyString(), any(), any(), any(), any())
val knownServices = mutableSetOf<Int>()
doAnswer { inv ->
@@ -132,8 +132,8 @@
advertiser.start()
verify(socket).addPacketHandler(packetHandlerCaptor.capture())
- verify(deps).makeMdnsProber(any(), any(), any(), probeCbCaptor.capture())
- verify(deps).makeMdnsAnnouncer(any(), any(), any(), announceCbCaptor.capture())
+ verify(deps).makeMdnsProber(any(), any(), any(), probeCbCaptor.capture(), any())
+ verify(deps).makeMdnsAnnouncer(any(), any(), any(), announceCbCaptor.capture(), any())
}
@After
@@ -150,7 +150,7 @@
0L /* initialDelayMs */)
thread.waitForIdle(TIMEOUT_MS)
- verify(cb).onRegisterServiceSucceeded(advertiser, TEST_SERVICE_ID_1)
+ verify(cb).onServiceProbingSucceeded(advertiser, TEST_SERVICE_ID_1)
// Remove the service: expect exit announcements
val testExitInfo = mock(ExitAnnouncementInfo::class.java)
@@ -256,7 +256,7 @@
val mockProbingInfo = mock(ProbingInfo::class.java)
doReturn(mockProbingInfo).`when`(repository).setServiceProbing(TEST_SERVICE_ID_1)
- advertiser.restartProbingForConflict(TEST_SERVICE_ID_1)
+ advertiser.maybeRestartProbingForConflict(TEST_SERVICE_ID_1)
verify(prober).restartForConflict(mockProbingInfo)
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
index 29de272..3701b0c 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
@@ -36,6 +36,7 @@
import android.os.HandlerThread;
import com.android.net.module.util.HexDump;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.MdnsSocketClientBase.SocketCreationCallback;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -66,6 +67,7 @@
@Mock private MdnsServiceBrowserListener mListener;
@Mock private MdnsSocketClientBase.Callback mCallback;
@Mock private SocketCreationCallback mSocketCreationCallback;
+ @Mock private SharedLog mSharedLog;
private MdnsMultinetworkSocketClient mSocketClient;
private Handler mHandler;
private SocketKey mSocketKey;
@@ -78,7 +80,7 @@
thread.start();
mHandler = new Handler(thread.getLooper());
mSocketKey = new SocketKey(1000 /* interfaceIndex */);
- mSocketClient = new MdnsMultinetworkSocketClient(thread.getLooper(), mProvider);
+ mSocketClient = new MdnsMultinetworkSocketClient(thread.getLooper(), mProvider, mSharedLog);
mHandler.post(() -> mSocketClient.setCallback(mCallback));
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
index 0a8d78d..5ca4dd6 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
@@ -21,6 +21,7 @@
import android.os.HandlerThread
import android.os.Looper
import com.android.internal.util.HexDump
+import com.android.net.module.util.SharedLog
import com.android.server.connectivity.mdns.MdnsProber.ProbingInfo
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
@@ -55,6 +56,7 @@
class MdnsProberTest {
private val thread = HandlerThread(MdnsProberTest::class.simpleName)
private val socket = mock(MdnsInterfaceSocket::class.java)
+ private val sharedLog = mock(SharedLog::class.java)
@Suppress("UNCHECKED_CAST")
private val cb = mock(MdnsPacketRepeater.PacketRepeaterCallback::class.java)
as MdnsPacketRepeater.PacketRepeaterCallback<ProbingInfo>
@@ -82,8 +84,9 @@
private class TestProber(
looper: Looper,
replySender: MdnsReplySender,
- cb: PacketRepeaterCallback<ProbingInfo>
- ) : MdnsProber("testiface", looper, replySender, cb) {
+ cb: PacketRepeaterCallback<ProbingInfo>,
+ sharedLog: SharedLog
+ ) : MdnsProber(looper, replySender, cb, sharedLog) {
override fun getInitialDelay() = 0L
}
@@ -116,8 +119,8 @@
@Test
fun testProbe() {
- val replySender = MdnsReplySender("testiface", thread.looper, socket, buffer)
- val prober = TestProber(thread.looper, replySender, cb)
+ val replySender = MdnsReplySender(thread.looper, socket, buffer, sharedLog)
+ val prober = TestProber(thread.looper, replySender, cb, sharedLog)
val probeInfo = TestProbeInfo(
listOf(makeServiceRecord(TEST_SERVICE_NAME_1, 37890)))
prober.startProbing(probeInfo)
@@ -140,8 +143,8 @@
@Test
fun testProbeMultipleRecords() {
- val replySender = MdnsReplySender("testiface", thread.looper, socket, buffer)
- val prober = TestProber(thread.looper, replySender, cb)
+ val replySender = MdnsReplySender(thread.looper, socket, buffer, sharedLog)
+ val prober = TestProber(thread.looper, replySender, cb, sharedLog)
val probeInfo = TestProbeInfo(listOf(
makeServiceRecord(TEST_SERVICE_NAME_1, 37890),
makeServiceRecord(TEST_SERVICE_NAME_2, 37891),
@@ -178,8 +181,8 @@
@Test
fun testStopProbing() {
- val replySender = MdnsReplySender("testiface", thread.looper, socket, buffer)
- val prober = TestProber(thread.looper, replySender, cb)
+ val replySender = MdnsReplySender(thread.looper, socket, buffer, sharedLog)
+ val prober = TestProber(thread.looper, replySender, cb, sharedLog)
val probeInfo = TestProbeInfo(
listOf(makeServiceRecord(TEST_SERVICE_NAME_1, 37890)),
// delayMs is the delay between each probe, so does not apply to the first one
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
index 1fdfe09..fde5abd 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
@@ -645,14 +646,14 @@
SERVICE_TYPE_LABELS,
Collections.emptyMap(), TEST_TTL), socketKey);
- verify(mockListenerOne).onServiceNameDiscovered(any());
- verify(mockListenerOne).onServiceFound(any());
+ verify(mockListenerOne).onServiceNameDiscovered(any(), eq(false) /* isServiceFromCache */);
+ verify(mockListenerOne).onServiceFound(any(), eq(false) /* isServiceFromCache */);
// File another identical query
startSendAndReceive(mockListenerTwo, searchOptions);
- verify(mockListenerTwo).onServiceNameDiscovered(any());
- verify(mockListenerTwo).onServiceFound(any());
+ verify(mockListenerTwo).onServiceNameDiscovered(any(), eq(true) /* isServiceFromCache */);
+ verify(mockListenerTwo).onServiceFound(any(), eq(true) /* isServiceFromCache */);
// This time no query is submitted, only scheduled
assertNull(currentThreadExecutor.getAndClearSubmittedRunnable());
@@ -686,7 +687,8 @@
"service-instance-1", null /* host */, 0 /* port */,
SERVICE_TYPE_LABELS,
Collections.emptyMap(), TEST_TTL), socketKey);
- verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
"service-instance-1",
SERVICE_TYPE_LABELS,
@@ -697,7 +699,7 @@
Collections.emptyMap(),
socketKey);
- verify(mockListenerOne, never()).onServiceFound(any(MdnsServiceInfo.class));
+ verify(mockListenerOne, never()).onServiceFound(any(MdnsServiceInfo.class), anyBoolean());
verify(mockListenerOne, never()).onServiceUpdated(any(MdnsServiceInfo.class));
}
@@ -718,7 +720,8 @@
socketKey);
// Verify onServiceNameDiscovered was called once for the initial response.
- verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
"service-instance-1",
SERVICE_TYPE_LABELS,
@@ -730,7 +733,8 @@
socketKey);
// Verify onServiceFound was called once for the initial response.
- verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
MdnsServiceInfo initialServiceInfo = serviceInfoCaptor.getAllValues().get(1);
assertEquals(initialServiceInfo.getServiceInstanceName(), "service-instance-1");
assertEquals(initialServiceInfo.getIpv4Address(), ipV4Address);
@@ -770,7 +774,8 @@
socketKey);
// Verify onServiceNameDiscovered was called once for the initial response.
- verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
"service-instance-1",
SERVICE_TYPE_LABELS,
@@ -782,7 +787,8 @@
socketKey);
// Verify onServiceFound was called once for the initial response.
- verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
MdnsServiceInfo initialServiceInfo = serviceInfoCaptor.getAllValues().get(1);
assertEquals(initialServiceInfo.getServiceInstanceName(), "service-instance-1");
assertEquals(initialServiceInfo.getIpv6Address(), ipV6Address);
@@ -867,7 +873,8 @@
startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
// Verify onServiceNameDiscovered was called once for the existing response.
- verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(true) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
"service-instance-1",
SERVICE_TYPE_LABELS,
@@ -879,7 +886,8 @@
socketKey);
// Verify onServiceFound was called once for the existing response.
- verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(true) /* isServiceFromCache */);
MdnsServiceInfo existingServiceInfo = serviceInfoCaptor.getAllValues().get(1);
assertEquals(existingServiceInfo.getServiceInstanceName(), "service-instance-1");
assertEquals(existingServiceInfo.getIpv4Address(), "192.168.1.1");
@@ -897,8 +905,9 @@
// Verify onServiceFound was not called on the newly registered listener after the existing
// response is gone.
- verify(mockListenerTwo, never()).onServiceNameDiscovered(any(MdnsServiceInfo.class));
- verify(mockListenerTwo, never()).onServiceFound(any(MdnsServiceInfo.class));
+ verify(mockListenerTwo, never()).onServiceNameDiscovered(
+ any(MdnsServiceInfo.class), eq(false));
+ verify(mockListenerTwo, never()).onServiceFound(any(MdnsServiceInfo.class), anyBoolean());
}
@Test
@@ -1044,7 +1053,8 @@
socketKey);
// Verify onServiceNameDiscovered was first called for the initial response.
- inOrder.verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ inOrder.verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
serviceName,
SERVICE_TYPE_LABELS,
@@ -1056,7 +1066,8 @@
socketKey);
// Verify onServiceFound was second called for the second response.
- inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ inOrder.verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(1),
serviceName,
SERVICE_TYPE_LABELS,
@@ -1183,10 +1194,11 @@
Collections.emptyList() /* authorityRecords */,
Collections.emptyList() /* additionalRecords */);
- inOrder.verify(mockListenerOne, never()).onServiceNameDiscovered(any());
+ inOrder.verify(mockListenerOne, never()).onServiceNameDiscovered(any(), anyBoolean());
processResponse(addressResponse, socketKey);
- inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ inOrder.verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getValue(),
instanceName,
SERVICE_TYPE_LABELS,
@@ -1253,8 +1265,10 @@
Collections.emptyList() /* additionalRecords */);
processResponse(srvTxtResponse, socketKey);
dispatchMessage();
- inOrder.verify(mockListenerOne).onServiceNameDiscovered(any());
- inOrder.verify(mockListenerOne).onServiceFound(any());
+ inOrder.verify(mockListenerOne).onServiceNameDiscovered(
+ any(), eq(false) /* isServiceFromCache */);
+ inOrder.verify(mockListenerOne).onServiceFound(
+ any(), eq(false) /* isServiceFromCache */);
// Expect no query on the next run
currentThreadExecutor.getAndClearLastScheduledRunnable().run();
@@ -1355,24 +1369,29 @@
// mockListenerOne gets notified for the requested instance
verify(mockListenerOne).onServiceNameDiscovered(
- matchServiceName(capitalizedRequestInstance));
- verify(mockListenerOne).onServiceFound(matchServiceName(capitalizedRequestInstance));
+ matchServiceName(capitalizedRequestInstance), eq(false) /* isServiceFromCache */);
+ verify(mockListenerOne).onServiceFound(
+ matchServiceName(capitalizedRequestInstance), eq(false) /* isServiceFromCache */);
// ...but does not get any callback for the other instance
- verify(mockListenerOne, never()).onServiceFound(matchServiceName(otherInstance));
- verify(mockListenerOne, never()).onServiceNameDiscovered(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceFound(
+ matchServiceName(otherInstance), anyBoolean());
+ verify(mockListenerOne, never()).onServiceNameDiscovered(
+ matchServiceName(otherInstance), anyBoolean());
verify(mockListenerOne, never()).onServiceUpdated(matchServiceName(otherInstance));
verify(mockListenerOne, never()).onServiceRemoved(matchServiceName(otherInstance));
// mockListenerTwo gets notified for both though
final InOrder inOrder = inOrder(mockListenerTwo);
inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
- matchServiceName(capitalizedRequestInstance));
+ matchServiceName(capitalizedRequestInstance), eq(false) /* isServiceFromCache */);
inOrder.verify(mockListenerTwo).onServiceFound(
- matchServiceName(capitalizedRequestInstance));
+ matchServiceName(capitalizedRequestInstance), eq(false) /* isServiceFromCache */);
- inOrder.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
- inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+ inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
+ inOrder.verify(mockListenerTwo).onServiceFound(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
inOrder.verify(mockListenerTwo).onServiceUpdated(matchServiceName(otherInstance));
inOrder.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
}
@@ -1439,22 +1458,30 @@
final ArgumentMatcher<MdnsServiceInfo> subtypeInstanceMatcher = info ->
info.getServiceInstanceName().equals(matchingInstance)
&& info.getSubtypes().equals(Collections.singletonList(subtype));
- verify(mockListenerOne).onServiceNameDiscovered(argThat(subtypeInstanceMatcher));
- verify(mockListenerOne).onServiceFound(argThat(subtypeInstanceMatcher));
+ verify(mockListenerOne).onServiceNameDiscovered(
+ argThat(subtypeInstanceMatcher), eq(false) /* isServiceFromCache */);
+ verify(mockListenerOne).onServiceFound(
+ argThat(subtypeInstanceMatcher), eq(false) /* isServiceFromCache */);
// ...but does not get any callback for the other instance
- verify(mockListenerOne, never()).onServiceFound(matchServiceName(otherInstance));
- verify(mockListenerOne, never()).onServiceNameDiscovered(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceFound(
+ matchServiceName(otherInstance), anyBoolean());
+ verify(mockListenerOne, never()).onServiceNameDiscovered(
+ matchServiceName(otherInstance), anyBoolean());
verify(mockListenerOne, never()).onServiceUpdated(matchServiceName(otherInstance));
verify(mockListenerOne, never()).onServiceRemoved(matchServiceName(otherInstance));
// mockListenerTwo gets notified for both though
final InOrder inOrder = inOrder(mockListenerTwo);
- inOrder.verify(mockListenerTwo).onServiceNameDiscovered(argThat(subtypeInstanceMatcher));
- inOrder.verify(mockListenerTwo).onServiceFound(argThat(subtypeInstanceMatcher));
+ inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
+ argThat(subtypeInstanceMatcher), eq(false) /* isServiceFromCache */);
+ inOrder.verify(mockListenerTwo).onServiceFound(
+ argThat(subtypeInstanceMatcher), eq(false) /* isServiceFromCache */);
- inOrder.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
- inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+ inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
+ inOrder.verify(mockListenerTwo).onServiceFound(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
inOrder.verify(mockListenerTwo).onServiceUpdated(matchServiceName(otherInstance));
inOrder.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
}
@@ -1508,24 +1535,30 @@
// mockListenerOne gets notified for the requested instance
final InOrder inOrder1 = inOrder(mockListenerOne);
inOrder1.verify(mockListenerOne).onServiceNameDiscovered(
- matchServiceName(requestedInstance));
- inOrder1.verify(mockListenerOne).onServiceFound(matchServiceName(requestedInstance));
+ matchServiceName(requestedInstance), eq(false) /* isServiceFromCache */);
+ inOrder1.verify(mockListenerOne).onServiceFound(
+ matchServiceName(requestedInstance), eq(false) /* isServiceFromCache */);
inOrder1.verify(mockListenerOne).onServiceRemoved(matchServiceName(requestedInstance));
inOrder1.verify(mockListenerOne).onServiceNameRemoved(matchServiceName(requestedInstance));
- verify(mockListenerOne, never()).onServiceFound(matchServiceName(otherInstance));
- verify(mockListenerOne, never()).onServiceNameDiscovered(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceFound(
+ matchServiceName(otherInstance), anyBoolean());
+ verify(mockListenerOne, never()).onServiceNameDiscovered(
+ matchServiceName(otherInstance), anyBoolean());
verify(mockListenerOne, never()).onServiceRemoved(matchServiceName(otherInstance));
verify(mockListenerOne, never()).onServiceNameRemoved(matchServiceName(otherInstance));
// mockListenerTwo gets notified for both though
final InOrder inOrder2 = inOrder(mockListenerTwo);
inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(
- matchServiceName(requestedInstance));
- inOrder2.verify(mockListenerTwo).onServiceFound(matchServiceName(requestedInstance));
+ matchServiceName(requestedInstance), eq(false) /* isServiceFromCache */);
+ inOrder2.verify(mockListenerTwo).onServiceFound(
+ matchServiceName(requestedInstance), eq(false) /* isServiceFromCache */);
inOrder2.verify(mockListenerTwo).onServiceRemoved(matchServiceName(requestedInstance));
inOrder2.verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(requestedInstance));
- verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
- verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+ verify(mockListenerTwo).onServiceNameDiscovered(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
+ verify(mockListenerTwo).onServiceFound(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(otherInstance));
}
@@ -1547,7 +1580,8 @@
socketKey);
// Verify that onServiceNameDiscovered is called.
- inOrder.verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ inOrder.verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
serviceName,
SERVICE_TYPE_LABELS,
@@ -1559,7 +1593,8 @@
socketKey);
// Verify that onServiceFound is called.
- inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ inOrder.verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(1),
serviceName,
SERVICE_TYPE_LABELS,
@@ -1581,7 +1616,8 @@
// The services are cached in MdnsServiceCache, verify that onServiceNameDiscovered is
// called immediately.
- inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(true) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(2),
serviceName,
SERVICE_TYPE_LABELS,
@@ -1594,7 +1630,8 @@
// The services are cached in MdnsServiceCache, verify that onServiceFound is
// called immediately.
- inOrder2.verify(mockListenerTwo).onServiceFound(serviceInfoCaptor.capture());
+ inOrder2.verify(mockListenerTwo).onServiceFound(
+ serviceInfoCaptor.capture(), eq(true) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(3),
serviceName,
SERVICE_TYPE_LABELS,
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
index 69efc61..74f1c37 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
@@ -39,6 +39,7 @@
import android.text.format.DateUtils;
import com.android.net.module.util.HexDump;
+import com.android.net.module.util.SharedLog;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -74,6 +75,7 @@
@Mock private MdnsSocket mockUnicastSocket;
@Mock private MulticastLock mockMulticastLock;
@Mock private MdnsSocketClient.Callback mockCallback;
+ @Mock private SharedLog sharedLog;
private MdnsSocketClient mdnsClient;
@@ -84,9 +86,9 @@
when(mockWifiManager.createMulticastLock(ArgumentMatchers.anyString()))
.thenReturn(mockMulticastLock);
- mdnsClient = new MdnsSocketClient(mContext, mockMulticastLock) {
+ mdnsClient = new MdnsSocketClient(mContext, mockMulticastLock, sharedLog) {
@Override
- MdnsSocket createMdnsSocket(int port) throws IOException {
+ MdnsSocket createMdnsSocket(int port, SharedLog sharedLog) throws IOException {
if (port == MdnsConstants.MDNS_PORT) {
return mockMulticastSocket;
}
@@ -513,9 +515,9 @@
//MdnsConfigsFlagsImpl.allowNetworkInterfaceIndexPropagation.override(true);
when(mockMulticastSocket.getInterfaceIndex()).thenReturn(21);
- mdnsClient = new MdnsSocketClient(mContext, mockMulticastLock) {
+ mdnsClient = new MdnsSocketClient(mContext, mockMulticastLock, sharedLog) {
@Override
- MdnsSocket createMdnsSocket(int port) {
+ MdnsSocket createMdnsSocket(int port, SharedLog sharedLog) {
if (port == MdnsConstants.MDNS_PORT) {
return mockMulticastSocket;
}
@@ -536,9 +538,9 @@
//MdnsConfigsFlagsImpl.allowNetworkInterfaceIndexPropagation.override(false);
when(mockMulticastSocket.getInterfaceIndex()).thenReturn(21);
- mdnsClient = new MdnsSocketClient(mContext, mockMulticastLock) {
+ mdnsClient = new MdnsSocketClient(mContext, mockMulticastLock, sharedLog) {
@Override
- MdnsSocket createMdnsSocket(int port) {
+ MdnsSocket createMdnsSocket(int port, SharedLog sharedLog) {
if (port == MdnsConstants.MDNS_PORT) {
return mockMulticastSocket;
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index e971de7..1cc9985 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -78,6 +78,7 @@
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -119,6 +120,7 @@
@Mock private NetworkInterfaceWrapper mLocalOnlyIfaceWrapper;
@Mock private NetworkInterfaceWrapper mTetheredIfaceWrapper;
@Mock private SocketRequestMonitor mSocketRequestMonitor;
+ private HandlerThread mHandlerThread;
private Handler mHandler;
private MdnsSocketProvider mSocketProvider;
private NetworkCallback mNetworkCallback;
@@ -152,14 +154,14 @@
.getNetworkInterfaceByName(WIFI_P2P_IFACE_NAME);
doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME);
doReturn(mock(MdnsInterfaceSocket.class))
- .when(mDeps).createMdnsInterfaceSocket(any(), anyInt(), any(), any());
+ .when(mDeps).createMdnsInterfaceSocket(any(), anyInt(), any(), any(), any());
doReturn(TETHERED_IFACE_IDX).when(mDeps).getNetworkInterfaceIndexByName(
- TETHERED_IFACE_NAME);
+ eq(TETHERED_IFACE_NAME), any());
doReturn(789).when(mDeps).getNetworkInterfaceIndexByName(
- WIFI_P2P_IFACE_NAME);
- final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
- thread.start();
- mHandler = new Handler(thread.getLooper());
+ eq(WIFI_P2P_IFACE_NAME), any());
+ mHandlerThread = new HandlerThread("MdnsSocketProviderTest");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
doReturn(mTestSocketNetLinkMonitor).when(mDeps).createSocketNetlinkMonitor(any(), any(),
any());
@@ -170,10 +172,18 @@
return mTestSocketNetLinkMonitor;
}).when(mDeps).createSocketNetlinkMonitor(any(), any(),
any());
- mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps, mLog,
+ mSocketProvider = new MdnsSocketProvider(mContext, mHandlerThread.getLooper(), mDeps, mLog,
mSocketRequestMonitor);
}
+ @After
+ public void tearDown() throws Exception {
+ if (mHandlerThread != null) {
+ mHandlerThread.quitSafely();
+ mHandlerThread.join();
+ }
+ }
+
private void runOnHandler(Runnable r) {
mHandler.post(r);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketTests.java
index 73dbd38..5809684 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketTests.java
@@ -21,6 +21,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import com.android.net.module.util.SharedLog;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -50,6 +51,7 @@
@Mock private NetworkInterfaceWrapper mockNetworkInterfaceWrapper;
@Mock private MulticastSocket mockMulticastSocket;
@Mock private MulticastNetworkInterfaceProvider mockMulticastNetworkInterfaceProvider;
+ @Mock private SharedLog sharedLog;
private SocketAddress socketIPv4Address;
private SocketAddress socketIPv6Address;
@@ -75,7 +77,8 @@
@Test
public void mdnsSocket_basicFunctionality() throws IOException {
- mdnsSocket = new MdnsSocket(mockMulticastNetworkInterfaceProvider, mockMulticastSocket);
+ mdnsSocket = new MdnsSocket(mockMulticastNetworkInterfaceProvider, mockMulticastSocket,
+ sharedLog);
mdnsSocket.send(datagramPacket);
verify(mockMulticastSocket).setNetworkInterface(networkInterface);
verify(mockMulticastSocket).send(datagramPacket);
@@ -101,7 +104,8 @@
when(mockMulticastNetworkInterfaceProvider.getMulticastNetworkInterfaces())
.thenReturn(Collections.singletonList(mockNetworkInterfaceWrapper));
- mdnsSocket = new MdnsSocket(mockMulticastNetworkInterfaceProvider, mockMulticastSocket);
+ mdnsSocket = new MdnsSocket(mockMulticastNetworkInterfaceProvider, mockMulticastSocket,
+ sharedLog);
when(mockMulticastNetworkInterfaceProvider.isOnIpV6OnlyNetwork(
Collections.singletonList(mockNetworkInterfaceWrapper)))
@@ -125,7 +129,8 @@
when(mockMulticastNetworkInterfaceProvider.getMulticastNetworkInterfaces())
.thenReturn(Collections.singletonList(mockNetworkInterfaceWrapper));
- mdnsSocket = new MdnsSocket(mockMulticastNetworkInterfaceProvider, mockMulticastSocket);
+ mdnsSocket = new MdnsSocket(mockMulticastNetworkInterfaceProvider, mockMulticastSocket,
+ sharedLog);
when(mockMulticastNetworkInterfaceProvider.isOnIpV6OnlyNetwork(
Collections.singletonList(mockNetworkInterfaceWrapper)))
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProviderTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProviderTests.java
index 2268dfe..af233c9 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProviderTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProviderTests.java
@@ -30,6 +30,7 @@
import androidx.test.InstrumentationRegistry;
+import com.android.net.module.util.SharedLog;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -65,6 +66,8 @@
@Mock private NetworkInterfaceWrapper multicastInterfaceOne;
@Mock private NetworkInterfaceWrapper multicastInterfaceTwo;
+ @Mock private SharedLog sharedLog;
+
private final List<NetworkInterfaceWrapper> networkInterfaces = new ArrayList<>();
private MulticastNetworkInterfaceProvider provider;
private Context context;
@@ -156,7 +159,7 @@
false /* isIpv6 */);
provider =
- new MulticastNetworkInterfaceProvider(context) {
+ new MulticastNetworkInterfaceProvider(context, sharedLog) {
@Override
List<NetworkInterfaceWrapper> getNetworkInterfaces() {
return networkInterfaces;
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 6292d45..9453617 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -79,7 +79,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -240,7 +239,9 @@
private @Mock INetd mNetd;
private @Mock TetheringManager mTetheringManager;
private @Mock NetworkStatsFactory mStatsFactory;
- private @Mock NetworkStatsSettings mSettings;
+ @NonNull
+ private final TestNetworkStatsSettings mSettings =
+ new TestNetworkStatsSettings(HOUR_IN_MILLIS, WEEK_IN_MILLIS);
private @Mock IBinder mUsageCallbackBinder;
private TestableUsageCallback mUsageCallback;
private @Mock AlarmManager mAlarmManager;
@@ -533,7 +534,6 @@
mStatsDir = null;
mNetd = null;
- mSettings = null;
mSession.close();
mService = null;
@@ -1765,7 +1765,7 @@
}
private void setCombineSubtypeEnabled(boolean enable) {
- doReturn(enable).when(mSettings).getCombineSubtypeEnabled();
+ mSettings.setCombineSubtypeEnabled(enable);
mHandler.post(() -> mContentObserver.onChange(false, Settings.Global
.getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED)));
waitForIdle();
@@ -2289,21 +2289,80 @@
mockSettings(HOUR_IN_MILLIS, WEEK_IN_MILLIS);
}
- private void mockSettings(long bucketDuration, long deleteAge) throws Exception {
- doReturn(HOUR_IN_MILLIS).when(mSettings).getPollInterval();
- doReturn(0L).when(mSettings).getPollDelay();
- doReturn(true).when(mSettings).getSampleEnabled();
- doReturn(false).when(mSettings).getCombineSubtypeEnabled();
+ private void mockSettings(long bucketDuration, long deleteAge) {
+ mSettings.setConfig(new Config(bucketDuration, deleteAge, deleteAge));
+ }
- final Config config = new Config(bucketDuration, deleteAge, deleteAge);
- doReturn(config).when(mSettings).getXtConfig();
- doReturn(config).when(mSettings).getUidConfig();
- doReturn(config).when(mSettings).getUidTagConfig();
+ // Note that this object will be accessed from test main thread and service handler thread.
+ // Thus, it has to be thread safe in order to prevent from flakiness.
+ private static class TestNetworkStatsSettings
+ extends NetworkStatsService.DefaultNetworkStatsSettings {
- doReturn(MB_IN_BYTES).when(mSettings).getGlobalAlertBytes(anyLong());
- doReturn(MB_IN_BYTES).when(mSettings).getXtPersistBytes(anyLong());
- doReturn(MB_IN_BYTES).when(mSettings).getUidPersistBytes(anyLong());
- doReturn(MB_IN_BYTES).when(mSettings).getUidTagPersistBytes(anyLong());
+ @NonNull
+ private volatile Config mConfig;
+ private final AtomicBoolean mCombineSubtypeEnabled = new AtomicBoolean();
+
+ TestNetworkStatsSettings(long bucketDuration, long deleteAge) {
+ mConfig = new Config(bucketDuration, deleteAge, deleteAge);
+ }
+
+ void setConfig(@NonNull Config config) {
+ mConfig = config;
+ }
+
+ @Override
+ public long getPollDelay() {
+ return 0L;
+ }
+
+ @Override
+ public long getGlobalAlertBytes(long def) {
+ return MB_IN_BYTES;
+ }
+
+ @Override
+ public Config getXtConfig() {
+ return mConfig;
+ }
+
+ @Override
+ public Config getUidConfig() {
+ return mConfig;
+ }
+
+ @Override
+ public Config getUidTagConfig() {
+ return mConfig;
+ }
+
+ @Override
+ public long getXtPersistBytes(long def) {
+ return MB_IN_BYTES;
+ }
+
+ @Override
+ public long getUidPersistBytes(long def) {
+ return MB_IN_BYTES;
+ }
+
+ @Override
+ public long getUidTagPersistBytes(long def) {
+ return MB_IN_BYTES;
+ }
+
+ @Override
+ public boolean getCombineSubtypeEnabled() {
+ return mCombineSubtypeEnabled.get();
+ }
+
+ public void setCombineSubtypeEnabled(boolean enable) {
+ mCombineSubtypeEnabled.set(enable);
+ }
+
+ @Override
+ public boolean getAugmentEnabled() {
+ return false;
+ }
}
private void assertStatsFilesExist(boolean exist) {