Device Admin CTS Verifier Tests

Bug 4562272

1. Screen Lock Test

   Checks that the screen locks immediately after calling
   DevicePolicyManager's lockNow() method.

2. Policy Serialization Test

   Checks that policies are saved and loaded properly across
   reboots.

Change-Id: I47edeed4118ee5b3c8181df43eab43dd9a730be2
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 8c900e2..d9d3763 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -22,8 +22,8 @@
       
     <uses-sdk android:minSdkVersion="5"></uses-sdk>
 
-    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     
@@ -44,6 +44,35 @@
         <provider android:name=".TestResultsProvider" 
                 android:authorities="com.android.cts.verifier.testresultsprovider" />
                 
+        <activity android:name=".admin.PolicySerializationTestActivity"
+                android:label="@string/da_policy_serialization_test"
+                android:configChanges="keyboardHidden|orientation">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_device_admin" />
+        </activity>
+
+        <activity android:name=".admin.ScreenLockTestActivity"
+                android:label="@string/da_screen_lock_test"
+                android:configChanges="keyboardHidden|orientation">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_device_admin" />
+        </activity>
+
+        <receiver android:name=".admin.TestDeviceAdminReceiver"
+                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" />
+            </intent-filter>
+        </receiver>
+
         <activity android:name=".bluetooth.BluetoothTestActivity"
                 android:label="@string/bluetooth_test"
                 android:configChanges="keyboardHidden|orientation">
diff --git a/apps/CtsVerifier/res/layout/da_policy_main.xml b/apps/CtsVerifier/res/layout/da_policy_main.xml
new file mode 100644
index 0000000..b80649e
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/da_policy_main.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        >
+
+    <ListView android:id="@id/android:list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            />
+
+    <TextView android:id="@id/android:empty"
+            android:layout_gravity="center_vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:padding="10dip"
+            android:text="@string/da_no_policy"
+            android:textSize="18dip"
+            />
+
+    <LinearLayout android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            >
+        <Button android:id="@+id/generate_policy_button"
+                android:layout_width="1dip"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/da_generate_policy"
+                />
+        <Button android:id="@+id/apply_policy_button"
+                android:layout_width="1dip"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/da_apply_policy"
+                />
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/da_screen_lock_main.xml b/apps/CtsVerifier/res/layout/da_screen_lock_main.xml
new file mode 100644
index 0000000..eaca05f
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/da_screen_lock_main.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:padding="10dip"
+        >
+
+    <Button android:id="@+id/da_force_lock_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:drawableTop="@android:drawable/ic_lock_lock"
+            android:text="@string/da_force_lock"
+            />
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index f2257a2..289050c 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -29,6 +29,7 @@
     <!-- Strings for TestListActivity -->
     <string name="test_list_title">Manual Test List</string>
     <string name="test_category_audio">Audio</string>
+    <string name="test_category_device_admin">Device Administration</string>
     <string name="test_category_networking">Networking</string>
     <string name="test_category_sensors">Sensors</string>
     <string name="test_category_security">Security</string>
@@ -42,6 +43,46 @@
     <string name="share_test_results">Share Test Results</string>
     <string name="test_results_error">Couldn\'t create test results report.</string>
 
+    <!-- Strings for Device Administration tests -->
+    <string name="da_policy_serialization_test">Policy Serialization Test</string>
+    <string name="da_policy_serialization_info">This test checks that a device policy is properly
+        saved and loaded across reboots.\n\nPress the \"Generate Policy\" button to create
+        a random policy. Then press the \"Apply Policy\" button to apply the policy. Reboot the
+        device and verify that all rows in the policy list are green. Red items indicate policy
+        settings that were not loaded properly.
+    </string>
+    <string name="da_no_policy">1. Press the \"Generate Policy\" to create a random device
+        policy\n\n2. Press \"Apply Policy\" to put the policy into effect.\n\n3. Reboot your
+        device and return to this test in the CTS Verifier.
+    </string>
+    <string name="da_generate_policy">Generate Policy</string>
+    <string name="da_apply_policy">Apply Policy</string>
+    <string name="da_random_policy">Random policy generated.</string>
+    <string name="da_policy_reboot">Reboot your device and return to this CTS Verifier test.</string>
+    <string name="da_password_quality">Password Quality</string>
+    <string name="da_password_quality_alphabetic">Alphabetic</string>
+    <string name="da_password_quality_alphanumeric">Alphanumeric</string>
+    <string name="da_password_quality_numeric">Numeric</string>
+    <string name="da_password_quality_something">Something</string>
+    <string name="da_password_quality_unspecified">Unspecified</string>
+    <string name="da_password_minimum_length">Minimum Password Length</string>
+    <string name="da_maximum_failed_passwords_for_wipe">Maximum Failed Passwords for Wipe</string>
+    <string name="da_maximum_time_to_lock">Maximum Time to Lock</string>
+    <string name="da_policy_info">Expected value: %1$s\nActual value: %2$s</string>
+
+    <string name="da_screen_lock_test">Screen Lock Test</string>
+    <string name="da_screen_lock_info">This test checks that the DevicePolicyManager\'s lockNow
+        method immediately locks the screen. It should lock the screen immediately despite any
+        settings that may specify a timeout.\n\nClick the \"Force Lock\" button to change
+        your password to \"12345\" and lock the screen. Your screen should be locked and require
+        the password to be entered.
+    </string>
+    <string name="da_force_lock">Force Lock</string>
+    <string name="da_lock_password_error">Couldn\'t change the password on the device.
+        Is there another active device administrator?</string>
+    <string name="da_lock_success">It appears the screen was locked successfully!</string>
+    <string name="da_lock_error">It does not look like the screen was locked...</string>
+
     <!-- Strings for BluetoothActivity -->
     <string name="bluetooth_test">Bluetooth Test</string>
     <string name="bluetooth_test_info">The Bluetooth Control tests check whether or not the device 
diff --git a/apps/CtsVerifier/res/xml/device_admin.xml b/apps/CtsVerifier/res/xml/device_admin.xml
new file mode 100644
index 0000000..49d705a
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/device_admin.xml
@@ -0,0 +1,25 @@
+<!-- Copyright (C) 2011 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.
+-->
+
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
+    <uses-policies>
+        <limit-password />
+        <watch-login />
+        <reset-password />
+        <force-lock />
+        <wipe-data />
+        <expire-password />
+    </uses-policies>
+</device-admin>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java
new file mode 100644
index 0000000..1d8056f
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2011 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.verifier.admin;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Test that checks that device policies are properly saved and loaded across reboots. The user
+ * clicks a button to generate a random policy and is then asked to reboot the device. When
+ * returning to the test, the activity checks that the device manager is reporting the values
+ * it set before the user rebooted the device.
+ */
+public class PolicySerializationTestActivity extends PassFailButtons.ListActivity {
+
+    /**
+     * Whether or not to load the expected policy from the preferences and check against
+     * what the {@link DevicePolicyManager} reports.
+     */
+    private static final String LOAD_EXPECTED_POLICY_PREFERENCE = "load-expected-policy";
+
+    private static final int ADD_DEVICE_ADMIN_REQUEST_CODE = 1;
+
+    private DevicePolicyManager mDevicePolicyManager;
+    private ComponentName mAdmin;
+
+    private List<PolicyItem<?>> mPolicyItems = new ArrayList<PolicyItem<?>>();
+    private PolicyAdapter mAdapter;
+
+    private View mGeneratePolicyButton;
+    private View mApplyPolicyButton;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.da_policy_main);
+        setInfoResources(R.string.da_policy_serialization_test,
+                R.string.da_policy_serialization_info, -1);
+        setPassFailButtonClickListeners();
+
+        mDevicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
+        mAdmin = TestDeviceAdminReceiver.getComponent(this);
+
+        mGeneratePolicyButton = findViewById(R.id.generate_policy_button);
+        mGeneratePolicyButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                generateRandomPolicy();
+                updateWidgets();
+            }
+        });
+
+        mApplyPolicyButton = findViewById(R.id.apply_policy_button);
+        mApplyPolicyButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                applyPolicy();
+            }
+        });
+
+        mPolicyItems.add(new PasswordQualityPolicy(this));
+        mPolicyItems.add(new PasswordMinimumLengthPolicy(this));
+        mPolicyItems.add(new MaximumFailedPasswordsForWipePolicy(this));
+        mPolicyItems.add(new MaximumTimeToLockPolicy(this));
+        mAdapter = new PolicyAdapter(this);
+        setListAdapter(mAdapter);
+
+        loadPolicy();
+        updateWidgets();
+    }
+
+    private void loadPolicy() {
+        mAdapter.clear();
+        SharedPreferences prefs = getPreferences(MODE_PRIVATE);
+        if (prefs.getBoolean(LOAD_EXPECTED_POLICY_PREFERENCE, true)) {
+            for (PolicyItem<?> item : mPolicyItems) {
+                item.loadExpectedValue(prefs);
+                item.loadActualValue(mDevicePolicyManager, mAdmin);
+                mAdapter.add(item);
+            }
+        }
+    }
+
+    private void generateRandomPolicy() {
+        Random random = new Random();
+        mAdapter.clear();
+        for (PolicyItem<?> item : mPolicyItems) {
+            item.setRandomExpectedValue(random);
+            item.resetActualValue();
+            mAdapter.add(item);
+        }
+
+        SharedPreferences prefs = getPreferences(MODE_PRIVATE);
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.clear();
+        editor.putBoolean(LOAD_EXPECTED_POLICY_PREFERENCE, false);
+        editor.apply();
+
+        Toast.makeText(this, R.string.da_random_policy, Toast.LENGTH_SHORT).show();
+    }
+
+    private void applyPolicy() {
+        Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
+        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+                TestDeviceAdminReceiver.getComponent(this));
+        startActivityForResult(intent, ADD_DEVICE_ADMIN_REQUEST_CODE);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case ADD_DEVICE_ADMIN_REQUEST_CODE:
+                handleAddDeviceAdminResult(resultCode, data);
+                break;
+        }
+    }
+
+    private void handleAddDeviceAdminResult(int resultCode, Intent data) {
+        if (resultCode == RESULT_OK) {
+            ComponentName admin = TestDeviceAdminReceiver.getComponent(this);
+            for (PolicyItem<?> item : mPolicyItems) {
+                item.applyExpectedValue(mDevicePolicyManager, admin);
+            }
+
+            SharedPreferences prefs = getPreferences(MODE_PRIVATE);
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.clear();
+            editor.putBoolean(LOAD_EXPECTED_POLICY_PREFERENCE, true);
+            for (PolicyItem<?> item : mPolicyItems) {
+                item.saveExpectedValue(editor);
+            }
+            editor.apply();
+            showRebootDialog();
+        }
+    }
+
+    private void showRebootDialog() {
+        new AlertDialog.Builder(this)
+            .setIcon(android.R.drawable.ic_dialog_info)
+            .setTitle(R.string.da_policy_serialization_test)
+            .setMessage(R.string.da_policy_reboot)
+            .setPositiveButton(android.R.string.ok, null)
+            .show();
+    }
+
+    private void updateWidgets() {
+        mApplyPolicyButton.setEnabled(!mAdapter.isEmpty());
+
+        // All items need to have been serialized properly for the pass button to activate.
+        boolean enablePass = !mAdapter.isEmpty();
+        int numItems = mAdapter.getCount();
+        for (int i = 0; i < numItems; i++) {
+            PolicyItem<?> item = mAdapter.getItem(i);
+            if (!item.matchesExpectedValue()) {
+                enablePass = false;
+            }
+        }
+        getPassButton().setEnabled(enablePass);
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        super.onListItemClick(l, v, position, id);
+        PolicyItem<?> item = mAdapter.getItem(position);
+        new AlertDialog.Builder(this)
+            .setIcon(android.R.drawable.ic_dialog_info)
+            .setTitle(item.getDisplayName())
+            .setMessage(getString(R.string.da_policy_info,
+                    item.getDisplayExpectedValue(),
+                    item.getDisplayActualValue()))
+            .setPositiveButton(android.R.string.ok, null)
+            .show();
+    }
+
+    static class PolicyAdapter extends ArrayAdapter<PolicyItem<?>> {
+
+        public PolicyAdapter(Context context) {
+            super(context, android.R.layout.simple_list_item_1);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TextView view = (TextView) super.getView(position, convertView, parent);
+
+            PolicyItem<?> item = getItem(position);
+            int backgroundResource = 0;
+            int iconResource = 0;
+            if (item.getExpectedValue() != null && item.getActualValue() != null) {
+                if (item.matchesExpectedValue()) {
+                    backgroundResource = R.drawable.test_pass_gradient;
+                    iconResource = R.drawable.fs_good;
+                } else {
+                    backgroundResource = R.drawable.test_fail_gradient;
+                    iconResource = R.drawable.fs_error;
+                }
+            }
+            view.setBackgroundResource(backgroundResource);
+            view.setPadding(10, 0, 10, 0);
+            view.setCompoundDrawablePadding(10);
+            view.setCompoundDrawablesWithIntrinsicBounds(0, 0, iconResource, 0);
+
+            return view;
+        }
+    }
+
+    interface PolicyItem<T> {
+
+        void setRandomExpectedValue(Random random);
+
+        void applyExpectedValue(DevicePolicyManager deviceManager, ComponentName admin);
+
+        void loadExpectedValue(SharedPreferences prefs);
+
+        void saveExpectedValue(SharedPreferences.Editor editor);
+
+        void resetActualValue();
+
+        void loadActualValue(DevicePolicyManager deviceManager, ComponentName admin);
+
+        String getDisplayName();
+
+        T getExpectedValue();
+
+        String getDisplayExpectedValue();
+
+        T getActualValue();
+
+        String getDisplayActualValue();
+
+        boolean matchesExpectedValue();
+    }
+
+    static abstract class BasePolicyItem<T> implements PolicyItem<T> {
+        private String mDisplayName;
+        private T mExpectedValue;
+        private T mActualValue;
+
+        BasePolicyItem(Context context, int nameResId) {
+            mDisplayName = context.getString(nameResId);
+        }
+
+        @Override
+        public final void setRandomExpectedValue(Random random) {
+            mExpectedValue = getRandomExpectedValue(random);
+        }
+
+        protected abstract T getRandomExpectedValue(Random random);
+
+        @Override
+        public final void loadExpectedValue(SharedPreferences prefs) {
+            mExpectedValue = getPreferencesValue(prefs);
+        }
+
+        protected abstract T getPreferencesValue(SharedPreferences prefs);
+
+        @Override
+        public final void loadActualValue(DevicePolicyManager deviceManager, ComponentName admin) {
+            mActualValue = getDeviceManagerValue(deviceManager, admin);
+        }
+
+        protected abstract T getDeviceManagerValue(DevicePolicyManager deviceManager,
+                ComponentName admin);
+
+        @Override
+        public final void resetActualValue() {
+            mActualValue = null;
+        }
+
+        @Override
+        public final String getDisplayName() {
+            return mDisplayName;
+        }
+
+        @Override
+        public final T getExpectedValue() {
+            return mExpectedValue;
+        }
+
+        @Override
+        public final String getDisplayExpectedValue() {
+            return mExpectedValue != null ? getDisplayValue(mExpectedValue) : "";
+        }
+
+        @Override
+        public final T getActualValue() {
+            return mActualValue;
+        }
+
+        @Override
+        public final String getDisplayActualValue() {
+            return mActualValue != null ? getDisplayValue(mActualValue) : "";
+        }
+
+        protected String getDisplayValue(T value) {
+            return "" + value;
+        }
+
+        @Override
+        public final boolean matchesExpectedValue() {
+            return mExpectedValue != null && mExpectedValue.equals(mActualValue);
+        }
+
+        @Override
+        public String toString() {
+            return getDisplayName();
+        }
+    }
+
+    static abstract class IntegerPolicyItem extends BasePolicyItem<Integer> {
+
+        private String mPreferenceKey;
+
+        IntegerPolicyItem(Context context, int nameResId, String preferenceKey) {
+            super(context, nameResId);
+            mPreferenceKey = preferenceKey;
+        }
+
+        @Override
+        protected final Integer getPreferencesValue(SharedPreferences prefs) {
+            return prefs.getInt(mPreferenceKey, -1);
+        }
+
+        @Override
+        public final void saveExpectedValue(Editor editor) {
+            editor.putInt(mPreferenceKey, getExpectedValue());
+        }
+    }
+
+    static abstract class LongPolicyItem extends BasePolicyItem<Long> {
+
+        private String mPreferenceKey;
+
+        LongPolicyItem(Context context, int nameResId, String preferenceKey) {
+            super(context, nameResId);
+            mPreferenceKey = preferenceKey;
+        }
+
+        @Override
+        protected final Long getPreferencesValue(SharedPreferences prefs) {
+            return prefs.getLong(mPreferenceKey, -1);
+        }
+
+        @Override
+        public final void saveExpectedValue(Editor editor) {
+            editor.putLong(mPreferenceKey, getExpectedValue());
+        }
+    }
+
+    static class PasswordQualityPolicy extends IntegerPolicyItem {
+
+        private final Context mContext;
+
+        public PasswordQualityPolicy(Context context) {
+            super(context, R.string.da_password_quality, "password-quality");
+            mContext = context;
+        }
+
+        @Override
+        protected Integer getRandomExpectedValue(Random random) {
+            int[] passwordQualities = new int[] {
+                    DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
+                    DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
+                    DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
+                    DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
+                    DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
+            };
+
+            int index = random.nextInt(passwordQualities.length);
+            return passwordQualities[index];
+        }
+
+        @Override
+        public void applyExpectedValue(DevicePolicyManager deviceManager, ComponentName admin) {
+            deviceManager.setPasswordQuality(admin, getExpectedValue());
+        }
+
+        @Override
+        protected Integer getDeviceManagerValue(DevicePolicyManager deviceManager,
+                ComponentName admin) {
+            return deviceManager.getPasswordQuality(admin);
+        }
+
+        @Override
+        protected String getDisplayValue(Integer value) {
+            switch (value) {
+                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+                    return mContext.getString(R.string.da_password_quality_alphabetic);
+                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+                    return mContext.getString(R.string.da_password_quality_alphanumeric);
+                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+                    return mContext.getString(R.string.da_password_quality_numeric);
+                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+                    return mContext.getString(R.string.da_password_quality_something);
+                case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
+                    return mContext.getString(R.string.da_password_quality_unspecified);
+                default:
+                    return Integer.toString(value);
+            }
+        }
+    }
+
+    static class PasswordMinimumLengthPolicy extends IntegerPolicyItem {
+
+        PasswordMinimumLengthPolicy(Context context) {
+            super(context, R.string.da_password_minimum_length, "password-minimum-length");
+        }
+
+        @Override
+        protected Integer getRandomExpectedValue(Random random) {
+            return random.nextInt(50);
+        }
+
+        @Override
+        public void applyExpectedValue(DevicePolicyManager deviceManager, ComponentName admin) {
+            deviceManager.setPasswordMinimumLength(admin, getExpectedValue());
+        }
+
+        @Override
+        protected Integer getDeviceManagerValue(DevicePolicyManager deviceManager,
+                ComponentName admin) {
+            return deviceManager.getPasswordMinimumLength(admin);
+        }
+    }
+
+    static class MaximumFailedPasswordsForWipePolicy extends IntegerPolicyItem {
+
+        MaximumFailedPasswordsForWipePolicy(Context context) {
+            super(context, R.string.da_maximum_failed_passwords_for_wipe,
+                    "maximum-failed-passwords-for-wipe");
+        }
+
+        @Override
+        protected Integer getRandomExpectedValue(Random random) {
+            return random.nextInt(50);
+        }
+
+        @Override
+        public void applyExpectedValue(DevicePolicyManager deviceManager, ComponentName admin) {
+            deviceManager.setMaximumFailedPasswordsForWipe(admin, getExpectedValue());
+        }
+
+        @Override
+        protected Integer getDeviceManagerValue(DevicePolicyManager deviceManager,
+                ComponentName admin) {
+            return deviceManager.getMaximumFailedPasswordsForWipe(admin);
+        }
+    }
+
+    static class MaximumTimeToLockPolicy extends LongPolicyItem {
+
+        MaximumTimeToLockPolicy(Context context) {
+            super(context, R.string.da_maximum_time_to_lock, "maximum-time-to-lock");
+        }
+
+        @Override
+        protected Long getRandomExpectedValue(Random random) {
+            return (long)(1000 + random.nextInt(60 * 60 * 1000));
+        }
+
+        @Override
+        public void applyExpectedValue(DevicePolicyManager deviceManager, ComponentName admin) {
+            deviceManager.setMaximumTimeToLock(admin, getExpectedValue());
+        }
+
+        @Override
+        protected Long getDeviceManagerValue(DevicePolicyManager deviceManager,
+                ComponentName admin) {
+            return deviceManager.getMaximumTimeToLock(admin);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java
new file mode 100644
index 0000000..4d6adf3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2011 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.verifier.admin;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.app.AlertDialog;
+import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+public class ScreenLockTestActivity extends PassFailButtons.Activity {
+
+    private static final String NEW_PASSWORD = "12345";
+
+    private static final int ADD_DEVICE_ADMIN_REQUEST_CODE = 1;
+
+    private ScreenOffReceiver mReceiver;
+
+    private Button mForceLockButton;
+
+    private DevicePolicyManager mDevicePolicyManager;
+
+    private KeyguardManager mKeyguardManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.da_screen_lock_main);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.da_screen_lock_test, R.string.da_screen_lock_info, -1);
+
+        mDevicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
+        mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
+
+        getPassButton().setEnabled(false);
+
+        mForceLockButton = (Button) findViewById(R.id.da_force_lock_button);
+        mForceLockButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                sendAddDeviceAdminIntent();
+            }
+        });
+
+        mReceiver = new ScreenOffReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        registerReceiver(mReceiver, filter);
+    }
+
+    private void sendAddDeviceAdminIntent() {
+        Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
+        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+                TestDeviceAdminReceiver.getComponent(this));
+        startActivityForResult(intent, ADD_DEVICE_ADMIN_REQUEST_CODE);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case ADD_DEVICE_ADMIN_REQUEST_CODE:
+                handleAddDeviceAdminResult(resultCode, data);
+                break;
+        }
+    }
+
+    private void handleAddDeviceAdminResult(int resultCode, Intent data) {
+        if (resultCode == RESULT_OK) {
+            mDevicePolicyManager.setPasswordMinimumLength(TestDeviceAdminReceiver
+                    .getComponent(this), NEW_PASSWORD.length());
+            mDevicePolicyManager.setPasswordQuality(TestDeviceAdminReceiver
+                    .getComponent(this), DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+            if (mDevicePolicyManager.resetPassword(NEW_PASSWORD, 0)) {
+                mDevicePolicyManager.lockNow();
+            } else {
+                new AlertDialog.Builder(this)
+                    .setTitle(R.string.da_screen_lock_test)
+                    .setMessage(R.string.da_lock_password_error)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setPositiveButton(android.R.string.ok, null)
+                    .show();
+            }
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+    }
+
+    private class ScreenOffReceiver extends BroadcastReceiver {
+
+        private static final int LOCK_CHECK_DELAY = 1000;
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mForceLockButton.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    boolean lockSuccess = mKeyguardManager.inKeyguardRestrictedInputMode();
+                    getPassButton().setEnabled(lockSuccess);
+
+                    int iconId = lockSuccess
+                            ? android.R.drawable.ic_dialog_info
+                            : android.R.drawable.ic_dialog_alert;
+                    int messageId = lockSuccess
+                            ? R.string.da_lock_success
+                            : R.string.da_lock_error;
+                    new AlertDialog.Builder(ScreenLockTestActivity.this)
+                        .setTitle(R.string.da_screen_lock_test)
+                        .setMessage(messageId)
+                        .setIcon(iconId)
+                        .setPositiveButton(android.R.string.ok, null)
+                        .show();
+                }
+            }, LOCK_CHECK_DELAY);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/TestDeviceAdminReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/TestDeviceAdminReceiver.java
new file mode 100644
index 0000000..5ecb36d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/TestDeviceAdminReceiver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 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.verifier.admin;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+
+public class TestDeviceAdminReceiver extends DeviceAdminReceiver {
+
+    public static ComponentName getComponent(Context context) {
+        return new ComponentName(context, TestDeviceAdminReceiver.class);
+    }
+}