Merge "Execute netd callbacks on CS handler thread" 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/OWNERS_core_networking_xts b/OWNERS_core_networking_xts
index 1844334..7612210 100644
--- a/OWNERS_core_networking_xts
+++ b/OWNERS_core_networking_xts
@@ -4,4 +4,6 @@
# For cherry-picks of CLs that are already merged in aosp/master, or flaky test fixes.
jchalard@google.com #{LAST_RESORT_SUGGESTION}
maze@google.com #{LAST_RESORT_SUGGESTION}
+# In addition to cherry-picks and flaky test fixes, also for incremental changes on NsdManager tests
+# to increase coverage for existing behavior, and testing of bug fixes in NsdManager
reminv@google.com #{LAST_RESORT_SUGGESTION}
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/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 062ecc5..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
@@ -17,7 +17,8 @@
package com.android.networkstack.tethering.apishim.api31;
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
-import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
+
+import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
import android.net.IpPrefix;
import android.net.MacAddress;
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/integration/Android.bp b/Tethering/tests/integration/Android.bp
index 5e08aba..20f0bc6 100644
--- a/Tethering/tests/integration/Android.bp
+++ b/Tethering/tests/integration/Android.bp
@@ -25,11 +25,12 @@
],
min_sdk_version: "30",
static_libs: [
- "NetworkStackApiStableLib",
+ "DhcpPacketLib",
"androidx.test.rules",
"cts-net-utils",
"mockito-target-extended-minus-junit4",
"net-tests-utils",
+ "net-utils-device-common",
"net-utils-device-common-bpf",
"testables",
"connectivity-net-module-utils-bpf",
diff --git a/Tethering/tests/integration/base/android/net/TetheringTester.java b/Tethering/tests/integration/base/android/net/TetheringTester.java
index 3f3768e..4f3c6e7 100644
--- a/Tethering/tests/integration/base/android/net/TetheringTester.java
+++ b/Tethering/tests/integration/base/android/net/TetheringTester.java
@@ -74,6 +74,7 @@
import com.android.net.module.util.Ipv6Utils;
import com.android.net.module.util.PacketBuilder;
import com.android.net.module.util.Struct;
+import com.android.net.module.util.arp.ArpPacket;
import com.android.net.module.util.structs.EthernetHeader;
import com.android.net.module.util.structs.Icmpv4Header;
import com.android.net.module.util.structs.Icmpv6Header;
@@ -85,7 +86,6 @@
import com.android.net.module.util.structs.RaHeader;
import com.android.net.module.util.structs.TcpHeader;
import com.android.net.module.util.structs.UdpHeader;
-import com.android.networkstack.arp.ArpPacket;
import com.android.testutils.TapPacketReader;
import java.net.Inet4Address;
diff --git a/bpf_progs/netd.h b/bpf_progs/netd.h
index 1326db0..dcf6d6a 100644
--- a/bpf_progs/netd.h
+++ b/bpf_progs/netd.h
@@ -55,19 +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;
-} Stats;
-
-typedef struct {
uint64_t timestampNs;
uint32_t ifindex;
uint32_t length;
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
index 9520ef6..05b84c2 100644
--- a/framework-t/Android.bp
+++ b/framework-t/Android.bp
@@ -42,6 +42,7 @@
srcs: [
":framework-connectivity-tiramisu-updatable-sources",
":framework-nearby-java-sources",
+ ":framework-thread-sources",
],
libs: [
"unsupportedappusage",
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/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/Android.bp b/framework/Android.bp
index 7897492..8de8097 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -112,6 +112,7 @@
static_libs: [
"httpclient_api",
"httpclient_impl",
+ "http_client_logging",
],
libs: [
// This cannot be in the defaults clause above because if it were, it would be used
@@ -132,6 +133,7 @@
],
impl_only_static_libs: [
"httpclient_impl",
+ "http_client_logging",
],
}
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index d239277..73feee4 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -210,8 +210,8 @@
};
auto configuration = mConfigurationMap.readValue(CURRENT_STATS_MAP_CONFIGURATION_KEY);
if (!configuration.ok()) {
- ALOGE("Failed to get current configuration: %s, fd: %d",
- strerror(configuration.error().code()), mConfigurationMap.getMap().get());
+ ALOGE("Failed to get current configuration: %s",
+ strerror(configuration.error().code()));
return -configuration.error().code();
}
if (configuration.value() != SELECT_MAP_A && configuration.value() != SELECT_MAP_B) {
@@ -224,7 +224,7 @@
// HACK: mStatsMapB becomes RW BpfMap here, but countUidStatsEntries doesn't modify so it works
base::Result<void> res = currentMap.iterate(countUidStatsEntries);
if (!res.ok()) {
- ALOGE("Failed to count the stats entry in map %d: %s", currentMap.getMap().get(),
+ ALOGE("Failed to count the stats entry in map: %s",
strerror(res.error().code()));
return -res.error().code();
}
@@ -243,8 +243,7 @@
// should be fine to concurrently update the map while eBPF program is running.
res = mCookieTagMap.writeValue(sock_cookie, newKey, BPF_ANY);
if (!res.ok()) {
- ALOGE("Failed to tag the socket: %s, fd: %d", strerror(res.error().code()),
- mCookieTagMap.getMap().get());
+ ALOGE("Failed to tag the socket: %s", strerror(res.error().code()));
return -res.error().code();
}
ALOGD("Socket with cookie %" PRIu64 " tagged successfully with tag %" PRIu32 " uid %u "
diff --git a/remoteauth/README.md b/remoteauth/README.md
index 384fcf7..f28154d 100644
--- a/remoteauth/README.md
+++ b/remoteauth/README.md
@@ -23,7 +23,6 @@
### AIDEGen
-AIDEGen is deprecated, prefer ASfP [go/asfp](http://go/asfp)
```sh
$ source build/envsetup.sh && lunch <TARGET>
$ cd packages/modules/Connectivity
@@ -31,17 +30,13 @@
# This will launch Intellij project for RemoteAuth module.
```
-### ASfP
-
-See full instructions for ASfP at [go/asfp-getting-started](http://go/asfp-getting-started)
-
## Build and Install
```sh
$ source build/envsetup.sh && lunch <TARGET>
-$ m com.google.android.tethering deapexer
+$ m com.android.tethering deapexer
$ $ANDROID_BUILD_TOP/out/host/linux-x86/bin/deapexer decompress --input \
- ${ANDROID_PRODUCT_OUT}/system/apex/com.google.android.tethering.capex \
+ ${ANDROID_PRODUCT_OUT}/system/apex/com.android.tethering.capex \
--output /tmp/tethering.apex
$ adb install -r /tmp/tethering.apex
```
diff --git a/remoteauth/service/java/com/android/server/remoteauth/README.md b/remoteauth/service/java/com/android/server/remoteauth/README.md
index 423ab45..2f8b096 100644
--- a/remoteauth/service/java/com/android/server/remoteauth/README.md
+++ b/remoteauth/service/java/com/android/server/remoteauth/README.md
@@ -1 +1,4 @@
-This is the source root for the RemoteAuthService
\ No newline at end of file
+This is the source root for the RemoteAuthService
+
+## Remote connectivity manager
+Provides the connectivity manager to manage connections with the peer device.
diff --git a/remoteauth/service/java/com/android/server/remoteauth/connectivity/CdmConnectionInfo.java b/remoteauth/service/java/com/android/server/remoteauth/connectivity/CdmConnectionInfo.java
new file mode 100644
index 0000000..8bfdd36
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/connectivity/CdmConnectionInfo.java
@@ -0,0 +1,108 @@
+/*
+ * 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.remoteauth.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.TargetApi;
+import android.companion.AssociationInfo;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Encapsulates the connection information for companion device manager connections.
+ *
+ * <p>Connection information captures the details of underlying connection such as connection id,
+ * type of connection and peer device mac address.
+ */
+// TODO(b/295407748): Change to use @DataClass.
+// TODO(b/296625303): Change to VANILLA_ICE_CREAM when AssociationInfo is available in V.
+@TargetApi(Build.VERSION_CODES.TIRAMISU)
+public final class CdmConnectionInfo extends ConnectionInfo {
+ @NonNull private final AssociationInfo mAssociationInfo;
+
+ public CdmConnectionInfo(int connectionId, @NonNull AssociationInfo associationInfo) {
+ super(connectionId);
+ mAssociationInfo = associationInfo;
+ }
+
+ private CdmConnectionInfo(@NonNull Parcel in) {
+ super(in);
+ mAssociationInfo = in.readTypedObject(AssociationInfo.CREATOR);
+ }
+
+ /** Used to read CdmConnectionInfo from a Parcel */
+ @NonNull
+ public static final Parcelable.Creator<CdmConnectionInfo> CREATOR =
+ new Parcelable.Creator<CdmConnectionInfo>() {
+ public CdmConnectionInfo createFromParcel(@NonNull Parcel in) {
+ return new CdmConnectionInfo(in);
+ }
+
+ public CdmConnectionInfo[] newArray(int size) {
+ return new CdmConnectionInfo[size];
+ }
+ };
+
+ /** No special parcel contents. */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Flatten this CdmConnectionInfo in to a Parcel.
+ *
+ * @param out The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeTypedObject(mAssociationInfo, 0);
+ }
+
+ public AssociationInfo getAssociationInfo() {
+ return mAssociationInfo;
+ }
+
+ /** Returns a string representation of ConnectionInfo. */
+ @Override
+ public String toString() {
+ return super.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof CdmConnectionInfo)) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ CdmConnectionInfo other = (CdmConnectionInfo) o;
+ return mAssociationInfo.equals(other.getAssociationInfo());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAssociationInfo);
+ }
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/connectivity/Connection.java b/remoteauth/service/java/com/android/server/remoteauth/connectivity/Connection.java
new file mode 100644
index 0000000..eb5458d
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/connectivity/Connection.java
@@ -0,0 +1,86 @@
+/*
+ * 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.remoteauth.connectivity;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A connection with the peer device.
+ *
+ * <p>Connections are used to exchange data with the peer device.
+ *
+ */
+public interface Connection {
+ /** Unknown error. */
+ int ERROR_UNKNOWN = 0;
+
+ /** Message was sent successfully. */
+ int ERROR_OK = 1;
+
+ /** Timeout occurred while waiting for response from the peer. */
+ int ERROR_DEADLINE_EXCEEDED = 2;
+
+ /** Device became unavailable while sending the message. */
+ int ERROR_DEVICE_UNAVAILABLE = 3;
+
+ /** Represents error code. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ERROR_UNKNOWN, ERROR_OK, ERROR_DEADLINE_EXCEEDED, ERROR_DEVICE_UNAVAILABLE})
+ @interface ErrorCode {}
+
+ /**
+ * Callback for clients to get the response of sendRequest. {@link onSuccess} is called if the
+ * peer device responds with Status::OK, otherwise runs the {@link onFailure} callback.
+ */
+ abstract class MessageResponseCallback {
+ /**
+ * Called on a success.
+ *
+ * @param buffer response from the device.
+ */
+ public void onSuccess(byte[] buffer) {}
+
+ /**
+ * Called when message sending fails.
+ *
+ * @param errorCode indicating the error.
+ */
+ public void onFailure(@ErrorCode int errorCode) {}
+ }
+
+ /**
+ * Sends a request to the peer.
+ *
+ * @param request byte array to be sent to the peer device.
+ * @param messageResponseCallback callback to be run when the peer response is received or if an
+ * error occurred.
+ */
+ void sendRequest(byte[] request, MessageResponseCallback messageResponseCallback);
+
+ /** Triggers a disconnect from the peer device. */
+ void disconnect();
+
+ /**
+ * Returns the connection information.
+ *
+ * @return A connection information object.
+ */
+ ConnectionInfo getConnectionInfo();
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/connectivity/ConnectionException.java b/remoteauth/service/java/com/android/server/remoteauth/connectivity/ConnectionException.java
new file mode 100644
index 0000000..a8c7860
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/connectivity/ConnectionException.java
@@ -0,0 +1,56 @@
+/*
+ * 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.remoteauth.connectivity;
+
+import static com.android.server.remoteauth.connectivity.ConnectivityManager.ReasonCode;
+
+import android.annotation.Nullable;
+
+/** Exception that signals that the connection request failed. */
+public final class ConnectionException extends RuntimeException {
+ private final @ReasonCode int mReasonCode;
+
+ public ConnectionException(@ReasonCode int reasonCode) {
+ super();
+ this.mReasonCode = reasonCode;
+ }
+
+ public ConnectionException(@ReasonCode int reasonCode, @Nullable String message) {
+ super(message);
+ this.mReasonCode = reasonCode;
+ }
+
+ public ConnectionException(@ReasonCode int reasonCode, @Nullable Throwable cause) {
+ super(cause);
+ this.mReasonCode = reasonCode;
+ }
+
+ public ConnectionException(
+ @ReasonCode int reasonCode, @Nullable String message, @Nullable Throwable cause) {
+ super(message, cause);
+ this.mReasonCode = reasonCode;
+ }
+
+ public @ReasonCode int getReasonCode() {
+ return this.mReasonCode;
+ }
+
+ @Override
+ public String getMessage() {
+ return super.getMessage() + " Reason code: " + this.mReasonCode;
+ }
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/connectivity/ConnectionInfo.java b/remoteauth/service/java/com/android/server/remoteauth/connectivity/ConnectionInfo.java
new file mode 100644
index 0000000..39bfa8d
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/connectivity/ConnectionInfo.java
@@ -0,0 +1,88 @@
+/*
+ * 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.remoteauth.connectivity;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Encapsulates the connection information.
+ *
+ * <p>Connection information captures the details of underlying connection such as connection id,
+ * type of connection and peer device mac address.
+ *
+ */
+// TODO(b/295407748) Change to use @DataClass.
+public abstract class ConnectionInfo implements Parcelable {
+ int mConnectionId;
+
+ public ConnectionInfo(int connectionId) {
+ mConnectionId = connectionId;
+ }
+
+ /** Create object from Parcel */
+ public ConnectionInfo(@NonNull Parcel in) {
+ mConnectionId = in.readInt();
+ }
+
+ public int getConnectionId() {
+ return mConnectionId;
+ }
+
+ /** No special parcel contents. */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Flattens this ConnectionInfo in to a Parcel.
+ *
+ * @param out The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mConnectionId);
+ }
+
+ /** Returns string representation of ConnectionInfo. */
+ @Override
+ public String toString() {
+ return "ConnectionInfo[" + "connectionId= " + mConnectionId + "]";
+ }
+
+ /** Returns true if this ConnectionInfo object is equal to the other. */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ConnectionInfo)) {
+ return false;
+ }
+
+ ConnectionInfo other = (ConnectionInfo) o;
+ return mConnectionId == other.getConnectionId();
+ }
+
+ /** Returns the hashcode of this object */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mConnectionId);
+ }
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/connectivity/ConnectivityManager.java b/remoteauth/service/java/com/android/server/remoteauth/connectivity/ConnectivityManager.java
new file mode 100644
index 0000000..bc0d77e
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/connectivity/ConnectivityManager.java
@@ -0,0 +1,115 @@
+/*
+ * 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.remoteauth.connectivity;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Performs discovery and triggers a connection to an associated device.
+ */
+public interface ConnectivityManager {
+ /**
+ * Starts device discovery.
+ *
+ * <p>Discovery continues until stopped using {@link stopDiscovery} or times out.
+ *
+ * @param discoveryFilter to filter for devices during discovery.
+ * @param discoveredDeviceReceiver callback to run when device is found or lost.
+ */
+ void startDiscovery(
+ @NonNull DiscoveryFilter discoveryFilter,
+ @NonNull DiscoveredDeviceReceiver discoveredDeviceReceiver);
+
+ /**
+ * Stops device discovery.
+ *
+ * @param discoveryFilter filter used to start discovery.
+ * @param discoveredDeviceReceiver callback passed with startDiscovery.
+ */
+ void stopDiscovery(
+ @NonNull DiscoveryFilter discoveryFilter,
+ @NonNull DiscoveredDeviceReceiver discoveredDeviceReceiver);
+
+ /** Unknown reason for connection failure. */
+ int ERROR_REASON_UNKNOWN = 0;
+
+ /** Indicates that the connection request timed out. */
+ int ERROR_CONNECTION_TIMED_OUT = 1;
+
+ /** Indicates that the connection request was refused by the peer. */
+ int ERROR_CONNECTION_REFUSED = 2;
+
+ /** Indicates that the peer device was unavailable. */
+ int ERROR_DEVICE_UNAVAILABLE = 3;
+
+ /** Reason codes for connect failure. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ERROR_REASON_UNKNOWN, ERROR_CONNECTION_TIMED_OUT, ERROR_CONNECTION_REFUSED,
+ ERROR_DEVICE_UNAVAILABLE})
+ @interface ReasonCode {}
+
+ /**
+ * Initiates a connection with the peer device.
+ *
+ * @param connectionInfo of the device discovered using {@link startDiscovery}.
+ * @param eventListener to listen for events from the underlying transport.
+ * @return {@link Connection} object or null connection is not established.
+ * @throws ConnectionException in case connection cannot be established.
+ */
+ @Nullable
+ Connection connect(
+ @NonNull ConnectionInfo connectionInfo, @NonNull EventListener eventListener);
+
+ /**
+ * Message received callback.
+ *
+ * <p>Clients should implement this callback to receive messages from the peer device.
+ */
+ abstract class MessageReceiver {
+ /**
+ * Receive message from the peer device.
+ *
+ * <p>Clients can set empty buffer as an ACK to the request.
+ *
+ * @param messageIn message from peer device.
+ * @param responseCallback {@link ResponseCallback} callback to send the response back.
+ */
+ public void onMessageReceived(byte[] messageIn, ResponseCallback responseCallback) {}
+ }
+
+ /**
+ * Starts listening for incoming messages.
+ *
+ * <p>Runs MessageReceiver callback when a message is received.
+ *
+ * @param messageReceiver to receive messages.
+ * @throws AssertionError if a listener is already configured.
+ */
+ void startListening(MessageReceiver messageReceiver);
+
+ /**
+ * Stops listening to incoming messages.
+ *
+ * @param messageReceiver to receive messages.
+ */
+ void stopListening(MessageReceiver messageReceiver);
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/connectivity/DiscoveredDevice.java b/remoteauth/service/java/com/android/server/remoteauth/connectivity/DiscoveredDevice.java
new file mode 100644
index 0000000..a3e1e58
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/connectivity/DiscoveredDevice.java
@@ -0,0 +1,79 @@
+/*
+ * 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.remoteauth.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Objects;
+
+/** Device discovered on a network interface like Bluetooth. */
+public final class DiscoveredDevice {
+ private @NonNull ConnectionInfo mConnectionInfo;
+ private @Nullable String mDisplayName;
+
+ public DiscoveredDevice(
+ @NonNull ConnectionInfo connectionInfo, @Nullable String displayName) {
+ this.mConnectionInfo = connectionInfo;
+ this.mDisplayName = displayName;
+ }
+
+ /**
+ * Returns connection information.
+ *
+ * @return connection information.
+ */
+ @NonNull
+ public ConnectionInfo getConnectionInfo() {
+ return this.mConnectionInfo;
+ }
+
+ /**
+ * Returns display name of the device.
+ *
+ * @return display name string.
+ */
+ @Nullable
+ public String getDisplayName() {
+ return this.mDisplayName;
+ }
+
+ /**
+ * Checks for equality between this and other object.
+ *
+ * @return true if equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof DiscoveredDevice)) {
+ return false;
+ }
+
+ DiscoveredDevice other = (DiscoveredDevice) o;
+ return mConnectionInfo.equals(other.getConnectionInfo())
+ && mDisplayName.equals(other.getDisplayName());
+ }
+
+ /**
+ * Returns hash code of the object.
+ *
+ * @return hash code.
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDisplayName, mConnectionInfo.getConnectionId());
+ }
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/connectivity/DiscoveredDeviceReceiver.java b/remoteauth/service/java/com/android/server/remoteauth/connectivity/DiscoveredDeviceReceiver.java
new file mode 100644
index 0000000..90a3e30
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/connectivity/DiscoveredDeviceReceiver.java
@@ -0,0 +1,34 @@
+/*
+ * 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.remoteauth.connectivity;
+
+/** Callbacks triggered on discovery. */
+public abstract class DiscoveredDeviceReceiver {
+ /**
+ * Callback called when a device matching the discovery filter is found.
+ *
+ * @param discoveredDevice the discovered device.
+ */
+ public void onDiscovered(DiscoveredDevice discoveredDevice) {}
+
+ /**
+ * Callback called when a previously discovered device using {@link
+ * ConnectivityManager#startDiscovery} is lost.
+ *
+ * @param discoveredDevice the lost device
+ */
+ public void onLost(DiscoveredDevice discoveredDevice) {}
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/connectivity/DiscoveryFilter.java b/remoteauth/service/java/com/android/server/remoteauth/connectivity/DiscoveryFilter.java
new file mode 100644
index 0000000..36c4b60
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/connectivity/DiscoveryFilter.java
@@ -0,0 +1,132 @@
+/*
+ * 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.remoteauth.connectivity;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Filter for device discovery.
+ *
+ * <p>Callers can use this class to provide a discovery filter to the {@link
+ * ConnectivityManager.startDiscovery} method. A device is discovered if it matches at least one of
+ * the filter criteria (device type, name or peer address).
+ */
+public final class DiscoveryFilter {
+
+ /** Device type WATCH. */
+ public static final int WATCH = 0;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({WATCH})
+ public @interface DeviceType {}
+
+ private @DeviceType int mDeviceType;
+ private final @Nullable String mDeviceName;
+ private final @Nullable String mPeerAddress;
+
+ public DiscoveryFilter(
+ @DeviceType int deviceType, @Nullable String deviceName, @Nullable String peerAddress) {
+ this.mDeviceType = deviceType;
+ this.mDeviceName = deviceName;
+ this.mPeerAddress = peerAddress;
+ }
+
+ /**
+ * Returns device type.
+ *
+ * @return device type.
+ */
+ public @DeviceType int getDeviceType() {
+ return this.mDeviceType;
+ }
+
+ /**
+ * Returns device name.
+ *
+ * @return device name.
+ */
+ public @Nullable String getDeviceName() {
+ return this.mDeviceName;
+ }
+
+ /**
+ * Returns mac address of the peer device .
+ *
+ * @return mac address string.
+ */
+ public @Nullable String getPeerAddress() {
+ return this.mPeerAddress;
+ }
+
+ /** Builder for {@link DiscoverFilter} */
+ public static class Builder {
+ private @DeviceType int mDeviceType;
+ private @Nullable String mDeviceName;
+ private @Nullable String mPeerAddress;
+
+ private Builder() {}
+
+ /** Static method to create a new builder */
+ public static Builder newInstance() {
+ return new Builder();
+ }
+
+ /**
+ * Sets the device type of the DiscoveryFilter.
+ *
+ * @param deviceType of the peer device.
+ */
+ @NonNull
+ public Builder setDeviceType(@DeviceType int deviceType) {
+ mDeviceType = deviceType;
+ return this;
+ }
+
+ /**
+ * Sets the device name.
+ *
+ * @param deviceName May be null.
+ */
+ @NonNull
+ public Builder setDeviceName(String deviceName) {
+ mDeviceName = deviceName;
+ return this;
+ }
+
+ /**
+ * Sets the peer address.
+ *
+ * @param peerAddress Mac address of the peer device.
+ */
+ @NonNull
+ public Builder setPeerAddress(String peerAddress) {
+ mPeerAddress = peerAddress;
+ return this;
+ }
+
+ /** Builds the DiscoveryFilter object. */
+ @NonNull
+ public DiscoveryFilter build() {
+ return new DiscoveryFilter(this.mDeviceType, this.mDeviceName, this.mPeerAddress);
+ }
+ }
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/connectivity/EventListener.java b/remoteauth/service/java/com/android/server/remoteauth/connectivity/EventListener.java
new file mode 100644
index 0000000..d07adb1
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/connectivity/EventListener.java
@@ -0,0 +1,27 @@
+/*
+ * 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.remoteauth.connectivity;
+
+import android.annotation.NonNull;
+
+/**
+ * Listens to the events from underlying transport.
+ */
+interface EventListener {
+ /** Called when remote device is disconnected from the underlying transport. */
+ void onDisconnect(@NonNull ConnectionInfo connectionInfo);
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/connectivity/ResponseCallback.java b/remoteauth/service/java/com/android/server/remoteauth/connectivity/ResponseCallback.java
new file mode 100644
index 0000000..8a09ab3
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/connectivity/ResponseCallback.java
@@ -0,0 +1,63 @@
+/*
+ * 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.remoteauth.connectivity;
+
+import android.annotation.NonNull;
+
+/**
+ * Abstract class to expose a callback for clients to send a response to the peer device.
+ *
+ * <p>When a device receives a message on a connection, this object is constructed using the
+ * connection information of the connection and the message id from the incoming message. This
+ * object is forwarded to the clients of the connection to allow them to send a response to the peer
+ * device.
+ */
+public abstract class ResponseCallback {
+ private final long mMessageId;
+ private final ConnectionInfo mConnectionInfo;
+
+ public ResponseCallback(long messageId, @NonNull ConnectionInfo connectionInfo) {
+ mMessageId = messageId;
+ mConnectionInfo = connectionInfo;
+ }
+
+ /**
+ * Returns message id identifying the message.
+ *
+ * @return message id of this message.
+ */
+ public long getMessageId() {
+ return mMessageId;
+ }
+
+ /**
+ * Returns connection info from the response.
+ *
+ * @return connection info.
+ */
+ @NonNull
+ public ConnectionInfo getConnectionInfo() {
+ return mConnectionInfo;
+ }
+
+ /**
+ * Callback to send a response to the peer device.
+ *
+ * @param response buffer to send to the peer device.
+ */
+ public void onResponse(@NonNull byte[] response) {}
+}
diff --git a/service-t/Android.bp b/service-t/Android.bp
index c277cf6..ce5bb92 100644
--- a/service-t/Android.bp
+++ b/service-t/Android.bp
@@ -55,6 +55,7 @@
"framework-wifi",
"service-connectivity-pre-jarjar",
"service-nearby-pre-jarjar",
+ "service-thread-pre-jarjar",
"ServiceConnectivityResources",
"unsupportedappusage",
],
@@ -119,4 +120,4 @@
visibility: [
"//visibility:private",
],
-}
\ No newline at end of file
+}
diff --git a/service-t/jni/com_android_server_net_NetworkStatsService.cpp b/service-t/jni/com_android_server_net_NetworkStatsService.cpp
index ae553f0..bdbb655 100644
--- a/service-t/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/service-t/jni/com_android_server_net_NetworkStatsService.cpp
@@ -47,11 +47,9 @@
RX_PACKETS = 1,
TX_BYTES = 2,
TX_PACKETS = 3,
- TCP_RX_PACKETS = 4, // not supported, always returns UNKNOWN (-1)
- TCP_TX_PACKETS = 5, // not supported, always returns UNKNOWN (-1)
};
-static uint64_t getStatsType(Stats* stats, StatsType type) {
+static uint64_t getStatsType(StatsValue* stats, StatsType type) {
switch (type) {
case RX_BYTES:
return stats->rxBytes;
@@ -61,15 +59,13 @@
return stats->txBytes;
case TX_PACKETS:
return stats->txPackets;
- case TCP_RX_PACKETS:
- case TCP_TX_PACKETS:
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);
@@ -84,7 +80,7 @@
return UNKNOWN;
}
- Stats stats = {};
+ StatsValue stats = {};
if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
@@ -94,7 +90,7 @@
}
static jlong nativeGetIfIndexStat(JNIEnv* env, jclass clazz, jint ifindex, jint type) {
- Stats stats = {};
+ StatsValue stats = {};
if (bpfGetIfIndexStats(ifindex, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
@@ -103,7 +99,7 @@
}
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);
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
index 317c3f9..fed2979 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
@@ -40,37 +40,26 @@
using base::Result;
-// This explicitly zero-initializes the relevant Stats fields.
-void InitStats(Stats* stats) {
- stats->rxBytes = 0;
- stats->rxPackets = 0;
- stats->txBytes = 0;
- stats->txPackets = 0;
-}
-
-int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
+int bpfGetUidStatsInternal(uid_t uid, StatsValue* stats,
const BpfMap<uint32_t, StatsValue>& appUidStatsMap) {
- InitStats(stats);
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) {
- InitStats(stats);
+ *stats = {};
int64_t unknownIfaceBytesTotal = 0;
const auto processIfaceStats =
[iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal](
@@ -86,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>();
};
@@ -97,27 +83,24 @@
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, Stats* stats,
+int bpfGetIfIndexStatsInternal(uint32_t ifindex, StatsValue* stats,
const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) {
- InitStats(stats);
auto statsEntry = ifaceStatsMap.readValue(ifindex);
- if (statsEntry.ok()) {
- stats->rxPackets = statsEntry.value().rxPackets;
- stats->txPackets = statsEntry.value().txPackets;
- stats->rxBytes = statsEntry.value().rxBytes;
- stats->txBytes = statsEntry.value().txBytes;
- return 0;
+ if (!statsEntry.ok()) {
+ *stats = {};
+ return (statsEntry.error().code() == ENOENT) ? 0 : -statsEntry.error().code();
}
- return (statsEntry.error().code() == ENOENT) ? 0 : -statsEntry.error().code();
+ *stats = statsEntry.value();
+ return 0;
}
-int bpfGetIfIndexStats(int ifindex, Stats* stats) {
+int bpfGetIfIndexStats(int ifindex, StatsValue* stats) {
static BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
return bpfGetIfIndexStatsInternal(ifindex, stats, ifaceStatsMap);
}
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
index 4f85d9b..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 = {
@@ -284,7 +284,7 @@
};
EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(IFACE_INDEX1, value, BPF_ANY));
- Stats result = {};
+ StatsValue result = {};
ASSERT_EQ(0, bpfGetIfIndexStatsInternal(IFACE_INDEX1, &result, mFakeIfaceStatsMap));
expectStatsEqual(value, result);
}
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
index 0a9c012..ea068fc 100644
--- a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
@@ -56,14 +56,14 @@
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, Stats* stats,
+int bpfGetIfIndexStatsInternal(uint32_t ifindex, StatsValue* stats,
const BpfMap<uint32_t, StatsValue>& ifaceStatsMap);
// For test only
int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>& lines,
@@ -113,9 +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 bpfGetIfIndexStats(int ifindex, 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 4594f71..052019f 100644
--- a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
+++ b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
@@ -123,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 f7edbe4..eb73ee5 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -104,6 +104,7 @@
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;
@@ -143,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
@@ -168,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;
@@ -239,7 +242,8 @@
}
}
- private static class MdnsListener implements MdnsServiceBrowserListener {
+ @VisibleForTesting
+ static class MdnsListener implements MdnsServiceBrowserListener {
protected final int mClientRequestId;
protected final int mTransactionId;
@NonNull
@@ -261,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) { }
@@ -270,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) { }
@@ -296,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
@@ -318,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));
}
}
@@ -333,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
@@ -459,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;
}
}
@@ -583,7 +598,7 @@
case NsdManager.DISCOVER_SERVICES:
cInfo = getClientInfoForReply(msg);
if (cInfo != null) {
- cInfo.onDiscoverServicesFailed(
+ cInfo.onDiscoverServicesFailedImmediately(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
@@ -611,7 +626,7 @@
case NsdManager.RESOLVE_SERVICE:
cInfo = getClientInfoForReply(msg);
if (cInfo != null) {
- cInfo.onResolveServiceFailed(
+ cInfo.onResolveServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
@@ -678,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();
@@ -689,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();
}
@@ -713,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();
}
@@ -758,7 +772,7 @@
}
if (requestLimitReached(clientInfo)) {
- clientInfo.onDiscoverServicesFailed(
+ clientInfo.onDiscoverServicesFailedImmediately(
clientRequestId, NsdManager.FAILURE_MAX_LIMIT);
break;
}
@@ -773,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;
}
@@ -796,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 {
@@ -806,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;
@@ -842,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);
@@ -907,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);
@@ -942,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);
@@ -979,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";
@@ -1002,7 +1020,7 @@
+ " for service type:" + resolveServiceType);
} else {
if (clientInfo.mResolvedService != null) {
- clientInfo.onResolveServiceFailed(
+ clientInfo.onResolveServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_ALREADY_ACTIVE);
break;
}
@@ -1010,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);
}
}
@@ -1044,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);
@@ -1174,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));
@@ -1198,7 +1221,8 @@
break;
}
setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
- clientInfo.onServiceFound(clientRequestId, servInfo);
+
+ clientInfo.onServiceFound(clientRequestId, servInfo, request);
break;
}
case IMDnsEventListener.SERVICE_LOST: {
@@ -1212,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;
@@ -1268,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;
@@ -1280,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: {
@@ -1312,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);
@@ -1390,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());
@@ -1419,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
@@ -1630,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 */);
}
/**
@@ -1642,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 */);
}
/**
@@ -1658,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 */);
}
@@ -1856,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
@@ -1869,7 +1896,7 @@
if (clientRequestId < 0) return;
final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
clientInfo.onRegisterServiceFailed(clientRequestId, errorCode, transactionId,
- request.calculateRequestDurationMs());
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
}
@Override
@@ -2186,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;
}
}
@@ -2216,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;
}
@@ -2229,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);
}
}
@@ -2239,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;
}
}
@@ -2303,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,
@@ -2327,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;
}
@@ -2345,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;
@@ -2396,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) {
@@ -2412,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) {
@@ -2420,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) {
@@ -2436,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) {
@@ -2445,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,
@@ -2485,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) {
@@ -2493,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) {
@@ -2509,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/MdnsMultinetworkSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
index 097dbe0..2ef7368 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -34,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
@@ -49,10 +48,9 @@
@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;
@@ -68,8 +66,7 @@
@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;
@@ -80,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);
}
}
@@ -108,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 {
@@ -171,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) mSharedLog.v("notifyNetworkRequested: network=" + network);
callback = new InterfaceSocketCallback(socketCreationCallback);
- mRequestedNetworks.put(listener, callback);
+ mSocketRequests.put(listener, callback);
mSocketProvider.requestSocket(network, callback);
}
@@ -186,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) {
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);
}
@@ -209,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) {
- mSharedLog.e("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;
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 53a7ab9..861d8d1 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -297,9 +297,9 @@
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;
}
}
@@ -512,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);
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/Android.bp b/service/Android.bp
index a65d664..9ae3d6c 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -237,6 +237,7 @@
"service-connectivity-tiramisu-pre-jarjar",
"service-nearby-pre-jarjar",
"service-remoteauth-pre-jarjar",
+ "service-thread-pre-jarjar",
],
// The below libraries are not actually needed to build since no source is compiled
// (only combining prebuilt static_libs), but they are necessary so that R8 has the right
@@ -305,6 +306,7 @@
":service-connectivity-jarjar-gen",
":service-nearby-jarjar-gen",
":service-remoteauth-jarjar-gen",
+ ":service-thread-jarjar-gen",
],
out: ["connectivity-jarjar-rules.txt"],
visibility: ["//packages/modules/Connectivity:__subpackages__"],
@@ -374,6 +376,24 @@
visibility: ["//visibility:private"],
}
+java_genrule {
+ name: "service-thread-jarjar-gen",
+ tool_files: [
+ ":service-thread-pre-jarjar{.jar}",
+ "jarjar-excludes.txt",
+ ],
+ tools: [
+ "jarjar-rules-generator",
+ ],
+ out: ["service_thread_jarjar_rules.txt"],
+ cmd: "$(location jarjar-rules-generator) " +
+ "$(location :service-thread-pre-jarjar{.jar}) " +
+ "--prefix com.android.server.thread " +
+ "--excludes $(location jarjar-excludes.txt) " +
+ "--output $(out)",
+ visibility: ["//visibility:private"],
+}
+
genrule {
name: "statslog-connectivity-java-gen",
tools: ["stats-log-api-gen"],
diff --git a/service/native/TrafficController.cpp b/service/native/TrafficController.cpp
index 8f6df21..3828389 100644
--- a/service/native/TrafficController.cpp
+++ b/service/native/TrafficController.cpp
@@ -576,53 +576,12 @@
}
}
-std::string getMapStatus(const base::unique_fd& map_fd, const char* path) {
- if (map_fd.get() < 0) {
- return StringPrintf("map fd lost");
- }
- if (access(path, F_OK) != 0) {
- return StringPrintf("map not pinned to location: %s", path);
- }
- return StringPrintf("OK");
-}
-
-// NOLINTNEXTLINE(google-runtime-references): grandfathered pass by non-const reference
-void dumpBpfMap(const std::string& mapName, DumpWriter& dw, const std::string& header) {
- dw.blankline();
- dw.println("%s:", mapName.c_str());
- if (!header.empty()) {
- dw.println(header);
- }
-}
-
void TrafficController::dump(int fd, bool verbose __unused) {
std::lock_guard guard(mMutex);
DumpWriter dw(fd);
ScopedIndent indentTop(dw);
dw.println("TrafficController");
-
- ScopedIndent indentPreBpfModule(dw);
-
- dw.blankline();
- dw.println("mCookieTagMap status: %s",
- getMapStatus(mCookieTagMap.getMap(), COOKIE_TAG_MAP_PATH).c_str());
- dw.println("mUidCounterSetMap status: %s",
- getMapStatus(mUidCounterSetMap.getMap(), UID_COUNTERSET_MAP_PATH).c_str());
- dw.println("mAppUidStatsMap status: %s",
- getMapStatus(mAppUidStatsMap.getMap(), APP_UID_STATS_MAP_PATH).c_str());
- dw.println("mStatsMapA status: %s",
- getMapStatus(mStatsMapA.getMap(), STATS_MAP_A_PATH).c_str());
- dw.println("mStatsMapB status: %s",
- getMapStatus(mStatsMapB.getMap(), STATS_MAP_B_PATH).c_str());
- dw.println("mIfaceIndexNameMap status: %s",
- getMapStatus(mIfaceIndexNameMap.getMap(), IFACE_INDEX_NAME_MAP_PATH).c_str());
- dw.println("mIfaceStatsMap status: %s",
- getMapStatus(mIfaceStatsMap.getMap(), IFACE_STATS_MAP_PATH).c_str());
- dw.println("mConfigurationMap status: %s",
- getMapStatus(mConfigurationMap.getMap(), CONFIGURATION_MAP_PATH).c_str());
- dw.println("mUidOwnerMap status: %s",
- getMapStatus(mUidOwnerMap.getMap(), UID_OWNER_MAP_PATH).c_str());
}
} // namespace net
diff --git a/service/native/TrafficControllerTest.cpp b/service/native/TrafficControllerTest.cpp
index 57f32af..99e9831 100644
--- a/service/native/TrafficControllerTest.cpp
+++ b/service/native/TrafficControllerTest.cpp
@@ -269,109 +269,6 @@
}
return ret;
}
-
- Status dump(bool verbose, std::vector<std::string>& outputLines) {
- if (!outputLines.empty()) return statusFromErrno(EUCLEAN, "Output buffer is not empty");
-
- android::base::unique_fd localFd, remoteFd;
- if (!Pipe(&localFd, &remoteFd)) return statusFromErrno(errno, "Failed on pipe");
-
- // dump() blocks until another thread has consumed all its output.
- std::thread dumpThread =
- std::thread([this, remoteFd{std::move(remoteFd)}, verbose]() {
- mTc.dump(remoteFd, verbose);
- });
-
- std::string dumpContent;
- if (!android::base::ReadFdToString(localFd.get(), &dumpContent)) {
- return statusFromErrno(errno, "Failed to read dump results from fd");
- }
- dumpThread.join();
-
- std::stringstream dumpStream(std::move(dumpContent));
- std::string line;
- while (std::getline(dumpStream, line)) {
- outputLines.push_back(line);
- }
-
- return netdutils::status::ok;
- }
-
- // Strings in the |expect| must exist in dump results in order. But no need to be consecutive.
- bool expectDumpsysContains(std::vector<std::string>& expect) {
- if (expect.empty()) return false;
-
- std::vector<std::string> output;
- Status result = dump(true, output);
- if (!isOk(result)) {
- GTEST_LOG_(ERROR) << "TrafficController dump failed: " << netdutils::toString(result);
- return false;
- }
-
- int matched = 0;
- auto it = expect.begin();
- for (const auto& line : output) {
- if (it == expect.end()) break;
- if (std::string::npos != line.find(*it)) {
- matched++;
- ++it;
- }
- }
-
- if (matched != expect.size()) {
- // dump results for debugging
- for (const auto& o : output) LOG(INFO) << "output: " << o;
- for (const auto& e : expect) LOG(INFO) << "expect: " << e;
- return false;
- }
- return true;
- }
-
- // Once called, the maps of TrafficController can't recover to valid maps which initialized
- // in SetUp().
- void makeTrafficControllerMapsInvalid() {
- constexpr char INVALID_PATH[] = "invalid";
-
- mFakeCookieTagMap.init(INVALID_PATH);
- mTc.mCookieTagMap = mFakeCookieTagMap;
- ASSERT_INVALID(mTc.mCookieTagMap);
-
- mFakeAppUidStatsMap.init(INVALID_PATH);
- mTc.mAppUidStatsMap = mFakeAppUidStatsMap;
- ASSERT_INVALID(mTc.mAppUidStatsMap);
-
- mFakeStatsMapA.init(INVALID_PATH);
- mTc.mStatsMapA = mFakeStatsMapA;
- ASSERT_INVALID(mTc.mStatsMapA);
-
- mFakeStatsMapB.init(INVALID_PATH);
- mTc.mStatsMapB = mFakeStatsMapB;
- ASSERT_INVALID(mTc.mStatsMapB);
-
- mFakeIfaceStatsMap.init(INVALID_PATH);
- mTc.mIfaceStatsMap = mFakeIfaceStatsMap;
- ASSERT_INVALID(mTc.mIfaceStatsMap);
-
- mFakeConfigurationMap.init(INVALID_PATH);
- mTc.mConfigurationMap = mFakeConfigurationMap;
- ASSERT_INVALID(mTc.mConfigurationMap);
-
- mFakeUidOwnerMap.init(INVALID_PATH);
- mTc.mUidOwnerMap = mFakeUidOwnerMap;
- ASSERT_INVALID(mTc.mUidOwnerMap);
-
- mFakeUidPermissionMap.init(INVALID_PATH);
- mTc.mUidPermissionMap = mFakeUidPermissionMap;
- ASSERT_INVALID(mTc.mUidPermissionMap);
-
- mFakeUidCounterSetMap.init(INVALID_PATH);
- mTc.mUidCounterSetMap = mFakeUidCounterSetMap;
- ASSERT_INVALID(mTc.mUidCounterSetMap);
-
- mFakeIfaceIndexNameMap.init(INVALID_PATH);
- mTc.mIfaceIndexNameMap = mFakeIfaceIndexNameMap;
- ASSERT_INVALID(mTc.mIfaceIndexNameMap);
- }
};
TEST_F(TrafficControllerTest, TestUpdateOwnerMapEntry) {
diff --git a/service/native/include/TrafficController.h b/service/native/include/TrafficController.h
index cb6c836..d610d25 100644
--- a/service/native/include/TrafficController.h
+++ b/service/native/include/TrafficController.h
@@ -33,8 +33,6 @@
class TrafficController {
public:
- static constexpr char DUMP_KEYWORD[] = "trafficcontroller";
-
/*
* Initialize the whole controller
*/
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index f08ffc3..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);
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index d5d7b5a..04d0b93 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -1412,10 +1412,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 */);
}
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 4f2909c..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,8 +218,8 @@
// 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) {
@@ -700,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) {
@@ -969,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 ae70767..feba821 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -993,7 +993,7 @@
*/
public boolean isAddressTranslationEnabled(@NonNull Context context) {
return DeviceConfigUtils.isFeatureSupported(context, FEATURE_CLAT_ADDRESS_TRANSLATE)
- && !DeviceConfigUtils.isTetheringFeatureForceDisabled(
+ && DeviceConfigUtils.isTetheringFeatureNotChickenedOut(
CONFIG_DISABLE_CLAT_ADDRESS_TRANSLATE);
}
}
diff --git a/tests/common/java/android/net/KeepalivePacketDataTest.kt b/tests/common/java/android/net/KeepalivePacketDataTest.kt
index f464ec6..403d6b5 100644
--- a/tests/common/java/android/net/KeepalivePacketDataTest.kt
+++ b/tests/common/java/android/net/KeepalivePacketDataTest.kt
@@ -22,6 +22,7 @@
import androidx.test.runner.AndroidJUnit4
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.NonNullTestUtils
import java.net.InetAddress
import java.util.Arrays
import org.junit.Assert.assertEquals
@@ -55,43 +56,42 @@
dstAddress: InetAddress? = TEST_DST_ADDRV4,
dstPort: Int = TEST_DST_PORT,
data: ByteArray = TESTBYTES
- ) : KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data)
+ ) : KeepalivePacketData(NonNullTestUtils.nullUnsafe(srcAddress), srcPort,
+ NonNullTestUtils.nullUnsafe(dstAddress), dstPort, data)
@Test
@IgnoreUpTo(Build.VERSION_CODES.Q)
fun testConstructor() {
- var data: TestKeepalivePacketData
-
try {
- data = TestKeepalivePacketData(srcAddress = null)
+ TestKeepalivePacketData(srcAddress = null)
fail("Null src address should cause exception")
} catch (e: InvalidPacketException) {
assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
}
try {
- data = TestKeepalivePacketData(dstAddress = null)
+ TestKeepalivePacketData(dstAddress = null)
fail("Null dst address should cause exception")
} catch (e: InvalidPacketException) {
assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
}
try {
- data = TestKeepalivePacketData(dstAddress = TEST_ADDRV6)
+ TestKeepalivePacketData(dstAddress = TEST_ADDRV6)
fail("Ip family mismatched should cause exception")
} catch (e: InvalidPacketException) {
assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
}
try {
- data = TestKeepalivePacketData(srcPort = INVALID_PORT)
+ TestKeepalivePacketData(srcPort = INVALID_PORT)
fail("Invalid srcPort should cause exception")
} catch (e: InvalidPacketException) {
assertEquals(e.error, ERROR_INVALID_PORT)
}
try {
- data = TestKeepalivePacketData(dstPort = INVALID_PORT)
+ TestKeepalivePacketData(dstPort = INVALID_PORT)
fail("Invalid dstPort should cause exception")
} catch (e: InvalidPacketException) {
assertEquals(e.error, ERROR_INVALID_PORT)
@@ -117,4 +117,4 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.Q)
fun testPacket() = assertTrue(Arrays.equals(TESTBYTES, TestKeepalivePacketData().packet))
-}
\ No newline at end of file
+}
diff --git a/tests/common/java/android/net/LinkPropertiesTest.java b/tests/common/java/android/net/LinkPropertiesTest.java
index 09f5d6e..d2e7c99 100644
--- a/tests/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/common/java/android/net/LinkPropertiesTest.java
@@ -1260,7 +1260,7 @@
assertFalse(lp.hasIpv4UnreachableDefaultRoute());
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
@CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden when targeting T+")
@EnableCompatChanges({ConnectivityCompatChanges.EXCLUDED_ROUTES})
public void testHasExcludeRoute() {
@@ -1273,7 +1273,7 @@
assertTrue(lp.hasExcludeRoute());
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
@CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden when targeting T+")
@EnableCompatChanges({ConnectivityCompatChanges.EXCLUDED_ROUTES})
public void testRouteAddWithSameKey() throws Exception {
@@ -1347,14 +1347,14 @@
assertExcludeRoutesVisible();
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
@CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden when targeting T+")
@EnableCompatChanges({ConnectivityCompatChanges.EXCLUDED_ROUTES})
public void testExcludedRoutesEnabledByCompatChange() {
assertExcludeRoutesVisible();
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
@CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden when targeting T+")
@DisableCompatChanges({ConnectivityCompatChanges.EXCLUDED_ROUTES})
public void testExcludedRoutesDisabledByCompatChange() {
diff --git a/tests/common/java/android/net/NetworkProviderTest.kt b/tests/common/java/android/net/NetworkProviderTest.kt
index fcbb0dd..c6a7346 100644
--- a/tests/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/common/java/android/net/NetworkProviderTest.kt
@@ -67,7 +67,7 @@
class NetworkProviderTest {
@Rule @JvmField
val mIgnoreRule = DevSdkIgnoreRule()
- private val mCm = context.getSystemService(ConnectivityManager::class.java)
+ private val mCm = context.getSystemService(ConnectivityManager::class.java)!!
private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
@Before
diff --git a/tests/cts/OWNERS b/tests/cts/OWNERS
index 286f9c8..cb4ca59 100644
--- a/tests/cts/OWNERS
+++ b/tests/cts/OWNERS
@@ -5,3 +5,8 @@
# IPsec
per-file **IpSec* = benedictwong@google.com, nharold@google.com
+
+# For incremental changes on EthernetManagerTest to increase coverage for existing behavior and for
+# testing bug fixes.
+per-file net/src/android/net/cts/EthernetManagerTest.kt = prohr@google.com #{LAST_RESORT_SUGGESTION}
+
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/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index 1d79806..99222dd 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -95,7 +95,7 @@
@RunWith(AndroidJUnit4::class)
class CaptivePortalTest {
private val context: android.content.Context by lazy { getInstrumentation().context }
- private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
+ private val cm by lazy { context.getSystemService(ConnectivityManager::class.java)!! }
private val pm by lazy { context.packageManager }
private val utils by lazy { CtsNetUtils(context) }
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/DscpPolicyTest.kt b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
index db13c49..f73134a 100644
--- a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
+++ b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
@@ -125,7 +125,7 @@
private val TEST_TARGET_MAC_ADDR = MacAddress.fromString("12:34:56:78:9a:bc")
private val realContext = InstrumentationRegistry.getContext()
- private val cm = realContext.getSystemService(ConnectivityManager::class.java)
+ private val cm = realContext.getSystemService(ConnectivityManager::class.java)!!
private val agentsToCleanUp = mutableListOf<NetworkAgent>()
private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
@@ -160,7 +160,7 @@
assumeTrue(kernelIsAtLeast(5, 15))
runAsShell(MANAGE_TEST_NETWORKS) {
- val tnm = realContext.getSystemService(TestNetworkManager::class.java)
+ val tnm = realContext.getSystemService(TestNetworkManager::class.java)!!
// Only statically configure the IPv4 address; for IPv6, use the SLAAC generated
// address.
@@ -306,7 +306,7 @@
val socket = Os.socket(if (sendV6) AF_INET6 else AF_INET, SOCK_DGRAM or SOCK_NONBLOCK,
IPPROTO_UDP)
- agent.network.bindSocket(socket)
+ checkNotNull(agent.network).bindSocket(socket)
val originalPacket = testPacket.readAsArray()
Os.sendto(socket, originalPacket, 0 /* bytesOffset */, originalPacket.size, 0 /* flags */,
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 732a42b..3146b41 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -92,7 +92,6 @@
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
-import kotlin.test.fail
import org.junit.After
import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
@@ -135,8 +134,8 @@
class EthernetManagerTest {
private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
- private val em by lazy { context.getSystemService(EthernetManager::class.java) }
- private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
+ private val em by lazy { context.getSystemService(EthernetManager::class.java)!! }
+ private val cm by lazy { context.getSystemService(ConnectivityManager::class.java)!! }
private val handler by lazy { Handler(Looper.getMainLooper()) }
private val ifaceListener = EthernetStateListener()
@@ -160,7 +159,7 @@
init {
tnm = runAsShell(MANAGE_TEST_NETWORKS) {
- context.getSystemService(TestNetworkManager::class.java)
+ context.getSystemService(TestNetworkManager::class.java)!!
}
tapInterface = runAsShell(MANAGE_TEST_NETWORKS) {
// Configuring a tun/tap interface always enables the carrier. If hasCarrier is
@@ -254,7 +253,7 @@
}
fun <T : CallbackEntry> expectCallback(expected: T): T {
- val event = pollOrThrow()
+ val event = events.poll(TIMEOUT_MS)
assertEquals(expected, event)
return event as T
}
@@ -267,24 +266,21 @@
expectCallback(EthernetStateChanged(state))
}
- fun createChangeEvent(iface: String, state: Int, role: Int) =
+ private fun createChangeEvent(iface: String, state: Int, role: Int) =
InterfaceStateChanged(iface, state, role,
if (state != STATE_ABSENT) DEFAULT_IP_CONFIGURATION else null)
- fun pollOrThrow(): CallbackEntry {
- return events.poll(TIMEOUT_MS) ?: fail("Did not receive callback after ${TIMEOUT_MS}ms")
+ fun eventuallyExpect(expected: CallbackEntry) {
+ val cb = events.poll(TIMEOUT_MS) { it == expected }
+ assertNotNull(cb, "Never received expected $expected. Received: ${events.backtrace()}")
}
- fun eventuallyExpect(expected: CallbackEntry) = events.poll(TIMEOUT_MS) { it == expected }
-
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 +648,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)
@@ -668,6 +663,20 @@
}
@Test
+ fun testCallbacks_afterRemovingServerModeInterface() {
+ // do not run this test if an interface that can be used for tethering already exists.
+ assumeNoInterfaceForTetheringAvailable()
+
+ val iface = createInterface()
+ requestTetheredInterface().expectOnAvailable()
+ removeInterface(iface)
+
+ val listener = EthernetStateListener()
+ addInterfaceStateListener(listener)
+ listener.assertNoCallback()
+ }
+
+ @Test
fun testGetInterfaceList() {
// Create two test interfaces and check the return list contains the interface names.
val iface1 = createInterface()
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/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 98ea224..5937655 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -257,7 +257,7 @@
callback: TestableNetworkCallback,
handler: Handler
) {
- mCM!!.registerBestMatchingNetworkCallback(request, callback, handler)
+ mCM.registerBestMatchingNetworkCallback(request, callback, handler)
callbacksToCleanUp.add(callback)
}
@@ -394,8 +394,8 @@
.setLegacyExtraInfo(legacyExtraInfo).build()
val (agent, callback) = createConnectedNetworkAgent(initialConfig = config)
val networkInfo = mCM.getNetworkInfo(agent.network)
- assertEquals(subtypeLTE, networkInfo.getSubtype())
- assertEquals(subtypeNameLTE, networkInfo.getSubtypeName())
+ assertEquals(subtypeLTE, networkInfo?.getSubtype())
+ assertEquals(subtypeNameLTE, networkInfo?.getSubtypeName())
assertEquals(legacyExtraInfo, config.getLegacyExtraInfo())
}
@@ -417,8 +417,8 @@
val nc = NetworkCapabilities(agent.nc)
nc.addCapability(NET_CAPABILITY_NOT_METERED)
agent.sendNetworkCapabilities(nc)
- callback.expectCaps(agent.network) { it.hasCapability(NET_CAPABILITY_NOT_METERED) }
- val networkInfo = mCM.getNetworkInfo(agent.network)
+ callback.expectCaps(agent.network!!) { it.hasCapability(NET_CAPABILITY_NOT_METERED) }
+ val networkInfo = mCM.getNetworkInfo(agent.network!!)!!
assertEquals(subtypeUMTS, networkInfo.getSubtype())
assertEquals(subtypeNameUMTS, networkInfo.getSubtypeName())
}
@@ -631,6 +631,7 @@
val defaultNetwork = mCM.activeNetwork
assertNotNull(defaultNetwork)
val defaultNetworkCapabilities = mCM.getNetworkCapabilities(defaultNetwork)
+ assertNotNull(defaultNetworkCapabilities)
val defaultNetworkTransports = defaultNetworkCapabilities.transportTypes
val agent = createNetworkAgent(initialNc = nc)
@@ -671,7 +672,7 @@
// This is not very accurate because the test does not control the capabilities of the
// underlying networks, and because not congested, not roaming, and not suspended are the
// default anyway. It's still useful as an extra check though.
- vpnNc = mCM.getNetworkCapabilities(agent.network!!)
+ vpnNc = mCM.getNetworkCapabilities(agent.network!!)!!
for (cap in listOf(NET_CAPABILITY_NOT_CONGESTED,
NET_CAPABILITY_NOT_ROAMING,
NET_CAPABILITY_NOT_SUSPENDED)) {
@@ -1041,8 +1042,8 @@
}
fun QosSocketInfo(agent: NetworkAgent, socket: Closeable) = when (socket) {
- is Socket -> QosSocketInfo(agent.network, socket)
- is DatagramSocket -> QosSocketInfo(agent.network, socket)
+ is Socket -> QosSocketInfo(checkNotNull(agent.network), socket)
+ is DatagramSocket -> QosSocketInfo(checkNotNull(agent.network), socket)
else -> fail("unexpected socket type")
}
@@ -1405,8 +1406,8 @@
val nc = makeTestNetworkCapabilities(ifName, transports).also {
if (transports.contains(TRANSPORT_VPN)) {
val sessionId = "NetworkAgentTest-${Process.myPid()}"
- it.transportInfo = VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, sessionId,
- /*bypassable=*/ false, /*longLivedTcpConnectionsExpensive=*/ false)
+ it.setTransportInfo(VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, sessionId,
+ /*bypassable=*/ false, /*longLivedTcpConnectionsExpensive=*/ false))
it.underlyingNetworks = listOf()
}
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt b/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt
index d6120f8..499d97f 100644
--- a/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt
@@ -16,12 +16,12 @@
package android.net.cts
-import android.os.Build
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkInfo
import android.net.NetworkInfo.DetailedState
import android.net.NetworkInfo.State
+import android.os.Build
import android.telephony.TelephonyManager
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
@@ -29,16 +29,17 @@
import com.android.modules.utils.build.SdkLevel
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.NonNullTestUtils
+import kotlin.reflect.jvm.isAccessible
+import kotlin.test.assertFails
+import kotlin.test.assertFailsWith
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Rule
-import org.junit.runner.RunWith
import org.junit.Test
-import kotlin.reflect.jvm.isAccessible
-import kotlin.test.assertFails
-import kotlin.test.assertFailsWith
+import org.junit.runner.RunWith
const val TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE
const val TYPE_WIFI = ConnectivityManager.TYPE_WIFI
@@ -106,10 +107,12 @@
}
if (SdkLevel.isAtLeastT()) {
- assertFailsWith<NullPointerException> { NetworkInfo(null) }
+ assertFailsWith<NullPointerException> {
+ NetworkInfo(NonNullTestUtils.nullUnsafe<NetworkInfo>(null))
+ }
} else {
// Doesn't immediately crash on S-
- NetworkInfo(null)
+ NetworkInfo(NonNullTestUtils.nullUnsafe<NetworkInfo>(null))
}
}
@@ -134,10 +137,11 @@
val incorrectDetailedState = constructor.newInstance("any", 200) as DetailedState
if (SdkLevel.isAtLeastT()) {
assertFailsWith<NullPointerException> {
- NetworkInfo(null)
+ NetworkInfo(NonNullTestUtils.nullUnsafe<NetworkInfo>(null))
}
assertFailsWith<NullPointerException> {
- networkInfo.setDetailedState(null, "reason", "extraInfo")
+ networkInfo.setDetailedState(NonNullTestUtils.nullUnsafe<DetailedState>(null),
+ "reason", "extraInfo")
}
// This actually throws ArrayOutOfBoundsException because of the implementation of
// EnumMap, but that's an implementation detail so accept any crash.
@@ -146,8 +150,9 @@
}
} else {
// Doesn't immediately crash on S-
- NetworkInfo(null)
- networkInfo.setDetailedState(null, "reason", "extraInfo")
+ NetworkInfo(NonNullTestUtils.nullUnsafe<NetworkInfo>(null))
+ networkInfo.setDetailedState(NonNullTestUtils.nullUnsafe<DetailedState>(null),
+ "reason", "extraInfo")
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
index 2704dd3..e660b1e 100644
--- a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
@@ -67,7 +67,7 @@
@RunWith(DevSdkIgnoreRunner::class)
class NetworkScoreTest {
private val TAG = javaClass.simpleName
- private val mCm = testContext.getSystemService(ConnectivityManager::class.java)
+ private val mCm = testContext.getSystemService(ConnectivityManager::class.java)!!
private val mHandlerThread = HandlerThread("$TAG handler thread")
private val mHandler by lazy { Handler(mHandlerThread.looper) }
private val agentsToCleanUp = Collections.synchronizedList(mutableListOf<NetworkAgent>())
@@ -111,7 +111,7 @@
// made for ConnectivityServiceTest.
// TODO : have TestNetworkCallback work for NetworkAgent too and remove this class.
private class AgentWrapper(val agent: NetworkAgent) : HasNetwork {
- override val network = agent.network
+ override val network = checkNotNull(agent.network)
fun sendNetworkScore(s: NetworkScore) = agent.sendNetworkScore(s)
}
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index e4ee8de..49a6ef1 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -25,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
@@ -73,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
@@ -82,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
@@ -103,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
@@ -133,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)
@@ -145,9 +159,9 @@
val ignoreRule = DevSdkIgnoreRule()
private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
- private val nsdManager by lazy { context.getSystemService(NsdManager::class.java) }
+ private val nsdManager by lazy { context.getSystemService(NsdManager::class.java)!! }
- private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
+ private val cm by lazy { context.getSystemService(ConnectivityManager::class.java)!! }
private val serviceName = "NsdTest%09d".format(Random().nextInt(1_000_000_000))
private val serviceType = "_nmt%09d._tcp".format(Random().nextInt(1_000_000_000))
private val handlerThread = HandlerThread(NsdManagerTest::class.java.simpleName)
@@ -194,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
@@ -383,7 +397,7 @@
}
private fun createTestNetwork(): TestTapNetwork {
- val tnm = context.getSystemService(TestNetworkManager::class.java)
+ val tnm = context.getSystemService(TestNetworkManager::class.java)!!
val iface = tnm.createTapInterface()
val cb = TestableNetworkCallback()
val testNetworkSpecifier = TestNetworkSpecifier(iface.interfaceName)
@@ -411,7 +425,6 @@
val lp = LinkProperties().apply {
interfaceName = ifaceName
}
-
val agent = TestableNetworkAgent(context, handlerThread.looper,
NetworkCapabilities().apply {
removeCapability(NET_CAPABILITY_TRUSTED)
@@ -1144,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.
*/
@@ -1169,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/ProxyTest.kt b/tests/cts/net/src/android/net/cts/ProxyTest.kt
index a661b26..872dbb9 100644
--- a/tests/cts/net/src/android/net/cts/ProxyTest.kt
+++ b/tests/cts/net/src/android/net/cts/ProxyTest.kt
@@ -70,7 +70,7 @@
private fun getDefaultProxy(): ProxyInfo? {
return InstrumentationRegistry.getInstrumentation().context
- .getSystemService(ConnectivityManager::class.java)
+ .getSystemService(ConnectivityManager::class.java)!!
.getDefaultProxy()
}
@@ -100,4 +100,4 @@
Proxy.setHttpProxyConfiguration(original)
}
}
-}
\ No newline at end of file
+}
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/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 67e1296..e264b55 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -56,6 +56,7 @@
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.TestableNetworkCallback
import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.test.fail
import org.junit.After
@@ -291,6 +292,7 @@
val capportData = testCb.expect<LinkPropertiesChanged>(na, TEST_TIMEOUT_MS) {
it.lp.captivePortalData != null
}.lp.captivePortalData
+ assertNotNull(capportData)
assertTrue(capportData.isCaptive)
assertEquals(Uri.parse("https://login.capport.android.com"), capportData.userPortalUrl)
assertEquals(Uri.parse("https://venueinfo.capport.android.com"), capportData.venueInfoUrl)
diff --git a/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt b/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
index e206313..467708a 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
@@ -20,9 +20,9 @@
import android.os.Parcelable
data class HttpResponse(
- val requestUrl: String,
+ val requestUrl: String?,
val responseCode: Int,
- val content: String = "",
+ val content: String? = "",
val redirectUrl: String? = null
) : Parcelable {
constructor(p: Parcel): this(p.readString(), p.readInt(), p.readString(), p.readString())
@@ -46,4 +46,4 @@
override fun createFromParcel(source: Parcel) = HttpResponse(source)
override fun newArray(size: Int) = arrayOfNulls<HttpResponse?>(size)
}
-}
\ No newline at end of file
+}
diff --git a/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
index e807952..104d063 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
@@ -70,7 +70,7 @@
* request is seen, the test will fail.
*/
override fun addHttpResponse(response: HttpResponse) {
- httpResponses.getValue(response.requestUrl).add(response)
+ httpResponses.getValue(checkNotNull(response.requestUrl)).add(response)
}
/**
@@ -81,4 +81,4 @@
return ArrayList(httpRequestUrls)
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
index 361c968..7e227c4 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -69,7 +69,8 @@
url: URL,
private val response: HttpResponse
) : HttpURLConnection(url) {
- private val responseBytes = response.content.toByteArray(StandardCharsets.UTF_8)
+ private val responseBytes = checkNotNull(response.content)
+ .toByteArray(StandardCharsets.UTF_8)
override fun getResponseCode() = response.responseCode
override fun getContentLengthLong() = responseBytes.size.toLong()
override fun getHeaderField(field: String): String? {
diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt
index 2f6c76b..a8414ca 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -49,6 +49,7 @@
import android.telephony.TelephonyManager
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.NonNullTestUtils
import com.android.testutils.assertParcelSane
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@@ -221,12 +222,13 @@
@DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
@Test
fun testBuildTemplateMobileAll_nullSubscriberId() {
- val templateMobileAllWithNullImsi = buildTemplateMobileAll(null)
+ val templateMobileAllWithNullImsi =
+ buildTemplateMobileAll(NonNullTestUtils.nullUnsafe<String>(null))
val setWithNull = HashSet<String?>().apply {
add(null)
}
val templateFromBuilder = NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES)
- .setSubscriberIds(setWithNull).build()
+ .setSubscriberIds(setWithNull).build()
assertEquals(templateFromBuilder, templateMobileAllWithNullImsi)
}
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 3688d83..708697c 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -2301,7 +2301,7 @@
}
@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;
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index f778075..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
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index b69b042..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;
@@ -359,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);
@@ -368,6 +367,10 @@
@After
public void teardown() throws Exception {
TestKeepaliveInfo.closeAllSockets();
+ if (mHandlerThread != null) {
+ mHandlerThread.quitSafely();
+ mHandlerThread.join();
+ }
}
private final class AOOTestHandler extends Handler {
diff --git a/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt b/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
index 3520c5b..0a3822a 100644
--- a/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
@@ -74,7 +74,7 @@
private val TAG = this::class.simpleName
- private var wtfHandler: Log.TerribleFailureHandler? = null
+ private lateinit var wtfHandler: Log.TerribleFailureHandler
@Before
fun setUp() {
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 8dcfffa..5bde31a 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -100,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;
@@ -214,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 =
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketTest.kt
index f88da1f..b667e5f 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketTest.kt
@@ -59,12 +59,12 @@
}
assertEquals(InetAddresses.parseNumericAddress("192.0.2.123"),
- (packet.authorityRecords[0] as MdnsInetAddressRecord).inet4Address)
+ (packet.authorityRecords[0] as MdnsInetAddressRecord).inet4Address!!)
assertEquals(InetAddresses.parseNumericAddress("2001:db8::123"),
- (packet.authorityRecords[1] as MdnsInetAddressRecord).inet6Address)
+ (packet.authorityRecords[1] as MdnsInetAddressRecord).inet6Address!!)
assertEquals(InetAddresses.parseNumericAddress("2001:db8::456"),
- (packet.authorityRecords[2] as MdnsInetAddressRecord).inet6Address)
+ (packet.authorityRecords[2] as MdnsInetAddressRecord).inet6Address!!)
assertEquals(InetAddresses.parseNumericAddress("2001:db8::789"),
- (packet.authorityRecords[3] as MdnsInetAddressRecord).inet6Address)
+ (packet.authorityRecords[3] as MdnsInetAddressRecord).inet6Address!!)
}
}
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/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index c0b74e1..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;
@@ -157,9 +159,9 @@
eq(TETHERED_IFACE_NAME), any());
doReturn(789).when(mDeps).getNetworkInterfaceIndexByName(
eq(WIFI_P2P_IFACE_NAME), any());
- final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
- thread.start();
- mHandler = new Handler(thread.getLooper());
+ 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/thread/OWNERS b/thread/OWNERS
new file mode 100644
index 0000000..c93ec4d
--- /dev/null
+++ b/thread/OWNERS
@@ -0,0 +1,11 @@
+# Bug component: 1203089
+
+# Primary reviewers
+wgtdkp@google.com
+handaw@google.com
+sunytt@google.com
+
+# Secondary reviewers
+jonhui@google.com
+xyk@google.com
+zhanglongxia@google.com
diff --git a/thread/README.md b/thread/README.md
new file mode 100644
index 0000000..f50e0cd
--- /dev/null
+++ b/thread/README.md
@@ -0,0 +1,3 @@
+# Thread
+
+Bring the [Thread](https://www.threadgroup.org/) networking protocol to Android.
diff --git a/thread/framework/Android.bp b/thread/framework/Android.bp
new file mode 100644
index 0000000..cc598d8
--- /dev/null
+++ b/thread/framework/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "framework-thread-sources",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ path: "java",
+ visibility: [
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+}
diff --git a/thread/service/Android.bp b/thread/service/Android.bp
new file mode 100644
index 0000000..fda206a
--- /dev/null
+++ b/thread/service/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "service-thread-sources",
+ srcs: ["java/**/*.java"],
+}
+
+java_library {
+ name: "service-thread-pre-jarjar",
+ defaults: ["framework-system-server-module-defaults"],
+ sdk_version: "system_server_current",
+ // This is included in service-connectivity which is 30+
+ // TODO (b/293613362): allow APEXes to have service jars with higher min_sdk than the APEX
+ // (service-connectivity is only used on 31+) and use 31 here
+ min_sdk_version: "30",
+ srcs: [":service-thread-sources"],
+ apex_available: ["com.android.tethering"],
+}