Initial CTS tests for DPM / driving safety integration.
Also set checkstyle hook for hostsidetests/devicepolicy.
Test: atest \
CtsDevicePolicyManagerTestCases:DeviceOwnerTest#testDevicePolicySafetyCheckerIntegration \
CtsDevicePolicyManagerTestCases:ProfileOwnerTest#testDevicePolicySafetyCheckerIntegration
Bug: 172376923
Change-Id: I877cd948b51032d0581cd41512a27a9a3a98a333
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ff509e0..b34d2ad 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -38,6 +38,7 @@
tests/tests/widget/
common/device-side/util/
hostsidetests/car/
+ hostsidetests/devicepolicy
hostsidetests/multiuser/
hostsidetests/scopedstorage/
hostsidetests/stagedinstall/
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp b/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp
index a81f939..1fb5013 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp
@@ -37,6 +37,7 @@
"cts-security-test-support-library",
"truth-prebuilt",
"androidx.legacy_legacy-support-v4",
+ "devicepolicy-deviceside-common",
],
min_sdk_version: "21",
// tag this module as a cts test artifact
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java
new file mode 100644
index 0000000..014d20f
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 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.cts.deviceowner;
+
+import static android.app.admin.DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER;
+import static android.app.admin.DevicePolicyManager.OPERATION_REMOVE_USER;
+import static android.app.admin.DevicePolicyManager.OPERATION_START_USER_IN_BACKGROUND;
+import static android.app.admin.DevicePolicyManager.OPERATION_STOP_USER;
+import static android.app.admin.DevicePolicyManager.OPERATION_SWITCH_USER;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.os.UserHandle;
+
+import com.android.cts.devicepolicy.DevicePolicySafetyCheckerIntegrationTester;
+
+//TODO(b/174859111): move to automotive-only section
+/**
+ * Tests that DPM calls fail when determined by the
+ * {@link android.app.admin.DevicePolicySafetyChecker}.
+ */
+public final class DevicePolicySafetyCheckerIntegrationTest extends BaseDeviceOwnerTest {
+
+ private static final int NO_FLAGS = 0;
+ private static final UserHandle USER_HANDLE = UserHandle.of(42);
+
+ private final DevicePolicySafetyCheckerIntegrationTester mTester =
+ new DevicePolicySafetyCheckerIntegrationTester() {
+
+ @Override
+ protected @DevicePolicyOperation int[] getSafetyAwareOperations() {
+ return new int [] {
+ OPERATION_CREATE_AND_MANAGE_USER,
+ OPERATION_REMOVE_USER,
+ OPERATION_START_USER_IN_BACKGROUND,
+ OPERATION_STOP_USER,
+ OPERATION_SWITCH_USER};
+ }
+
+ @Override
+ protected void runOperation(DevicePolicyManager dpm,
+ @DevicePolicyOperation int operation, boolean overloaded) {
+ switch (operation) {
+ case OPERATION_CREATE_AND_MANAGE_USER:
+ dpm.createAndManageUser(/* admin= */ getWho(), /* name= */ null,
+ /* profileOwner= */ getWho(), /* adminExtras= */ null, NO_FLAGS);
+ break;
+ case OPERATION_REMOVE_USER:
+ dpm.removeUser(getWho(), USER_HANDLE);
+ break;
+ case OPERATION_START_USER_IN_BACKGROUND:
+ dpm.startUserInBackground(getWho(), USER_HANDLE);
+ break;
+ case OPERATION_STOP_USER:
+ dpm.stopUser(getWho(), USER_HANDLE);
+ break;
+ case OPERATION_SWITCH_USER:
+ dpm.switchUser(getWho(), USER_HANDLE);
+ break;
+ default:
+ throwUnsupportedOperationException(operation, overloaded);
+ }
+ }
+ };
+
+ /**
+ * Tests that all safety-aware operations are properly implemented.
+ */
+ public void testAllOperations() {
+ mTester.testAllOperations(mDevicePolicyManager);
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/ProfileOwner/Android.bp b/hostsidetests/devicepolicy/app/ProfileOwner/Android.bp
index c49bc4b..c235c99 100644
--- a/hostsidetests/devicepolicy/app/ProfileOwner/Android.bp
+++ b/hostsidetests/devicepolicy/app/ProfileOwner/Android.bp
@@ -26,6 +26,7 @@
static_libs: [
"ctstestrunner-axt",
"compatibility-device-util-axt",
+ "devicepolicy-deviceside-common",
"ub-uiautomator",
],
// tag this module as a cts test artifact
diff --git a/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/DevicePolicySafetyCheckerIntegrationTest.java b/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/DevicePolicySafetyCheckerIntegrationTest.java
new file mode 100644
index 0000000..9ba0a97
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/DevicePolicySafetyCheckerIntegrationTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 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.cts.profileowner;
+
+import com.android.cts.devicepolicy.DevicePolicySafetyCheckerIntegrationTester;
+
+//TODO(b/174859111): move to automotive-only section
+/**
+ * Tests that DPM calls fail when determined by the
+ * {@link android.app.admin.DevicePolicySafetyChecker}.
+ */
+public final class DevicePolicySafetyCheckerIntegrationTest extends BaseProfileOwnerTest {
+
+ private final DevicePolicySafetyCheckerIntegrationTester mTester =
+ new DevicePolicySafetyCheckerIntegrationTester();
+
+ /**
+ * Tests that all safety-aware operations are properly implemented.
+ */
+ public void testAllOperations() {
+ mTester.testAllOperations(mDevicePolicyManager);
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/common/Android.bp b/hostsidetests/devicepolicy/app/common/Android.bp
index 349a7e7..9d3fb5e 100644
--- a/hostsidetests/devicepolicy/app/common/Android.bp
+++ b/hostsidetests/devicepolicy/app/common/Android.bp
@@ -16,10 +16,11 @@
java_library {
name: "devicepolicy-deviceside-common",
srcs: ["src/**/*.java"],
- sdk_version: "test_current",
+ platform_apis: true,
libs: ["junit"],
static_libs: [
"androidx.legacy_legacy-support-v4",
+ "compatibility-device-util-axt",
"ctstestrunner-axt",
"androidx.test.rules",
"ub-uiautomator",
diff --git a/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/DevicePolicySafetyCheckerIntegrationTester.java b/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/DevicePolicySafetyCheckerIntegrationTester.java
new file mode 100644
index 0000000..23f0412
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/DevicePolicySafetyCheckerIntegrationTester.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 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.cts.devicepolicy;
+
+import static android.app.admin.DevicePolicyManager.OPERATION_LOCK_NOW;
+import static android.app.admin.DevicePolicyManager.operationToString;
+
+import static org.junit.Assert.fail;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.UnsafeStateException;
+import android.util.Log;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Helper class to test that DPM calls fail when determined by the
+ * {@link android.app.admin.DevicePolicySafetyChecker}; it provides the base infra, so it can be
+ * used by both device and profile owner tests.
+ */
+public class DevicePolicySafetyCheckerIntegrationTester {
+
+ public static final String TAG = DevicePolicySafetyCheckerIntegrationTester.class
+ .getSimpleName();
+
+ private static final int[] OPERATIONS = new int[] {
+ OPERATION_LOCK_NOW
+ };
+
+ private static final int[] OVERLOADED_OPERATIONS = new int[] {
+ OPERATION_LOCK_NOW
+ };
+
+ /**
+ * Tests that all safety-aware operations are properly implemented.
+ */
+ public final void testAllOperations(DevicePolicyManager dpm) {
+ Objects.requireNonNull(dpm);
+
+ List<String> failures = new ArrayList<>();
+ for (int operation : OPERATIONS) {
+ safeOperationTest(dpm, failures, operation, /* overloaded= */ false);
+ }
+
+ for (int operation : OVERLOADED_OPERATIONS) {
+ safeOperationTest(dpm, failures, operation, /* overloaded= */ true);
+ }
+
+ for (int operation : getSafetyAwareOperations()) {
+ safeOperationTest(dpm, failures, operation, /* overloaded= */ false);
+ }
+
+ for (int operation : getOverloadedSafetyAwareOperations()) {
+ safeOperationTest(dpm, failures, operation, /* overloaded= */ true);
+ }
+
+ if (!failures.isEmpty()) {
+ fail(failures.size() + " operations failed: " + failures);
+ }
+ }
+
+ /**
+ * Gets the device / profile owner-specific operations.
+ *
+ * <p>By default it returns an empty array, but sub-classes can override to add its supported
+ * operations.
+ */
+ protected @DevicePolicyOperation int[] getSafetyAwareOperations() {
+ return new int[] {};
+ }
+
+ /**
+ * Gets the device / profile owner-specific operations that are overloaded.
+ *
+ * <p>For example, {@code OPERATION_WIPE_DATA} is used for both {@code wipeData(flags)} and
+ * {@code wipeData(flags, reason)}, so it should be returned both here and on
+ * {@link #getSafetyAwareOperations()}, then
+ * {@link #runOperation(DevicePolicyManager, int, boolean)} will handle which method to call for
+ * each case.
+ *
+ * <p>By default it returns an empty array, but sub-classes can override to add its supported
+ * operations.
+ */
+ protected @DevicePolicyOperation int[] getOverloadedSafetyAwareOperations() {
+ return new int[] {};
+ }
+
+ /**
+ * Runs the device / profile owner-specific operation.
+ *
+ * <p>MUST be overridden if {@link #getSafetyAwareOperations()} is overridden as well.
+ */
+ protected void runOperation(DevicePolicyManager dpm, int operation,
+ boolean overloaded) {
+ throwUnsupportedOperationException(operation, overloaded);
+ }
+
+ /**
+ * Throws a {@link UnsupportedOperationException} then the given {@code operation} is not
+ * supported.
+ */
+ protected final void throwUnsupportedOperationException(@DevicePolicyOperation int operation,
+ boolean overloaded) {
+ throw new UnsupportedOperationException(
+ "Unsupported operation " + getOperationName(operation, overloaded));
+ }
+
+ private void safeOperationTest(DevicePolicyManager dpm, List<String> failures,
+ @DevicePolicyOperation int operation, boolean overloaded) {
+ String name = getOperationName(operation, overloaded);
+ try {
+ setOperationUnsafe(operation);
+ runCommonOrSpecificOperation(dpm, operation, overloaded);
+ Log.e(TAG, name + " didn't throw an UnsafeStateException");
+ failures.add(name);
+ } catch (UnsafeStateException e) {
+ Log.d(TAG, name + " failed as expected: " + e);
+ } catch (Exception e) {
+ Log.e(TAG, name + " threw unexpected exception", e);
+ failures.add(name + "(" + e + ")");
+ }
+ }
+
+ private String getOperationName(@DevicePolicyOperation int operation, boolean overloaded) {
+ String name = operationToString(operation);
+ return overloaded ? name + "(OVERLOADED)" : name;
+ }
+
+ private void runCommonOrSpecificOperation(DevicePolicyManager dpm,
+ @DevicePolicyOperation int operation, boolean overloaded) {
+ String name = getOperationName(operation, overloaded);
+ Log.v(TAG, "runOperation(): " + name);
+ switch (operation) {
+ case OPERATION_LOCK_NOW:
+ if (overloaded) {
+ dpm.lockNow(/* flags= */ 0);
+ } else {
+ dpm.lockNow();
+ }
+ break;
+ default:
+ runOperation(dpm, operation, overloaded);
+ }
+ }
+
+ private void setOperationUnsafe(@DevicePolicyOperation int operation) {
+ // TODO(b/172376923): use a proper DevicePolicyManager command
+ String cmd = "cmd device_policy set-operation-safe " + operation + " false";
+ Log.v(TAG, "Running '" + cmd + "'");
+ SystemUtil.runShellCommand(cmd);
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 41fbaf9..2686de2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -30,16 +30,11 @@
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.util.LocationModeSetter;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
-import com.android.tradefed.device.DeviceNotAvailableException;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -1008,6 +1003,14 @@
}
}
+ @Test
+ public void testDevicePolicySafetyCheckerIntegration() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ executeDeviceTestMethod(".DevicePolicySafetyCheckerIntegrationTest", "testAllOperations");
+ }
+
private void executeDeviceOwnerTest(String testClassName) throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
index 35015df..1246246 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
@@ -86,6 +86,19 @@
executeProfileOwnerTest("BackupServicePoliciesTest");
}
+ @Test
+ public void testDevicePolicySafetyCheckerIntegration() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ if (true) {
+ // TODO(b/172376923): currently disabled as PO is not properly set to run lockNow()
+ return;
+ }
+
+ executeProfileOwnerTest("DevicePolicySafetyCheckerIntegrationTest");
+ }
+
@Override
public void tearDown() throws Exception {
if (mHasFeature) {