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) {