diff --git a/TEST_MAPPING b/TEST_MAPPING index 1e0b58ef973c..55fa5ed13d79 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -8,5 +8,10 @@ } ] } + ], + "postsubmit-managedprofile-stress": [ + { + "name": "ManagedProfileLifecycleStressTest" + } ] } diff --git a/tests/ManagedProfileLifecycleStressTest/Android.bp b/tests/ManagedProfileLifecycleStressTest/Android.bp new file mode 100644 index 000000000000..639ce3cfe935 --- /dev/null +++ b/tests/ManagedProfileLifecycleStressTest/Android.bp @@ -0,0 +1,23 @@ +// Copyright (C) 2019 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. + +java_test_host { + name: "ManagedProfileLifecycleStressTest", + srcs: ["src/**/*.java"], + libs: ["tradefed"], + test_suites: ["device-tests"], + target_required: [ + "DummyDPC", + ], +} diff --git a/tests/ManagedProfileLifecycleStressTest/AndroidTest.xml b/tests/ManagedProfileLifecycleStressTest/AndroidTest.xml new file mode 100644 index 000000000000..e7dbc5118457 --- /dev/null +++ b/tests/ManagedProfileLifecycleStressTest/AndroidTest.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp new file mode 100644 index 000000000000..d95af340337e --- /dev/null +++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp @@ -0,0 +1,20 @@ +// Copyright (C) 2019 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. + +android_test { + name: "DummyDPC", + defaults: ["cts_defaults"], + srcs: ["src/**/*.java"], + sdk_version: "current", +} diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/AndroidManifest.xml b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/AndroidManifest.xml new file mode 100644 index 000000000000..860940d4e025 --- /dev/null +++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/res/xml/device_admin.xml b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/res/xml/device_admin.xml new file mode 100644 index 000000000000..4b3581e3e8da --- /dev/null +++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/res/xml/device_admin.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/src/com/android/dummydpc/DummyDeviceAdminReceiver.java b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/src/com/android/dummydpc/DummyDeviceAdminReceiver.java new file mode 100644 index 000000000000..92190b73b0ff --- /dev/null +++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/src/com/android/dummydpc/DummyDeviceAdminReceiver.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 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.dummydpc; + +import android.app.admin.DeviceAdminReceiver; + +/** + * Empty admin to use as a managed profile owner. + */ +public class DummyDeviceAdminReceiver extends DeviceAdminReceiver { +} + diff --git a/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java new file mode 100644 index 000000000000..e323592d40ca --- /dev/null +++ b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 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.test.stress; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.android.tradefed.log.LogUtil.CLog; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A test to exercise Android Framework parts related to creating, starting, stopping, and deleting + * a managed profile as much as possible. The aim is to catch any issues in this code before it + * affects managed profile CTS tests. + */ +@RunWith(DeviceJUnit4ClassRunner.class) +public class ManagedProfileLifecycleStressTest extends BaseHostJUnit4Test { + // Stop the test once this time limit has been reached. 25 minutes used as a limit to make total + // test time less than 30 minutes, so that it can be put into presubmit. + private static final int TIME_LIMIT_MINUTES = 25; + + private static final String DUMMY_DPC_APK = "DummyDPC.apk"; + private static final String DUMMY_DPC_COMPONENT = + "com.android.dummydpc/com.android.dummydpc.DummyDeviceAdminReceiver"; + private static final Pattern CREATE_USER_OUTPUT_REGEX = + Pattern.compile("Success: created user id (\\d+)"); + + /** + * Create, start, and kill managed profiles in a loop. + */ + @Test + public void testCreateStartDelete() throws Exception { + int iteration = 0; + final long deadline = System.nanoTime() + TimeUnit.MINUTES.toNanos(TIME_LIMIT_MINUTES); + while (System.nanoTime() < deadline) { + iteration++; + CLog.w("Iteration N" + iteration); + final int userId = createManagedProfile(); + startUser(userId); + installPackageAsUser(DUMMY_DPC_APK, true /* grantPermissions */, userId, "-t"); + setProfileOwner(DUMMY_DPC_COMPONENT, userId); + removeUser(userId); + } + CLog.w("Completed " + iteration + " iterations."); + } + + private int createManagedProfile() throws Exception { + final String output = getDevice().executeShellCommand( + "pm create-user --profileOf 0 --managed TestProfile"); + final Matcher matcher = CREATE_USER_OUTPUT_REGEX.matcher(output.trim()); + if (!matcher.matches() || matcher.groupCount() != 1) { + fail("user creation failed, output: " + output); + } + return Integer.parseInt(matcher.group(1)); + } + + private void setProfileOwner(String componentName, int userId) throws Exception { + String command = "dpm set-profile-owner --user " + userId + " '" + componentName + "'"; + String commandOutput = getDevice().executeShellCommand(command); + assertTrue("Unexpected dpm output: " + commandOutput, commandOutput.startsWith("Success:")); + } + + private void removeUser(int userId) throws Exception { + final String output = getDevice().executeShellCommand("pm remove-user " + userId).trim(); + assertEquals("Unexpected pm output: " + output, "Success: removed user", output); + } + + private void startUser(int userId) throws Exception { + final String output = getDevice().executeShellCommand("am start-user -w " + userId).trim(); + assertEquals("Unexpected am output: " + output, "Success: user started", output); + } +}