Add CTS tests for activating malformed Device Admins.
Bug: 9224129
Change-Id: I74293a2f8b46f4bd5e83bdf9810b56a6dfee9c8d
diff --git a/tests/deviceadmin/AndroidManifest.xml b/tests/deviceadmin/AndroidManifest.xml
index f851e6c..69bc74d 100644
--- a/tests/deviceadmin/AndroidManifest.xml
+++ b/tests/deviceadmin/AndroidManifest.xml
@@ -40,6 +40,60 @@
</intent-filter>
</receiver>
+ <!-- Helper Activity used by Device Admin activation tests -->
+ <activity android:name="android.deviceadmin.cts.CtsDeviceAdminActivationTestActivity"
+ android:label="Device Admin activation test" />
+
+ <!-- Broken device admin: meta-data missing -->
+ <receiver android:name="android.deviceadmin.cts.CtsDeviceAdminBrokenReceiver"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+
+ <!-- Broken device admin: filter doesn't match an Intent with action
+ android.app.action.DEVICE_ADMIN_ENABLED and nothing else (e.g.,
+ data) set -->
+ <receiver android:name="android.deviceadmin.cts.CtsDeviceAdminBrokenReceiver2"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <data android:scheme="https" />
+ </intent-filter>
+ </receiver>
+
+ <!-- Broken device admin: meta-data element doesn't point to valid
+ Device Admin configuration/description -->
+ <receiver android:name="android.deviceadmin.cts.CtsDeviceAdminBrokenReceiver3"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/broken_device_admin" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+
+ <!-- Broken device admin: filter doesn't match Intents with action
+ android.app.action.DEVICE_ADMIN_ENABLED -->
+ <receiver android:name="android.deviceadmin.cts.CtsDeviceAdminBrokenReceiver4"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
+ </intent-filter>
+ </receiver>
+
+ <!-- Broken device admin: no intent-filter -->
+ <receiver android:name="android.deviceadmin.cts.CtsDeviceAdminBrokenReceiver5"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin" />
+ </receiver>
+
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/tests/deviceadmin/res/xml/broken_device_admin.xml b/tests/deviceadmin/res/xml/broken_device_admin.xml
new file mode 100644
index 0000000..937dabd
--- /dev/null
+++ b/tests/deviceadmin/res/xml/broken_device_admin.xml
@@ -0,0 +1,18 @@
+<!--
+ * Copyright (C) 2013 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.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" />
+
diff --git a/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminActivationTestActivity.java b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminActivationTestActivity.java
new file mode 100644
index 0000000..a8c5051
--- /dev/null
+++ b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminActivationTestActivity.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 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.deviceadmin.cts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+/**
+ * Helper {@link Activity} for CTS tests of Device Admin activation. The {@code Activity}
+ * enables tests to capture the invocations of its {@link #onActivityResult(int, int, Intent)} by
+ * providing a {@link OnActivityResultListener}.
+ */
+public class CtsDeviceAdminActivationTestActivity extends Activity {
+ public interface OnActivityResultListener {
+ void onActivityResult(int requestCode, int resultCode, Intent data);
+ }
+
+ private OnActivityResultListener mOnActivityResultListener;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Dismiss keyguard and keep screen on while this Activity is displayed.
+ // This is needed because on older platforms, when the keyguard is on, onActivityResult is
+ // not invoked when a Device Admin activation is rejected without user interaction.
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+ public void setOnActivityResultListener(OnActivityResultListener listener) {
+ mOnActivityResultListener = listener;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mOnActivityResultListener != null) {
+ mOnActivityResultListener.onActivityResult(requestCode, resultCode, data);
+ return;
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+}
diff --git a/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver.java b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver.java
new file mode 100644
index 0000000..f64c6b6
--- /dev/null
+++ b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 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.deviceadmin.cts;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class CtsDeviceAdminBrokenReceiver extends DeviceAdminReceiver {
+}
diff --git a/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver2.java b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver2.java
new file mode 100644
index 0000000..492a7cb
--- /dev/null
+++ b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver2.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 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.deviceadmin.cts;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class CtsDeviceAdminBrokenReceiver2 extends DeviceAdminReceiver {
+}
diff --git a/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver3.java b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver3.java
new file mode 100644
index 0000000..92bc878
--- /dev/null
+++ b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver3.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 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.deviceadmin.cts;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class CtsDeviceAdminBrokenReceiver3 extends DeviceAdminReceiver {
+}
diff --git a/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver4.java b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver4.java
new file mode 100644
index 0000000..53f15a7
--- /dev/null
+++ b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver4.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 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.deviceadmin.cts;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class CtsDeviceAdminBrokenReceiver4 extends DeviceAdminReceiver {
+}
diff --git a/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver5.java b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver5.java
new file mode 100644
index 0000000..2dc7100
--- /dev/null
+++ b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminBrokenReceiver5.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 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.deviceadmin.cts;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class CtsDeviceAdminBrokenReceiver5 extends DeviceAdminReceiver {
+}
diff --git a/tests/tests/admin/src/android/admin/cts/DeviceAdminActivationTest.java b/tests/tests/admin/src/android/admin/cts/DeviceAdminActivationTest.java
new file mode 100644
index 0000000..7e09989
--- /dev/null
+++ b/tests/tests/admin/src/android/admin/cts/DeviceAdminActivationTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2013 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.admin.cts;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.deviceadmin.cts.CtsDeviceAdminBrokenReceiver;
+import android.deviceadmin.cts.CtsDeviceAdminBrokenReceiver2;
+import android.deviceadmin.cts.CtsDeviceAdminBrokenReceiver3;
+import android.deviceadmin.cts.CtsDeviceAdminBrokenReceiver4;
+import android.deviceadmin.cts.CtsDeviceAdminBrokenReceiver5;
+import android.deviceadmin.cts.CtsDeviceAdminReceiver;
+import android.deviceadmin.cts.CtsDeviceAdminActivationTestActivity;
+import android.deviceadmin.cts.CtsDeviceAdminActivationTestActivity.OnActivityResultListener;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * Tests for the standard way of activating a Device Admin: by starting system UI via an
+ * {@link Intent} with {@link DevicePolicyManager#ACTION_ADD_DEVICE_ADMIN}. The test requires that
+ * the {@code CtsDeviceAdmin.apk} be installed.
+ */
+public class DeviceAdminActivationTest
+ extends ActivityInstrumentationTestCase2<CtsDeviceAdminActivationTestActivity> {
+
+ // IMPLEMENTATION NOTE: Because Device Admin activation requires the use of
+ // Activity.startActivityForResult, this test creates an empty Activity which then invokes
+ // startActivityForResult.
+
+ private static final int REQUEST_CODE_ACTIVATE_ADMIN = 1;
+
+ /**
+ * Maximum duration of time (milliseconds) after which the effects of programmatic actions in
+ * this test should have affected the UI.
+ */
+ private static final int UI_EFFECT_TIMEOUT_MILLIS = 5000;
+
+ /**
+ * Monitor guarding access to {@link #mLastOnActivityResultResultCode} and which is notified
+ * every time {@code onActivityResult} of the {@code CtsDeviceAdminActivationTestActivity} is
+ * invoked.
+ */
+ private final Object mOnActivityResultListenerLock = new Object();
+
+ /**
+ * Result code of the most recent invocation of
+ * {@code CtsDeviceAdminActivationTestActivity.onActivityResult} or {@code null} if no
+ * invocations have occured yet.
+ *
+ * @GuardedBy {@link #mOnActivityResultListenerLock}
+ */
+ private Integer mLastOnActivityResultResultCode;
+
+ public DeviceAdminActivationTest() {
+ super(CtsDeviceAdminActivationTestActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ getActivity().setOnActivityResultListener(new OnActivityResultListener() {
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode != REQUEST_CODE_ACTIVATE_ADMIN) {
+ return;
+ }
+ synchronized (mOnActivityResultListenerLock) {
+ mLastOnActivityResultResultCode = resultCode;
+ mOnActivityResultListenerLock.notifyAll();
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ finishActivateDeviceAdminActivity();
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ public void testActivateGoodReceiverDisplaysActivationUi() throws Exception {
+ startAddDeviceAdminActivityForResult(CtsDeviceAdminReceiver.class);
+ assertWithTimeoutOnActivityResultNotInvoked();
+ // The UI is up and running. Assert that dismissing the UI returns the corresponding result
+ // to the test activity.
+ finishActivateDeviceAdminActivity();
+ assertWithTimeoutOnActivityResultInvokedWithResultCode(Activity.RESULT_CANCELED);
+ }
+
+ public void testActivateBrokenReceiverFails() throws Exception {
+ startAddDeviceAdminActivityForResult(CtsDeviceAdminBrokenReceiver.class);
+ assertWithTimeoutOnActivityResultInvokedWithResultCode(Activity.RESULT_CANCELED);
+ }
+
+ public void testActivateBrokenReceiver2Fails() throws Exception {
+ startAddDeviceAdminActivityForResult(CtsDeviceAdminBrokenReceiver2.class);
+ assertWithTimeoutOnActivityResultInvokedWithResultCode(Activity.RESULT_CANCELED);
+ }
+
+ public void testActivateBrokenReceiver3Fails() throws Exception {
+ startAddDeviceAdminActivityForResult(CtsDeviceAdminBrokenReceiver3.class);
+ assertWithTimeoutOnActivityResultInvokedWithResultCode(Activity.RESULT_CANCELED);
+ }
+
+ public void testActivateBrokenReceiver4Fails() throws Exception {
+ startAddDeviceAdminActivityForResult(CtsDeviceAdminBrokenReceiver4.class);
+ assertWithTimeoutOnActivityResultInvokedWithResultCode(Activity.RESULT_CANCELED);
+ }
+
+ public void testActivateBrokenReceiver5Fails() throws Exception {
+ startAddDeviceAdminActivityForResult(CtsDeviceAdminBrokenReceiver5.class);
+ assertWithTimeoutOnActivityResultInvokedWithResultCode(Activity.RESULT_CANCELED);
+ }
+
+ private void startAddDeviceAdminActivityForResult(Class<?> receiverClass) {
+ getActivity().startActivityForResult(
+ getAddDeviceAdminIntent(receiverClass),
+ REQUEST_CODE_ACTIVATE_ADMIN);
+ }
+
+ private Intent getAddDeviceAdminIntent(Class<?> receiverClass) {
+ return new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
+ .putExtra(
+ DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ new ComponentName(
+ getInstrumentation().getTargetContext(),
+ receiverClass));
+ }
+
+ private void assertWithTimeoutOnActivityResultNotInvoked() {
+ SystemClock.sleep(UI_EFFECT_TIMEOUT_MILLIS);
+ synchronized (mOnActivityResultListenerLock) {
+ assertNull(mLastOnActivityResultResultCode);
+ }
+ }
+
+ private void assertWithTimeoutOnActivityResultInvokedWithResultCode(int expectedResultCode)
+ throws Exception {
+ long deadlineMillis = SystemClock.elapsedRealtime() + UI_EFFECT_TIMEOUT_MILLIS;
+ synchronized (mOnActivityResultListenerLock) {
+ while (true) {
+ if (mLastOnActivityResultResultCode != null) {
+ // onActivityResult has been invoked -- check the arguments
+ assertEquals(expectedResultCode, (int) mLastOnActivityResultResultCode);
+ break;
+ }
+
+ // onActivityResult has not yet been invoked -- wait until it is
+ long millisTillDeadline = deadlineMillis - SystemClock.elapsedRealtime();
+ if (millisTillDeadline <= 0) {
+ fail("onActivityResult not invoked within " + UI_EFFECT_TIMEOUT_MILLIS + " ms");
+ break;
+ }
+
+ mOnActivityResultListenerLock.wait(millisTillDeadline);
+ }
+ }
+ }
+
+ private void finishActivateDeviceAdminActivity() {
+ getActivity().finishActivity(REQUEST_CODE_ACTIVATE_ADMIN);
+ }
+}