Merge "Increase testReadWriteProgram() timeout to 15 minutes" into main
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
index 576e806..1e36676 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
@@ -76,7 +76,7 @@
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public class NearbyManagerTest {
- @ClassRule static final EnableBluetoothRule sEnableBluetooth = new EnableBluetoothRule();
+ @ClassRule public static final EnableBluetoothRule sEnableBluetooth = new EnableBluetoothRule();
private static final byte[] SALT = new byte[]{1, 2};
private static final byte[] SECRET_ID = new byte[]{1, 2, 3, 4};
diff --git a/service/ServiceConnectivityResources/res/values/config_thread.xml b/service/ServiceConnectivityResources/res/values/config_thread.xml
index 4783f2b..02a9ce6 100644
--- a/service/ServiceConnectivityResources/res/values/config_thread.xml
+++ b/service/ServiceConnectivityResources/res/values/config_thread.xml
@@ -53,4 +53,10 @@
UTF-8 bytes.
-->
<string translatable="false" name="config_thread_model_name">Thread Border Router</string>
+
+ <!-- Whether the Thread network will be managed by the Google Home ecosystem. When this value
+ is set, a TXT entry "vgh=0" or "vgh=1" will be added to the "_mehscop._udp" mDNS service
+ respectively (The TXT value is a string).
+ -->
+ <bool name="config_thread_managed_by_google_home">false</bool>
</resources>
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index d886182..f333dae 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -683,13 +683,25 @@
throw new IOException("Failed to start clat ", e);
} finally {
if (tunFd != null) {
- tunFd.close();
+ try {
+ tunFd.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Fail to close tun file descriptor " + e);
+ }
}
if (readSock6 != null) {
- readSock6.close();
+ try {
+ readSock6.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Fail to close read socket " + e);
+ }
}
if (writeSock6 != null) {
- writeSock6.close();
+ try {
+ writeSock6.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Fail to close write socket " + e);
+ }
}
}
}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java
index da633c0..00f67f4 100644
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java
@@ -16,7 +16,7 @@
package com.android.cts.netpolicy.hostside;
-import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static org.junit.Assume.assumeTrue;
@@ -46,14 +46,15 @@
public final void tearDown() throws Exception {
super.tearDown();
+ stopApp();
removePowerSaveModeWhitelist(TEST_APP2_PKG);
removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
}
@Test
public void testFgsNetworkAccess() throws Exception {
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ SystemClock.sleep(mProcessStateTransitionShortDelayMs);
assertNetworkAccess(false, null);
launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
@@ -61,8 +62,8 @@
@Test
public void testActivityNetworkAccess() throws Exception {
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ SystemClock.sleep(mProcessStateTransitionShortDelayMs);
assertNetworkAccess(false, null);
launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
@@ -70,23 +71,23 @@
@Test
public void testBackgroundNetworkAccess_inFullAllowlist() throws Exception {
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ SystemClock.sleep(mProcessStateTransitionShortDelayMs);
assertNetworkAccess(false, null);
addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
assertNetworkAccess(true, null);
}
@Test
public void testBackgroundNetworkAccess_inExceptIdleAllowlist() throws Exception {
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ SystemClock.sleep(mProcessStateTransitionShortDelayMs);
assertNetworkAccess(false, null);
addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
assertNetworkAccess(true, null);
}
}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index d0203c5..0f5f58c 100644
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -96,7 +96,12 @@
protected static final String TEST_PKG = "com.android.cts.netpolicy.hostside";
protected static final String TEST_APP2_PKG = "com.android.cts.netpolicy.hostside.app2";
// TODO(b/321797685): Configure it via device-config once it is available.
- protected static final long PROCESS_STATE_TRANSITION_DELAY_MS = TimeUnit.SECONDS.toMillis(5);
+ protected final long mProcessStateTransitionLongDelayMs =
+ useDifferentDelaysForBackgroundChain() ? TimeUnit.SECONDS.toMillis(20)
+ : TimeUnit.SECONDS.toMillis(5);
+ protected final long mProcessStateTransitionShortDelayMs =
+ useDifferentDelaysForBackgroundChain() ? TimeUnit.SECONDS.toMillis(2)
+ : TimeUnit.SECONDS.toMillis(5);
private static final String TEST_APP2_ACTIVITY_CLASS = TEST_APP2_PKG + ".MyActivity";
private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService";
@@ -241,6 +246,22 @@
return Boolean.parseBoolean(output);
}
+ /**
+ * Check if the flag to use different delays for sensitive proc-states is enabled.
+ * This is a manual check because the feature flag infrastructure may not be available
+ * in all the branches that will get this code.
+ * TODO: b/322115994 - Use @RequiresFlagsEnabled with
+ * Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN once the tests are moved to cts.
+ */
+ private boolean useDifferentDelaysForBackgroundChain() {
+ if (!SdkLevel.isAtLeastV()) {
+ return false;
+ }
+ final String output = executeShellCommand("device_config get backstage_power"
+ + " com.android.server.net.use_different_delays_for_background_chain");
+ return Boolean.parseBoolean(output);
+ }
+
protected int getUid(String packageName) throws Exception {
return mContext.getPackageManager().getPackageUid(packageName, 0);
}
@@ -824,6 +845,10 @@
assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE");
}
+ protected void stopApp() {
+ executeSilentShellCommand("am stop-app " + TEST_APP2_PKG);
+ }
+
protected void setAppIdle(boolean isIdle) throws Exception {
setAppIdleNoAssert(isIdle);
assertAppIdle(isIdle);
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java
index 811190f..bfccce9 100644
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java
@@ -53,7 +53,7 @@
@After
public final void tearDown() throws Exception {
super.tearDown();
- finishActivity();
+ stopApp();
resetDeviceState();
}
@@ -108,7 +108,7 @@
assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
assertLaunchedActivityHasNetworkAccess("testStartActivity_default", () -> {
assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+ SystemClock.sleep(mProcessStateTransitionLongDelayMs);
assertNetworkAccess(false, null);
});
}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java
index 7038d02..3934cfa 100644
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java
@@ -268,6 +268,7 @@
setRestrictBackground(false);
setBatterySaverMode(false);
unregisterNetworkCallback();
+ stopApp();
if (SdkLevel.isAtLeastT() && (mCtsNetUtils != null)) {
mCtsNetUtils.restorePrivateDnsSetting();
@@ -387,7 +388,7 @@
finishActivity();
assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+ SystemClock.sleep(mProcessStateTransitionLongDelayMs);
assertNetworkAccess(false, null);
mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
@@ -413,7 +414,7 @@
finishActivity();
assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+ SystemClock.sleep(mProcessStateTransitionLongDelayMs);
assertNetworkAccess(false, null);
mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */);
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java
index 9b3fe9f..6c5f2ff 100644
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java
@@ -17,6 +17,7 @@
package com.android.cts.netpolicy.hostside;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
import static android.os.Process.SYSTEM_UID;
@@ -65,6 +66,7 @@
setRestrictBackground(false);
setRestrictedNetworkingMode(false);
unregisterNetworkCallback();
+ stopApp();
}
@Test
@@ -248,8 +250,8 @@
assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
try {
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ SystemClock.sleep(mProcessStateTransitionShortDelayMs);
assertNetworkingBlockedStatusForUid(mUid, METERED, true /* expectedResult */);
assertTrue(isUidNetworkingBlocked(mUid, NON_METERED));
@@ -260,7 +262,7 @@
finishActivity();
assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+ SystemClock.sleep(mProcessStateTransitionLongDelayMs);
assertNetworkingBlockedStatusForUid(mUid, METERED, true /* expectedResult */);
assertTrue(isUidNetworkingBlocked(mUid, NON_METERED));
diff --git a/tests/cts/multidevices/connectivity_multi_devices_test.py b/tests/cts/multidevices/connectivity_multi_devices_test.py
index 417db99..1d6fb51 100644
--- a/tests/cts/multidevices/connectivity_multi_devices_test.py
+++ b/tests/cts/multidevices/connectivity_multi_devices_test.py
@@ -34,6 +34,9 @@
)
def test_hotspot_upstream_wifi(self):
+ tether_utils.assume_hotspot_test_preconditions(
+ self.serverDevice, self.clientDevice, UpstreamType.WIFI
+ )
try:
# Connectivity of the client verified by asserting the validated capability.
tether_utils.setup_hotspot_and_client_for_upstream_type(
@@ -45,6 +48,9 @@
)
def test_hotspot_upstream_cellular(self):
+ tether_utils.assume_hotspot_test_preconditions(
+ self.serverDevice, self.clientDevice, UpstreamType.CELLULAR
+ )
try:
# Connectivity of the client verified by asserting the validated capability.
tether_utils.setup_hotspot_and_client_for_upstream_type(
diff --git a/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt b/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
index 8805edd..f4ad2c4 100644
--- a/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
+++ b/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
@@ -77,9 +77,9 @@
ctsNetUtils.expectNetworkIsSystemDefault(network)
}
- @Rpc(description = "Unrequest cellular connection.")
- fun unrequestCellular() {
- cbHelper.unrequestCell()
+ @Rpc(description = "Unregister all connections.")
+ fun unregisterAll() {
+ cbHelper.unregisterAll()
}
@Rpc(description = "Ensure any wifi is connected and is the default network.")
diff --git a/tests/cts/multidevices/tether_utils.py b/tests/cts/multidevices/tether_utils.py
index a2d703c..61f5c43 100644
--- a/tests/cts/multidevices/tether_utils.py
+++ b/tests/cts/multidevices/tether_utils.py
@@ -46,6 +46,32 @@
return base64.b64encode(uuid.uuid1().bytes).decode("utf-8").strip("=")
+def assume_hotspot_test_preconditions(
+ server_device: android_device,
+ client_device: android_device,
+ upstream_type: UpstreamType,
+) -> None:
+ server = server_device.connectivity_multi_devices_snippet
+ client = client_device.connectivity_multi_devices_snippet
+
+ # Assert pre-conditions specific to each upstream type.
+ asserts.skip_if(not client.hasWifiFeature(), "Client requires Wifi feature")
+ asserts.skip_if(
+ not server.hasHotspotFeature(), "Server requires hotspot feature"
+ )
+ if upstream_type == UpstreamType.CELLULAR:
+ asserts.skip_if(
+ not server.hasTelephonyFeature(), "Server requires Telephony feature"
+ )
+ elif upstream_type == UpstreamType.WIFI:
+ asserts.skip_if(
+ not server.isStaApConcurrencySupported(),
+ "Server requires Wifi AP + STA concurrency",
+ )
+ else:
+ raise ValueError(f"Invalid upstream type: {upstream_type}")
+
+
def setup_hotspot_and_client_for_upstream_type(
server_device: android_device,
client_device: android_device,
@@ -60,21 +86,9 @@
server = server_device.connectivity_multi_devices_snippet
client = client_device.connectivity_multi_devices_snippet
- # Assert pre-conditions specific to each upstream type.
- asserts.skip_if(not client.hasWifiFeature(), "Client requires Wifi feature")
- asserts.skip_if(
- not server.hasHotspotFeature(), "Server requires hotspot feature"
- )
if upstream_type == UpstreamType.CELLULAR:
- asserts.skip_if(
- not server.hasTelephonyFeature(), "Server requires Telephony feature"
- )
server.requestCellularAndEnsureDefault()
elif upstream_type == UpstreamType.WIFI:
- asserts.skip_if(
- not server.isStaApConcurrencySupported(),
- "Server requires Wifi AP + STA concurrency",
- )
server.ensureWifiIsDefault()
else:
raise ValueError(f"Invalid upstream type: {upstream_type}")
@@ -98,6 +112,6 @@
) -> None:
server = server_device.connectivity_multi_devices_snippet
if upstream_type == UpstreamType.CELLULAR:
- server.unrequestCellular()
+ server.unregisterAll()
# Teardown the hotspot.
server.stopAllTethering()
diff --git a/tests/cts/net/native/dns/Android.bp b/tests/cts/net/native/dns/Android.bp
index 8e24fba..de4a3bf 100644
--- a/tests/cts/net/native/dns/Android.bp
+++ b/tests/cts/net/native/dns/Android.bp
@@ -3,6 +3,11 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+filegroup {
+ name: "dns_async_test_default_map",
+ srcs: ["dns_async_test_default.map"],
+}
+
cc_defaults {
name: "dns_async_defaults",
@@ -20,6 +25,14 @@
srcs: [
"NativeDnsAsyncTest.cpp",
],
+ // This test runs on older platform versions, so many libraries (such as libbase and libc++)
+ // need to be linked statically. The test also needs to be linked with a version script to
+ // ensure that the statically-linked library isn't exported from the executable, where it
+ // would override the shared libraries that the platform itself uses.
+ // See http://b/333438055 for an example of what goes wrong when libc++ is partially exported
+ // from an executable.
+ version_script: ":dns_async_test_default_map",
+ stl: "libc++_static",
shared_libs: [
"libandroid",
"liblog",
diff --git a/tests/cts/net/native/dns/dns_async_test_default.map b/tests/cts/net/native/dns/dns_async_test_default.map
new file mode 100644
index 0000000..e342e43
--- /dev/null
+++ b/tests/cts/net/native/dns/dns_async_test_default.map
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2024 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.
+#
+
+{
+ local:
+ *;
+};
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 633f2b6..0e926cc 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -1237,13 +1237,14 @@
final IntentFilter filter = new IntentFilter();
filter.addAction(broadcastAction);
+ final CompletableFuture<NetworkRequest> requestFuture = new CompletableFuture<>();
final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
final AtomicInteger receivedCount = new AtomicInteger(0);
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final NetworkRequest request = intent.getParcelableExtra(EXTRA_NETWORK_REQUEST);
- assertPendingIntentRequestMatches(request, secondRequest, useListen);
+ requestFuture.complete(request);
receivedCount.incrementAndGet();
networkFuture.complete(intent.getParcelableExtra(EXTRA_NETWORK));
}
@@ -1258,6 +1259,9 @@
} catch (TimeoutException e) {
throw new AssertionError("PendingIntent not received for " + secondRequest, e);
}
+ assertPendingIntentRequestMatches(
+ requestFuture.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS),
+ secondRequest, useListen);
// Sleep for a small amount of time to try to check that only one callback is ever
// received (so the first callback was really unregistered). This does not guarantee
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 6fa2812..61ebd8f 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -303,18 +303,6 @@
fun expectOnAvailable(timeout: Long = TIMEOUT_MS): String {
return available.get(timeout, TimeUnit.MILLISECONDS)
}
-
- fun expectOnUnavailable() {
- // Assert that the future fails with the IllegalStateException from the
- // completeExceptionally() call inside onUnavailable.
- assertFailsWith(IllegalStateException::class) {
- try {
- available.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
- } catch (e: ExecutionException) {
- throw e.cause!!
- }
- }
- }
}
private class EthernetOutcomeReceiver :
diff --git a/thread/service/java/com/android/server/thread/NsdPublisher.java b/thread/service/java/com/android/server/thread/NsdPublisher.java
index d0cb9b8..1447ff8 100644
--- a/thread/service/java/com/android/server/thread/NsdPublisher.java
+++ b/thread/service/java/com/android/server/thread/NsdPublisher.java
@@ -45,7 +45,6 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -615,9 +614,8 @@
}
List<DnsTxtAttribute> txtList = new ArrayList<>();
for (Map.Entry<String, byte[]> entry : serviceInfo.getAttributes().entrySet()) {
- DnsTxtAttribute attribute = new DnsTxtAttribute();
- attribute.name = entry.getKey();
- attribute.value = Arrays.copyOf(entry.getValue(), entry.getValue().length);
+ DnsTxtAttribute attribute =
+ new DnsTxtAttribute(entry.getKey(), entry.getValue().clone());
txtList.add(attribute);
}
// TODO: b/329018320 - Use the serviceInfo.getExpirationTime to derive TTL.
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 737ec41..0c77dee 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -61,6 +61,8 @@
import static com.android.server.thread.openthread.IOtDaemon.OT_STATE_ENABLED;
import static com.android.server.thread.openthread.IOtDaemon.TUN_IF_NAME;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -117,6 +119,7 @@
import com.android.server.connectivity.ConnectivityResources;
import com.android.server.thread.openthread.BackboneRouterState;
import com.android.server.thread.openthread.BorderRouterConfigurationParcel;
+import com.android.server.thread.openthread.DnsTxtAttribute;
import com.android.server.thread.openthread.IChannelMasksReceiver;
import com.android.server.thread.openthread.IOtDaemon;
import com.android.server.thread.openthread.IOtDaemonCallback;
@@ -130,7 +133,6 @@
import java.io.IOException;
import java.net.Inet6Address;
-import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.time.Clock;
import java.time.DateTimeException;
@@ -338,9 +340,11 @@
final String modelName = resources.getString(R.string.config_thread_model_name);
final String vendorName = resources.getString(R.string.config_thread_vendor_name);
final String vendorOui = resources.getString(R.string.config_thread_vendor_oui);
+ final boolean managedByGoogle =
+ resources.getBoolean(R.bool.config_thread_managed_by_google_home);
if (!modelName.isEmpty()) {
- if (modelName.getBytes(StandardCharsets.UTF_8).length > MAX_MODEL_NAME_UTF8_BYTES) {
+ if (modelName.getBytes(UTF_8).length > MAX_MODEL_NAME_UTF8_BYTES) {
throw new IllegalStateException(
"Model name is longer than "
+ MAX_MODEL_NAME_UTF8_BYTES
@@ -350,7 +354,7 @@
}
if (!vendorName.isEmpty()) {
- if (vendorName.getBytes(StandardCharsets.UTF_8).length > MAX_VENDOR_NAME_UTF8_BYTES) {
+ if (vendorName.getBytes(UTF_8).length > MAX_VENDOR_NAME_UTF8_BYTES) {
throw new IllegalStateException(
"Vendor name is longer than "
+ MAX_VENDOR_NAME_UTF8_BYTES
@@ -367,9 +371,21 @@
meshcopTxts.modelName = modelName;
meshcopTxts.vendorName = vendorName;
meshcopTxts.vendorOui = HexEncoding.decode(vendorOui.replace("-", "").replace(":", ""));
+ meshcopTxts.nonStandardTxtEntries = List.of(makeManagedByGoogleTxtAttr(managedByGoogle));
+
return meshcopTxts;
}
+ /**
+ * Creates a DNS-SD TXT entry for indicating whether Thread on this device is managed by Google.
+ *
+ * @return TXT entry "vgh=1" if {@code managedByGoogle} is {@code true}; otherwise, "vgh=0"
+ */
+ private static DnsTxtAttribute makeManagedByGoogleTxtAttr(boolean managedByGoogle) {
+ final byte[] value = (managedByGoogle ? "1" : "0").getBytes(UTF_8);
+ return new DnsTxtAttribute("vgh", value);
+ }
+
private void onOtDaemonDied() {
checkOnHandlerThread();
Log.w(TAG, "OT daemon is dead, clean up...");
diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
index 11c4819..0e95703 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
@@ -45,7 +45,6 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
-
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import android.content.Context;
@@ -859,6 +858,7 @@
assertThat(txtMap.get("rv")).isNotNull();
assertThat(txtMap.get("tv")).isNotNull();
assertThat(txtMap.get("sb")).isNotNull();
+ assertThat(new String(txtMap.get("vgh"))).isIn(List.of("0", "1"));
}
@Test
@@ -885,6 +885,7 @@
assertThat(txtMap.get("tv")).isNotNull();
assertThat(txtMap.get("sb")).isNotNull();
assertThat(txtMap.get("id").length).isEqualTo(16);
+ assertThat(new String(txtMap.get("vgh"))).isIn(List.of("0", "1"));
}
@Test
diff --git a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
index d24059a..c0a8eea 100644
--- a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
+++ b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
@@ -17,7 +17,9 @@
import static android.net.thread.utils.IntegrationTestUtils.SERVICE_DISCOVERY_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.waitFor;
+
import static com.google.common.io.BaseEncoding.base16;
+
import static java.util.concurrent.TimeUnit.SECONDS;
import android.net.InetAddresses;
@@ -26,7 +28,9 @@
import android.net.thread.ActiveOperationalDataset;
import android.os.Handler;
import android.os.HandlerThread;
+
import com.google.errorprone.annotations.FormatMethod;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
diff --git a/thread/tests/unit/src/com/android/server/thread/NsdPublisherTest.java b/thread/tests/unit/src/com/android/server/thread/NsdPublisherTest.java
index 3cae84f..ae0bc80 100644
--- a/thread/tests/unit/src/com/android/server/thread/NsdPublisherTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/NsdPublisherTest.java
@@ -22,14 +22,15 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import android.net.DnsResolver;
import android.net.InetAddresses;
@@ -50,21 +51,23 @@
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.net.InetAddress;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
/** Unit tests for {@link NsdPublisher}. */
public final class NsdPublisherTest {
+ private static final DnsTxtAttribute TEST_TXT_ENTRY_1 =
+ new DnsTxtAttribute("key1", new byte[] {0x01, 0x02});
+ private static final DnsTxtAttribute TEST_TXT_ENTRY_2 =
+ new DnsTxtAttribute("key2", new byte[] {0x03});
+
@Mock private NsdManager mMockNsdManager;
@Mock private DnsResolver mMockDnsResolver;
@@ -87,19 +90,15 @@
public void registerService_nsdManagerSucceeds_serviceRegistrationSucceeds() throws Exception {
prepareTest();
- DnsTxtAttribute txt1 = makeTxtAttribute("key1", List.of(0x01, 0x02));
- DnsTxtAttribute txt2 = makeTxtAttribute("key2", List.of(0x03));
-
mNsdPublisher.registerService(
null,
"MyService",
"_test._tcp",
List.of("_subtype1", "_subtype2"),
12345,
- List.of(txt1, txt2),
+ List.of(TEST_TXT_ENTRY_1, TEST_TXT_ENTRY_2),
mRegistrationReceiver,
16 /* listenerId */);
-
mTestLooper.dispatchAll();
ArgumentCaptor<NsdServiceInfo> actualServiceInfoCaptor =
@@ -126,11 +125,10 @@
assertThat(actualServiceInfo.getSubtypes()).isEqualTo(Set.of("_subtype1", "_subtype2"));
assertThat(actualServiceInfo.getPort()).isEqualTo(12345);
assertThat(actualServiceInfo.getAttributes().size()).isEqualTo(2);
- assertThat(actualServiceInfo.getAttributes().get("key1"))
- .isEqualTo(new byte[] {(byte) 0x01, (byte) 0x02});
- assertThat(actualServiceInfo.getAttributes().get("key2"))
- .isEqualTo(new byte[] {(byte) 0x03});
-
+ assertThat(actualServiceInfo.getAttributes().get(TEST_TXT_ENTRY_1.name))
+ .isEqualTo(TEST_TXT_ENTRY_1.value);
+ assertThat(actualServiceInfo.getAttributes().get(TEST_TXT_ENTRY_2.name))
+ .isEqualTo(TEST_TXT_ENTRY_2.value);
verify(mRegistrationReceiver, times(1)).onSuccess();
}
@@ -138,19 +136,15 @@
public void registerService_nsdManagerFails_serviceRegistrationFails() throws Exception {
prepareTest();
- DnsTxtAttribute txt1 = makeTxtAttribute("key1", List.of(0x01, 0x02));
- DnsTxtAttribute txt2 = makeTxtAttribute("key2", List.of(0x03));
-
mNsdPublisher.registerService(
null,
"MyService",
"_test._tcp",
List.of("_subtype1", "_subtype2"),
12345,
- List.of(txt1, txt2),
+ List.of(TEST_TXT_ENTRY_1, TEST_TXT_ENTRY_2),
mRegistrationReceiver,
16 /* listenerId */);
-
mTestLooper.dispatchAll();
ArgumentCaptor<NsdServiceInfo> actualServiceInfoCaptor =
@@ -177,21 +171,16 @@
assertThat(actualServiceInfo.getSubtypes()).isEqualTo(Set.of("_subtype1", "_subtype2"));
assertThat(actualServiceInfo.getPort()).isEqualTo(12345);
assertThat(actualServiceInfo.getAttributes().size()).isEqualTo(2);
- assertThat(actualServiceInfo.getAttributes().get("key1"))
- .isEqualTo(new byte[] {(byte) 0x01, (byte) 0x02});
- assertThat(actualServiceInfo.getAttributes().get("key2"))
- .isEqualTo(new byte[] {(byte) 0x03});
-
+ assertThat(actualServiceInfo.getAttributes().get(TEST_TXT_ENTRY_1.name))
+ .isEqualTo(TEST_TXT_ENTRY_1.value);
+ assertThat(actualServiceInfo.getAttributes().get(TEST_TXT_ENTRY_2.name))
+ .isEqualTo(TEST_TXT_ENTRY_2.value);
verify(mRegistrationReceiver, times(1)).onError(FAILURE_INTERNAL_ERROR);
}
@Test
public void registerService_nsdManagerThrows_serviceRegistrationFails() throws Exception {
prepareTest();
-
- DnsTxtAttribute txt1 = makeTxtAttribute("key1", List.of(0x01, 0x02));
- DnsTxtAttribute txt2 = makeTxtAttribute("key2", List.of(0x03));
-
doThrow(new IllegalArgumentException("NsdManager fails"))
.when(mMockNsdManager)
.registerService(any(), anyInt(), any(Executor.class), any());
@@ -202,7 +191,7 @@
"_test._tcp",
List.of("_subtype1", "_subtype2"),
12345,
- List.of(txt1, txt2),
+ List.of(TEST_TXT_ENTRY_1, TEST_TXT_ENTRY_2),
mRegistrationReceiver,
16 /* listenerId */);
mTestLooper.dispatchAll();
@@ -215,16 +204,13 @@
throws Exception {
prepareTest();
- DnsTxtAttribute txt1 = makeTxtAttribute("key1", List.of(0x01, 0x02));
- DnsTxtAttribute txt2 = makeTxtAttribute("key2", List.of(0x03));
-
mNsdPublisher.registerService(
null,
"MyService",
"_test._tcp",
List.of("_subtype1", "_subtype2"),
12345,
- List.of(txt1, txt2),
+ List.of(TEST_TXT_ENTRY_1, TEST_TXT_ENTRY_2),
mRegistrationReceiver,
16 /* listenerId */);
@@ -260,16 +246,13 @@
public void unregisterService_nsdManagerFails_serviceUnregistrationFails() throws Exception {
prepareTest();
- DnsTxtAttribute txt1 = makeTxtAttribute("key1", List.of(0x01, 0x02));
- DnsTxtAttribute txt2 = makeTxtAttribute("key2", List.of(0x03));
-
mNsdPublisher.registerService(
null,
"MyService",
"_test._tcp",
List.of("_subtype1", "_subtype2"),
12345,
- List.of(txt1, txt2),
+ List.of(TEST_TXT_ENTRY_1, TEST_TXT_ENTRY_2),
mRegistrationReceiver,
16 /* listenerId */);
@@ -587,8 +570,8 @@
List.of(
InetAddress.parseNumericAddress("2001::1"),
InetAddress.parseNumericAddress("2001::2")));
- serviceInfo.setAttribute("key1", new byte[] {(byte) 0x01, (byte) 0x02});
- serviceInfo.setAttribute("key2", new byte[] {(byte) 0x03});
+ serviceInfo.setAttribute(TEST_TXT_ENTRY_1.name, TEST_TXT_ENTRY_1.value);
+ serviceInfo.setAttribute(TEST_TXT_ENTRY_2.name, TEST_TXT_ENTRY_2.value);
serviceInfoCallbackArgumentCaptor.getValue().onServiceUpdated(serviceInfo);
mTestLooper.dispatchAll();
@@ -599,11 +582,8 @@
eq("_test._tcp"),
eq(12345),
eq(List.of("2001::1", "2001::2")),
- argThat(
- new TxtMatcher(
- List.of(
- makeTxtAttribute("key1", List.of(0x01, 0x02)),
- makeTxtAttribute("key2", List.of(0x03))))),
+ (List<DnsTxtAttribute>)
+ argThat(containsInAnyOrder(TEST_TXT_ENTRY_1, TEST_TXT_ENTRY_2)),
anyInt());
}
@@ -725,10 +705,6 @@
@Test
public void reset_unregisterAll() {
prepareTest();
-
- DnsTxtAttribute txt1 = makeTxtAttribute("key1", List.of(0x01, 0x02));
- DnsTxtAttribute txt2 = makeTxtAttribute("key2", List.of(0x03));
-
ArgumentCaptor<NsdServiceInfo> actualServiceInfoCaptor =
ArgumentCaptor.forClass(NsdServiceInfo.class);
ArgumentCaptor<NsdManager.RegistrationListener> actualRegistrationListenerCaptor =
@@ -740,7 +716,7 @@
"_test._tcp",
List.of("_subtype1", "_subtype2"),
12345,
- List.of(txt1, txt2),
+ List.of(TEST_TXT_ENTRY_1, TEST_TXT_ENTRY_2),
mRegistrationReceiver,
16 /* listenerId */);
mTestLooper.dispatchAll();
@@ -814,19 +790,6 @@
verify(spyNsdPublisher, times(1)).reset();
}
- private static DnsTxtAttribute makeTxtAttribute(String name, List<Integer> value) {
- DnsTxtAttribute txtAttribute = new DnsTxtAttribute();
-
- txtAttribute.name = name;
- txtAttribute.value = new byte[value.size()];
-
- for (int i = 0; i < value.size(); ++i) {
- txtAttribute.value[i] = value.get(i).byteValue();
- }
-
- return txtAttribute;
- }
-
private static List<InetAddress> makeAddresses(String... addressStrings) {
List<InetAddress> addresses = new ArrayList<>();
@@ -836,30 +799,6 @@
return addresses;
}
- private static class TxtMatcher implements ArgumentMatcher<List<DnsTxtAttribute>> {
- private final List<DnsTxtAttribute> mAttributes;
-
- TxtMatcher(List<DnsTxtAttribute> attributes) {
- mAttributes = attributes;
- }
-
- @Override
- public boolean matches(List<DnsTxtAttribute> argument) {
- if (argument.size() != mAttributes.size()) {
- return false;
- }
- for (int i = 0; i < argument.size(); ++i) {
- if (!Objects.equals(argument.get(i).name, mAttributes.get(i).name)) {
- return false;
- }
- if (!Arrays.equals(argument.get(i).value, mAttributes.get(i).value)) {
- return false;
- }
- }
- return true;
- }
- }
-
// @Before and @Test run in different threads. NsdPublisher requires the jobs are run on the
// thread looper, so TestLooper needs to be created inside each test case to install the
// correct looper.
diff --git a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
index 8f60783..2f58943 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
@@ -78,6 +78,7 @@
import com.android.connectivity.resources.R;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.connectivity.ConnectivityResources;
+import com.android.server.thread.openthread.DnsTxtAttribute;
import com.android.server.thread.openthread.MeshcopTxtAttributes;
import com.android.server.thread.openthread.testing.FakeOtDaemon;
@@ -94,6 +95,7 @@
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
+import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.DateTimeException;
import java.util.concurrent.CompletableFuture;
@@ -145,6 +147,7 @@
private static final byte[] TEST_VENDOR_OUI_BYTES = new byte[] {(byte) 0xAC, (byte) 0xDE, 0x48};
private static final String TEST_VENDOR_NAME = "test vendor";
private static final String TEST_MODEL_NAME = "test model";
+ private static final boolean TEST_VGH_VALUE = false;
@Mock private ConnectivityManager mMockConnectivityManager;
@Mock private NetworkAgent mMockNetworkAgent;
@@ -197,6 +200,8 @@
.thenReturn(TEST_VENDOR_OUI);
when(mResources.getString(eq(R.string.config_thread_model_name)))
.thenReturn(TEST_MODEL_NAME);
+ when(mResources.getBoolean(eq(R.bool.config_thread_managed_by_google_home)))
+ .thenReturn(TEST_VGH_VALUE);
final AtomicFile storageFile = new AtomicFile(tempFolder.newFile("thread_settings.xml"));
mPersistentSettings = new ThreadPersistentSettings(storageFile, mConnectivityResources);
@@ -232,13 +237,15 @@
}
@Test
- public void initialize_vendorAndModelNameInResourcesAreSetToOtDaemon() throws Exception {
+ public void initialize_resourceOverlayValuesAreSetToOtDaemon() throws Exception {
when(mResources.getString(eq(R.string.config_thread_vendor_name)))
.thenReturn(TEST_VENDOR_NAME);
when(mResources.getString(eq(R.string.config_thread_vendor_oui)))
.thenReturn(TEST_VENDOR_OUI);
when(mResources.getString(eq(R.string.config_thread_model_name)))
.thenReturn(TEST_MODEL_NAME);
+ when(mResources.getBoolean(eq(R.bool.config_thread_managed_by_google_home)))
+ .thenReturn(true);
mService.initialize();
mTestLooper.dispatchAll();
@@ -247,6 +254,20 @@
assertThat(meshcopTxts.vendorName).isEqualTo(TEST_VENDOR_NAME);
assertThat(meshcopTxts.vendorOui).isEqualTo(TEST_VENDOR_OUI_BYTES);
assertThat(meshcopTxts.modelName).isEqualTo(TEST_MODEL_NAME);
+ assertThat(meshcopTxts.nonStandardTxtEntries)
+ .containsExactly(new DnsTxtAttribute("vgh", "1".getBytes(StandardCharsets.UTF_8)));
+ }
+
+ @Test
+ public void getMeshcopTxtAttributes_managedByGoogleIsFalse_vghIsZero() {
+ when(mResources.getBoolean(eq(R.bool.config_thread_managed_by_google_home)))
+ .thenReturn(false);
+
+ MeshcopTxtAttributes meshcopTxts =
+ ThreadNetworkControllerService.getMeshcopTxtAttributes(mResources);
+
+ assertThat(meshcopTxts.nonStandardTxtEntries)
+ .containsExactly(new DnsTxtAttribute("vgh", "0".getBytes(StandardCharsets.UTF_8)));
}
@Test