Merge "[Thread] add CTS tests for DISALLOW_THREAD_NETWORK" into main
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/DisallowThreadNetwork.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/DisallowThreadNetwork.java
new file mode 100644
index 0000000..3dd09f7
--- /dev/null
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/DisallowThreadNetwork.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package com.android.bedstead.harrier.policies;
+
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_DEVICE_OWNER;
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_PARENT_INSTANCE_OF_ORGANIZATIONAL_OWNED_PROFILE_OWNER_PROFILE;
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_GLOBALLY;
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.CANNOT_BE_APPLIED_BY_ROLE_HOLDER;
+import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_DEVICE_POLICY_THREAD_NETWORK;
+
+import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy;
+
+/**
+ * Policy related to setting {@code DISLLOW_THREAD_NETWORK}.
+*/
+@EnterprisePolicy(
+ dpc = { APPLIED_BY_DEVICE_OWNER
+ | APPLIED_BY_PARENT_INSTANCE_OF_ORGANIZATIONAL_OWNED_PROFILE_OWNER_PROFILE
+ | APPLIES_GLOBALLY
+ | CANNOT_BE_APPLIED_BY_ROLE_HOLDER },
+ permissions = @EnterprisePolicy.Permission(
+ appliedWith = MANAGE_DEVICE_POLICY_THREAD_NETWORK,
+ appliesTo = APPLIES_GLOBALLY))
+public final class DisallowThreadNetwork {
+}
diff --git a/common/device-side/bedstead/nene/common/src/main/java/com/android/bedstead/nene/permissions/CommonPermissions.java b/common/device-side/bedstead/nene/common/src/main/java/com/android/bedstead/nene/permissions/CommonPermissions.java
index 90b6199..a8c0377 100644
--- a/common/device-side/bedstead/nene/common/src/main/java/com/android/bedstead/nene/permissions/CommonPermissions.java
+++ b/common/device-side/bedstead/nene/common/src/main/java/com/android/bedstead/nene/permissions/CommonPermissions.java
@@ -1990,6 +1990,8 @@
public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS";
/** See {@link Manifest#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} */
public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL";
+ /** See {@link Manifest#MANAGE_DEVICE_POLICY_THREAD_NETWORK} */
+ public static final String MANAGE_DEVICE_POLICY_THREAD_NETWORK = "android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK";
public static final String MANAGE_DEFAULT_APPLICATIONS = "android.permission.MANAGE_DEFAULT_APPLICATIONS";
}
diff --git a/common/device-side/bedstead/nene/common/src/main/java/com/android/bedstead/nene/userrestrictions/CommonUserRestrictions.java b/common/device-side/bedstead/nene/common/src/main/java/com/android/bedstead/nene/userrestrictions/CommonUserRestrictions.java
index edcefa0..96ff3b2 100644
--- a/common/device-side/bedstead/nene/common/src/main/java/com/android/bedstead/nene/userrestrictions/CommonUserRestrictions.java
+++ b/common/device-side/bedstead/nene/common/src/main/java/com/android/bedstead/nene/userrestrictions/CommonUserRestrictions.java
@@ -249,6 +249,9 @@
/** See {@code android.os.UserManager#DISALLOW_CONFIG_DEFAULT_APPS} */
public static final String DISALLOW_CONFIG_DEFAULT_APPS = "disallow_config_default_apps";
+ /** See {@code android.os.UserManager#DISALLOW_THREAD_NETWORK} */
+ public static final String DISALLOW_THREAD_NETWORK = "no_thread_network";
+
/** See {@code Manifest#ACTION_USER_RESTRICTIONS_CHANGED} */
public static final String ACTION_USER_RESTRICTIONS_CHANGED =
"android.os.action.USER_RESTRICTIONS_CHANGED";
@@ -327,6 +330,7 @@
DISALLOW_ADD_WIFI_CONFIG,
DISALLOW_CELLULAR_2G,
DISALLOW_ULTRA_WIDEBAND_RADIO,
- DISALLOW_CONFIG_DEFAULT_APPS
+ DISALLOW_CONFIG_DEFAULT_APPS,
+ DISALLOW_THREAD_NETWORK,
};
}
diff --git a/tests/devicepolicy/Android.bp b/tests/devicepolicy/Android.bp
index 8709b05..fe6294c 100644
--- a/tests/devicepolicy/Android.bp
+++ b/tests/devicepolicy/Android.bp
@@ -25,6 +25,7 @@
"compatibility-device-util-axt",
"ctstestrunner-axt",
"cts-net-utils",
+ "flag-junit",
"truth",
"androidx.test.ext.junit",
"testng", // used for assertThrows
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/ThreadNetworkTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/ThreadNetworkTest.java
new file mode 100644
index 0000000..9c71653
--- /dev/null
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/ThreadNetworkTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+package android.devicepolicy.cts;
+
+import static android.net.thread.ThreadNetworkException.ERROR_FAILED_PRECONDITION;
+import static android.os.UserManager.DISALLOW_THREAD_NETWORK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import java.time.Duration;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import android.Manifest;
+import android.content.Context;
+import android.net.thread.ThreadNetworkController;
+import android.net.thread.ThreadNetworkException;
+import android.net.thread.ThreadNetworkManager;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import com.android.activitycontext.ActivityContext;
+import com.android.bedstead.harrier.BedsteadJUnit4;
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.EnsureDoesNotHaveUserRestriction;
+import com.android.bedstead.harrier.annotations.EnsureHasPermission;
+import com.android.bedstead.harrier.annotations.EnsureHasUserRestriction;
+import com.android.bedstead.harrier.annotations.Postsubmit;
+import com.android.bedstead.harrier.annotations.RequireFeature;
+import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest;
+import com.android.bedstead.harrier.annotations.enterprise.PolicyAppliesTest;
+import com.android.bedstead.harrier.annotations.enterprise.PolicyDoesNotApplyTest;
+import com.android.bedstead.harrier.policies.DisallowThreadNetwork;
+import com.android.bedstead.nene.TestApis;
+import com.android.compatibility.common.util.ApiTest;
+import com.android.net.thread.platform.flags.Flags;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(BedsteadJUnit4.class)
+// If the device doesn't support Thread then as long as the user restriction doesn't throw an
+// exception when setting - we can assume it's fine
+@RequireFeature("android.hardware.thread_network")
+@RequiresFlagsEnabled(Flags.FLAG_THREAD_USER_RESTRICTION_ENABLED)
+public final class ThreadNetworkTest {
+
+ @ClassRule @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+ private static final Context sContext = TestApis.context().instrumentedContext();
+ private static final ThreadNetworkManager sThreadNetworkManager =
+ sContext.getSystemService(ThreadNetworkManager.class);
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @CannotSetPolicyTest(policy = DisallowThreadNetwork.class, includeNonDeviceAdminStates = false)
+ @Postsubmit(reason = "new test")
+ @ApiTest(apis = "android.os.UserManager#DISALLOW_THREAD_NETWORK")
+ public void setUserRestriction_disallowThreadNetwork_throwsSecurityException() {
+ assertThrows(SecurityException.class,
+ () -> sDeviceState.dpc().devicePolicyManager().addUserRestriction(
+ sDeviceState.dpc().componentName(), DISALLOW_THREAD_NETWORK));
+ }
+
+ @PolicyAppliesTest(policy = DisallowThreadNetwork.class)
+ @Postsubmit(reason = "new test")
+ @ApiTest(apis = "android.os.UserManager#DISALLOW_THREAD_NETWORK")
+ public void setUserRestriction_disallowThreadNetwork_isSet() {
+ try {
+ sDeviceState.dpc().devicePolicyManager().addUserRestriction(
+ sDeviceState.dpc().componentName(), DISALLOW_THREAD_NETWORK);
+
+ assertThat(TestApis.devicePolicy().userRestrictions().isSet(DISALLOW_THREAD_NETWORK))
+ .isTrue();
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().clearUserRestriction(
+ sDeviceState.dpc().componentName(), DISALLOW_THREAD_NETWORK);
+ }
+ }
+
+ @PolicyDoesNotApplyTest(policy = DisallowThreadNetwork.class)
+ @Postsubmit(reason = "new test")
+ @ApiTest(apis = "android.os.UserManager#DISALLOW_THREAD_NETWORK")
+ public void setUserRestriction_disallowThreadNetwork_isNotSet() {
+ try {
+ sDeviceState.dpc().devicePolicyManager().addUserRestriction(
+ sDeviceState.dpc().componentName(), DISALLOW_THREAD_NETWORK);
+
+ assertThat(TestApis.devicePolicy().userRestrictions().isSet(DISALLOW_THREAD_NETWORK))
+ .isFalse();
+ } finally {
+
+ sDeviceState.dpc().devicePolicyManager().clearUserRestriction(
+ sDeviceState.dpc().componentName(), DISALLOW_THREAD_NETWORK);
+ }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_THREAD_ENABLED_PLATFORM)
+ @EnsureHasPermission(Manifest.permission.THREAD_NETWORK_PRIVILEGED)
+ @EnsureHasUserRestriction(DISALLOW_THREAD_NETWORK)
+ @Postsubmit(reason = "new test")
+ @Test
+ @ApiTest(apis = "android.os.UserManager#DISALLOW_THREAD_NETWORK")
+ public void enableThread_disallowThreadNetworkIsSet_failWithFailedPrecondition()
+ throws Exception {
+ ThreadNetworkController controller =
+ sThreadNetworkManager.getAllThreadNetworkControllers().get(0);
+ CompletableFuture<Boolean> setEnabledFuture = new CompletableFuture<>();
+
+ controller.setEnabled(
+ true, sContext.getMainExecutor(), newOutcomeReceiver(setEnabledFuture));
+
+ ExecutionException thrown = assertThrows(
+ ExecutionException.class, () -> setEnabledFuture.get(1, TimeUnit.SECONDS));
+ ThreadNetworkException cause = (ThreadNetworkException) thrown.getCause();
+ assertThat(cause.getErrorCode()).isEqualTo(ERROR_FAILED_PRECONDITION);
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_THREAD_ENABLED_PLATFORM)
+ @EnsureHasPermission(Manifest.permission.THREAD_NETWORK_PRIVILEGED)
+ @EnsureDoesNotHaveUserRestriction(DISALLOW_THREAD_NETWORK)
+ @Postsubmit(reason = "new test")
+ @Test
+ @ApiTest(apis = "android.os.UserManager#DISALLOW_THREAD_NETWORK")
+ public void enableThread_disallowThreadIsNotSet_success() throws Exception {
+ ThreadNetworkController controller =
+ sThreadNetworkManager.getAllThreadNetworkControllers().get(0);
+ CompletableFuture<Boolean> setEnabledFuture = new CompletableFuture<>();
+
+ controller.setEnabled(
+ true, sContext.getMainExecutor(), newOutcomeReceiver(setEnabledFuture));
+
+ assertThat(setEnabledFuture.get(1, TimeUnit.SECONDS)).isTrue();
+ }
+
+ // TODO(b/328393183): add the Thread API call to bedstead when there is another use case
+ /**
+ * Creates a {@link OutcomeReceiver} which sets the {@code future} to {@code true} when the
+ * receiver is invoked with a result, or fails the {@code future} with a {@link
+ * ThreadNetworkException} when the receiver is invoked with an error.
+ */
+ private static final OutcomeReceiver<Void, ThreadNetworkException> newOutcomeReceiver(
+ CompletableFuture<Boolean> future) {
+ return new OutcomeReceiver<Void, ThreadNetworkException>() {
+ @Override
+ public void onResult(Void v) {
+ future.complete(true);
+ }
+ @Override
+ public void onError(ThreadNetworkException exp) {
+ future.completeExceptionally(exp);
+ }
+ };
+ }
+}