Merge remote branch 'goog/honeycomb' into manualmerge

Conflicts:
	tools/host/src/com/android/cts/Version.java

Change-Id: I059dc4b100504988ab3b1ccbd0a0bd61c1b05787
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index a9a5dba..0a9c79e 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -31,6 +31,7 @@
 CTS_COVERAGE_TEST_CASE_LIST := \
 	CtsAccessibilityServiceTestCases \
 	CtsAccountManagerTestCases \
+	CtsAdminTestCases \
 	CtsAppTestCases \
 	CtsBluetoothTestCases \
 	CtsContentTestCases \
@@ -72,6 +73,7 @@
 CTS_TEST_CASE_LIST := \
 	TestDeviceSetup \
 	CtsDelegatingAccessibilityService \
+	CtsDeviceAdmin \
 	SignatureTest \
 	ApiDemos \
 	ApiDemosReferenceTest \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 9bdb37a..708f9a9 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -17,13 +17,13 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.android.cts.verifier"
-      android:versionCode="1"
-      android:versionName="1.0">
+      android:versionCode="3"
+      android:versionName="3.1_r2">
       
     <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">
@@ -56,15 +85,63 @@
         
         <activity android:name=".bluetooth.BluetoothToggleActivity"
                 android:label="@string/bt_toggle_bluetooth"
-                android:configChanges="keyboardHidden|orientation" />
+                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/bt_control" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+        </activity>
 
+        <activity android:name=".bluetooth.SecureServerActivity"
+                android:label="@string/bt_secure_server"
+                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/bt_device_communication" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+        </activity>
+        
+        <activity android:name=".bluetooth.InsecureServerActivity"
+                android:label="@string/bt_insecure_server"
+                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/bt_device_communication" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.SecureClientActivity"
+                android:label="@string/bt_secure_client"
+                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/bt_device_communication" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+        </activity>
+        
+        <activity android:name=".bluetooth.InsecureClientActivity"
+                android:label="@string/bt_insecure_client"
+                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/bt_device_communication" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+        </activity>       
+        
         <activity android:name=".bluetooth.DevicePickerActivity"
                 android:label="@string/bt_device_picker"
                 android:configChanges="keyboardHidden|orientation" />
 
-        <activity android:name=".bluetooth.MessageTestActivity"
-                android:configChanges="keyboardHidden|orientation" />
-
         <activity android:name=".suid.SuidFilesActivity" 
                 android:label="@string/suid_files"
                 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/layout/main.xml b/apps/CtsVerifier/res/layout/main.xml
index d6cb6cd..cdfb8f4 100644
--- a/apps/CtsVerifier/res/layout/main.xml
+++ b/apps/CtsVerifier/res/layout/main.xml
@@ -26,12 +26,21 @@
         android:text="@string/continue_button_text"
         />
     <TextView
-        android:id="@+id/welcome_text"
+        android:id="@+id/version_text"
         android:gravity="center"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_above="@+id/continue_button"
-        android:padding="10dip"
+        android:paddingBottom="10dip"
+        style="@style/VersionFont"
+        />
+    <TextView
+        android:id="@+id/welcome_text"
+        android:gravity="center"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_above="@+id/version_text"
+        android:paddingTop="10dip"
         android:text="@string/welcome_text"
         style="@style/WelcomeFont"
         />
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 08bd98b..7aa7458 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -16,6 +16,7 @@
 <resources>
     <string name="app_name">CTS Verifier</string>
     <string name="welcome_text">Welcome to the CTS Verifier!</string>
+    <string name="version_text">%1$s</string>
     <string name="continue_button_text">Continue</string>
 
     <string name="pass_button_text">Pass</string>
@@ -23,15 +24,12 @@
     <string name="fail_button_text">Fail</string>
 
     <!-- Strings for TestResultsReport -->
-    <string name="subject_header">[CTS Verifier %1$s]</string>
-    <string name="body_header">CTS Verifier %1$s Test Results</string>
-    <string name="pass_result">PASS</string>
-    <string name="fail_result">FAIL</string>
-    <string name="not_executed_result">NOT_EXECUTED</string>
+    <string name="subject_header">CTS Verifier %1$s - %2$s</string>
 
     <!-- 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>
@@ -43,6 +41,44 @@
     <string name="test_results_copied">Test results copied to clipboard.</string>
     <string name="share">Share</string>
     <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 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_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>
diff --git a/apps/CtsVerifier/res/values/styles.xml b/apps/CtsVerifier/res/values/styles.xml
index c5b05cb..c7fe859 100644
--- a/apps/CtsVerifier/res/values/styles.xml
+++ b/apps/CtsVerifier/res/values/styles.xml
@@ -3,4 +3,7 @@
     <style name="WelcomeFont" parent="@android:style/TextAppearance.Large">
         <item name="android:textColor">#9fbf3b</item>
     </style>
+    <style name="VersionFont" parent="@android:style/TextAppearance.Large">
+        <item name="android:textColor">#ffffff</item>
+    </style>
 </resources>
\ No newline at end of file
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/CtsVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierActivity.java
index e35674c..ee3184b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierActivity.java
@@ -20,8 +20,9 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
-import android.view.Window;
 import android.view.View.OnClickListener;
+import android.view.Window;
+import android.widget.TextView;
 
 /** {@link Activity} that displays an introduction to the verifier. */
 public class CtsVerifierActivity extends Activity {
@@ -40,6 +41,9 @@
             }
         };
 
+        TextView versionText = (TextView) findViewById(R.id.version_text);
+        versionText.setText(getString(R.string.version_text, Version.getVersionName(this)));
+
         findViewById(R.id.detective_logo).setOnClickListener(clickListener);
         findViewById(R.id.continue_button).setOnClickListener(clickListener);
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
index c563e13..fe41583 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
@@ -19,13 +19,10 @@
 import com.android.cts.verifier.TestListAdapter.TestListItem;
 
 import android.app.ListActivity;
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Intent;
-import android.database.ContentObserver;
 import android.os.Bundle;
-import android.os.Handler;
 import android.text.ClipboardManager;
+import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -33,21 +30,23 @@
 import android.widget.ListView;
 import android.widget.Toast;
 
+import java.io.IOException;
+
 /** {@link ListActivity} that displays a  list of manual tests. */
 public class TestListActivity extends ListActivity {
 
+    private static final String TAG = TestListActivity.class.getSimpleName();
+
     private static final int LAUNCH_TEST_REQUEST_CODE = 1;
 
+    private TestListAdapter mAdapter;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        TestListAdapter adapter = new TestListAdapter(this);
-        setListAdapter(adapter);
-
-        TestResultContentObserver observer = new TestResultContentObserver(adapter);
-        ContentResolver resolver = getContentResolver();
-        resolver.registerContentObserver(TestResultsProvider.RESULTS_CONTENT_URI, true, observer);
+        mAdapter = new TestListAdapter(this, null);
+        setListAdapter(mAdapter);
+        mAdapter.loadTestResults();
     }
 
     /** Launch the activity when its {@link ListView} item is clicked. */
@@ -59,17 +58,11 @@
     }
 
     private Intent getIntent(int position) {
-        TestListAdapter adapter = getListAdapter();
-        TestListItem item = adapter.getItem(position);
+        TestListItem item = mAdapter.getItem(position);
         return item.intent;
     }
 
     @Override
-    public TestListAdapter getListAdapter() {
-        return (TestListAdapter) super.getListAdapter();
-    }
-
-    @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         switch (requestCode) {
@@ -85,18 +78,7 @@
     private void handleLaunchTestResult(int resultCode, Intent data) {
         if (resultCode == RESULT_OK) {
             TestResult testResult = TestResult.fromActivityResult(resultCode, data);
-            ContentValues values = new ContentValues(2);
-            values.put(TestResultsProvider.COLUMN_TEST_RESULT, testResult.getResult());
-            values.put(TestResultsProvider.COLUMN_TEST_NAME, testResult.getName());
-
-            ContentResolver resolver = getContentResolver();
-            int numUpdated = resolver.update(TestResultsProvider.RESULTS_CONTENT_URI, values,
-                    TestResultsProvider.COLUMN_TEST_NAME + " = ?",
-                    new String[] {testResult.getName()});
-
-            if (numUpdated == 0) {
-                resolver.insert(TestResultsProvider.RESULTS_CONTENT_URI, values);
-            }
+            mAdapter.setTestResult(testResult);
         }
     }
 
@@ -128,47 +110,34 @@
     }
 
     private void handleClearItemSelected() {
-        ContentResolver resolver = getContentResolver();
-        resolver.delete(TestResultsProvider.RESULTS_CONTENT_URI, "1", null);
+        mAdapter.clearTestResults();
         Toast.makeText(this, R.string.test_results_cleared, Toast.LENGTH_SHORT).show();
     }
 
     private void handleCopyItemSelected() {
-        TestResultsReport report = new TestResultsReport(this, getListAdapter());
-        ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
-        clipboardManager.setText(report.getBody());
-        Toast.makeText(this, R.string.test_results_copied, Toast.LENGTH_SHORT).show();
+        try {
+            TestResultsReport report = new TestResultsReport(this, mAdapter);
+            ClipboardManager clipboardManager = (ClipboardManager)
+                    getSystemService(CLIPBOARD_SERVICE);
+            clipboardManager.setText(report.getBody());
+            Toast.makeText(this, R.string.test_results_copied, Toast.LENGTH_SHORT).show();
+        } catch (IOException e) {
+            Toast.makeText(this, R.string.test_results_error, Toast.LENGTH_SHORT).show();
+            Log.e(TAG, "Coudn't copy test results report", e);
+        }
     }
 
     private void handleShareItemSelected() {
-        Intent target = new Intent(Intent.ACTION_SEND);
-        target.setType("text/plain");
-
-        TestResultsReport report = new TestResultsReport(this, getListAdapter());
-        target.putExtra(Intent.EXTRA_SUBJECT, report.getSubject());
-        target.putExtra(Intent.EXTRA_TEXT, report.getBody());
-        startActivity(Intent.createChooser(target, getString(R.string.share_test_results)));
-    }
-
-    /**
-     * {@link ContentResolver} that refreshes the {@link TestListAdapter} and thus
-     * the {@link ListView} when the test results change.
-     */
-    private static class TestResultContentObserver extends ContentObserver {
-
-        private final TestListAdapter mAdapter;
-
-        public TestResultContentObserver(TestListAdapter adapter) {
-            super(new Handler());
-            this.mAdapter = adapter;
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-
-            // TODO: Could be improved by just refreshing the particular test result.
-            mAdapter.refreshTestResults();
+        try {
+            Intent target = new Intent(Intent.ACTION_SEND);
+            TestResultsReport report = new TestResultsReport(this, mAdapter);
+            target.setType(report.getType());
+            target.putExtra(Intent.EXTRA_SUBJECT, report.getSubject());
+            target.putExtra(Intent.EXTRA_TEXT, report.getBody());
+            startActivity(Intent.createChooser(target, getString(R.string.share_test_results)));
+        } catch (IOException e) {
+            Toast.makeText(this, R.string.test_results_error, Toast.LENGTH_SHORT).show();
+            Log.e(TAG, "Coudn't share test results report", e);
         }
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
index 420408e..f6e6f1b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
@@ -17,13 +17,17 @@
 package com.android.cts.verifier;
 
 import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
 import android.database.Cursor;
+import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -59,15 +63,22 @@
  *             <meta-data android:name="test_category" android:value="@string/test_category_security" />
  *         </pre>
  *     </li>
+ *     <li>OPTIONAL: Add a meta data attribute to indicate whether this test has a parent test.
+ *         <pre>
+ *             <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+ *         </pre>
+ *     </li>
  * </ol>
  */
-class TestListAdapter extends BaseAdapter {
+public class TestListAdapter extends BaseAdapter {
 
     /** Activities implementing {@link Intent#ACTION_MAIN} and this will appear in the list. */
     public static final String CATEGORY_MANUAL_TEST = "android.cts.intent.category.MANUAL_TEST";
 
     private static final String TEST_CATEGORY_META_DATA = "test_category";
 
+    private static final String TEST_PARENT_META_DATA = "test_parent";
+
     /** View type for a category of tests like "Sensors" or "Features" */
     private static final int CATEGORY_HEADER_VIEW_TYPE = 0;
 
@@ -79,8 +90,10 @@
 
     private final Context mContext;
 
+    private final String mTestParent;
+
     /** Immutable data of tests like the test's title and launch intent. */
-    private final List<TestListItem> mRows;
+    private final List<TestListItem> mRows = new ArrayList<TestListAdapter.TestListItem>();
 
     /** Mutable test results that will change as each test activity finishes. */
     private final Map<String, Integer> mTestResults = new HashMap<String, Integer>();
@@ -88,7 +101,7 @@
     private final LayoutInflater mLayoutInflater;
 
     /** {@link ListView} row that is either a test category header or a test. */
-    static class TestListItem {
+    public static class TestListItem {
 
         /** Title shown in the {@link ListView}. */
         final String title;
@@ -99,6 +112,9 @@
         /** Intent used to launch the activity from the list. Null for categories. */
         final Intent intent;
 
+        /** Tests within this test. For instance, the Bluetooth test contains more tests. */
+        final List<TestListItem> subItems = new ArrayList<TestListItem>();
+
         static TestListItem newTest(String title, String className, Intent intent) {
             return new TestListItem(title, className, intent);
         }
@@ -113,26 +129,81 @@
             this.intent = intent;
         }
 
+        public Intent getIntent() {
+            return intent;
+        }
+
         boolean isTest() {
             return intent != null;
         }
+
+        void addTestListItem(TestListItem item) {
+            subItems.add(item);
+        }
     }
 
-    TestListAdapter(Context context) {
+    public TestListAdapter(Context context, String testParent) {
         this.mContext = context;
-        this.mRows = getRows(context);
+        this.mTestParent = testParent;
         this.mLayoutInflater =
                 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        updateTestResults(mContext, mTestResults);
+
+        TestResultContentObserver observer = new TestResultContentObserver();
+        ContentResolver resolver = context.getContentResolver();
+        resolver.registerContentObserver(TestResultsProvider.RESULTS_CONTENT_URI, true, observer);
     }
 
-    static List<TestListItem> getRows(Context context) {
+    public void loadTestResults() {
+        new RefreshTestResultsTask().execute();
+    }
+
+    public void clearTestResults() {
+        new ClearTestResultsTask().execute();
+    }
+
+    public void setTestResult(TestResult testResult) {
+        new SetTestResultTask(testResult.getName(), testResult.getResult()).execute();
+    }
+
+    class RefreshTestResultsTask extends AsyncTask<Void, Void, RefreshResult> {
+        @Override
+        protected RefreshResult doInBackground(Void... params) {
+            List<TestListItem> rows = getRows();
+            Map<String, Integer> results = getTestResults();
+            return new RefreshResult(rows, results);
+        }
+
+        @Override
+        protected void onPostExecute(RefreshResult result) {
+            super.onPostExecute(result);
+            mRows.clear();
+            mRows.addAll(result.mItems);
+            mTestResults.clear();
+            mTestResults.putAll(result.mResults);
+            notifyDataSetChanged();
+        }
+    }
+
+    static class RefreshResult {
+        List<TestListItem> mItems;
+        Map<String, Integer> mResults;
+
+        RefreshResult(List<TestListItem> items, Map<String, Integer> results) {
+            mItems = items;
+            mResults = results;
+        }
+    }
+
+    List<TestListItem> getRows() {
+
         /*
-         * 1. Get all the tests keyed by their category.
-         * 2. Flatten the tests and categories into one giant list for the list view.
+         * 1. Get all the tests belonging to the test parent.
+         * 2. Get all the tests keyed by their category.
+         * 3. Flatten the tests and categories into one giant list for the list view.
          */
 
-        Map<String, List<TestListItem>> testsByCategory = getTestsByCategory(context);
+        List<ResolveInfo> infos = getResolveInfosForParent();
+        Map<String, List<TestListItem>> testsByCategory = getTestsByCategory(infos);
 
         List<String> testCategories = new ArrayList<String>(testsByCategory.keySet());
         Collections.sort(testCategories);
@@ -152,25 +223,41 @@
         return allRows;
     }
 
-    static Map<String, List<TestListItem>> getTestsByCategory(Context context) {
-        Map<String, List<TestListItem>> testsByCategory =
-                new HashMap<String, List<TestListItem>>();
-
+    List<ResolveInfo> getResolveInfosForParent() {
         Intent mainIntent = new Intent(Intent.ACTION_MAIN);
         mainIntent.addCategory(CATEGORY_MANUAL_TEST);
 
-        PackageManager packageManager = context.getPackageManager();
+        PackageManager packageManager = mContext.getPackageManager();
         List<ResolveInfo> list = packageManager.queryIntentActivities(mainIntent,
                 PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+        int size = list.size();
 
-        for (int i = 0; i < list.size(); i++) {
+        List<ResolveInfo> matchingList = new ArrayList<ResolveInfo>();
+        for (int i = 0; i < size; i++) {
             ResolveInfo info = list.get(i);
-            String testCategory = getTestCategory(context, info.activityInfo.metaData);
-            String title = getTitle(context, info.activityInfo);
+            String parent = getTestParent(mContext, info.activityInfo.metaData);
+            if ((mTestParent == null && parent == null)
+                    || (mTestParent != null && mTestParent.equals(parent))) {
+                matchingList.add(info);
+            }
+        }
+        return matchingList;
+    }
+
+    Map<String, List<TestListItem>> getTestsByCategory(List<ResolveInfo> list) {
+        Map<String, List<TestListItem>> testsByCategory =
+                new HashMap<String, List<TestListItem>>();
+
+        int size = list.size();
+        for (int i = 0; i < size; i++) {
+            ResolveInfo info = list.get(i);
+            String title = getTitle(mContext, info.activityInfo);
             String className = info.activityInfo.name;
             Intent intent = getActivityIntent(info.activityInfo);
+            TestListItem item = TestListItem.newTest(title, className, intent);
 
-            addTestToCategory(testsByCategory, testCategory, title, className, intent);
+            String testCategory = getTestCategory(mContext, info.activityInfo.metaData);
+            addTestToCategory(testsByCategory, testCategory, item);
         }
 
         return testsByCategory;
@@ -188,6 +275,10 @@
         }
     }
 
+    static String getTestParent(Context context, Bundle metaData) {
+        return metaData != null ? metaData.getString(TEST_PARENT_META_DATA) : null;
+    }
+
     static String getTitle(Context context, ActivityInfo activityInfo) {
         if (activityInfo.labelRes != 0) {
             return context.getString(activityInfo.labelRes);
@@ -203,7 +294,7 @@
     }
 
     static void addTestToCategory(Map<String, List<TestListItem>> testsByCategory,
-            String testCategory, String title, String className, Intent intent) {
+            String testCategory, TestListItem item) {
         List<TestListItem> tests;
         if (testsByCategory.containsKey(testCategory)) {
             tests = testsByCategory.get(testCategory);
@@ -211,12 +302,12 @@
             tests = new ArrayList<TestListItem>();
         }
         testsByCategory.put(testCategory, tests);
-        tests.add(TestListItem.newTest(title, className, intent));
+        tests.add(item);
     }
 
-    static void updateTestResults(Context context, Map<String, Integer> testResults) {
-        testResults.clear();
-        ContentResolver resolver = context.getContentResolver();
+    Map<String, Integer> getTestResults() {
+        Map<String, Integer> results = new HashMap<String, Integer>();
+        ContentResolver resolver = mContext.getContentResolver();
         Cursor cursor = null;
         try {
             cursor = resolver.query(TestResultsProvider.RESULTS_CONTENT_URI,
@@ -225,7 +316,7 @@
                 do {
                     String className = cursor.getString(1);
                     int testResult = cursor.getInt(2);
-                    testResults.put(className, testResult);
+                    results.put(className, testResult);
                 } while (cursor.moveToNext());
             }
         } finally {
@@ -233,11 +324,59 @@
                 cursor.close();
             }
         }
+        return results;
     }
 
-    public void refreshTestResults() {
-        updateTestResults(mContext, mTestResults);
-        notifyDataSetChanged();
+    class ClearTestResultsTask extends AsyncTask<Void, Void, Void> {
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            ContentResolver resolver = mContext.getContentResolver();
+            resolver.delete(TestResultsProvider.RESULTS_CONTENT_URI, "1", null);
+            return null;
+        }
+    }
+
+    class SetTestResultTask extends AsyncTask<Void, Void, Void> {
+
+        private final String mTestName;
+
+        private final int mResult;
+
+        SetTestResultTask(String testName, int result) {
+            mTestName = testName;
+            mResult = result;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            ContentValues values = new ContentValues(2);
+            values.put(TestResultsProvider.COLUMN_TEST_RESULT, mResult);
+            values.put(TestResultsProvider.COLUMN_TEST_NAME, mTestName);
+
+            ContentResolver resolver = mContext.getContentResolver();
+            int numUpdated = resolver.update(TestResultsProvider.RESULTS_CONTENT_URI, values,
+                    TestResultsProvider.COLUMN_TEST_NAME + " = ?",
+                    new String[] {mTestName});
+
+            if (numUpdated == 0) {
+                resolver.insert(TestResultsProvider.RESULTS_CONTENT_URI, values);
+            }
+            return null;
+        }
+    }
+
+    class TestResultContentObserver extends ContentObserver {
+
+        public TestResultContentObserver() {
+            super(new Handler());
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            super.onChange(selfChange);
+            loadTestResults();
+        }
     }
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
index 18a08fe..c7af68a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -18,90 +18,131 @@
 
 import com.android.cts.verifier.TestListAdapter.TestListItem;
 
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Build;
+import org.xmlpull.v1.XmlSerializer;
 
-/** Plain text report of the current test results. */
+import android.content.Context;
+import android.os.Build;
+import android.util.Xml;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * XML text report of the current test results.
+ * <p>
+ * Sample:
+ * <pre>
+ * <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ * <test-results-report report-version="1" creation-time="Tue Jun 28 11:04:10 PDT 2011">
+ *   <verifier-info version-name="2.3_r4" version-code="2" />
+ *   <device-info>
+ *     <build-info fingerprint="google/soju/crespo:2.3.4/GRJ22/121341:user/release-keys" />
+ *   </device-info>
+ *   <test-results>
+ *     <test title="Audio Quality Verifier" class-name="com.android.cts.verifier.audioquality.AudioQualityVerifierActivity" result="not-executed" />
+ *     <test title="Hardware/Software Feature Summary" class-name="com.android.cts.verifier.features.FeatureSummaryActivity" result="fail" />
+ *     <test title="Bluetooth Test" class-name="com.android.cts.verifier.bluetooth.BluetoothTestActivity" result="fail" />
+ *     <test title="SUID File Scanner" class-name="com.android.cts.verifier.suid.SuidFilesActivity" result="not-executed" />
+ *     <test title="Accelerometer Test" class-name="com.android.cts.verifier.sensors.AccelerometerTestActivity" result="pass" />
+ *   </test-results>
+ * </test-results-report>
+ * </pre>
+ */
 class TestResultsReport {
 
+    /** Version of the test report. Increment whenever adding new tags and attributes. */
+    private static final int REPORT_VERSION = 1;
+
+    /** Format of the report's creation time. Maintain the same format at CTS. */
+    private static DateFormat DATE_FORMAT =
+            new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH);
+
+    private static final String TEST_RESULTS_REPORT_TAG = "test-results-report";
+    private static final String VERIFIER_INFO_TAG = "verifier-info";
+    private static final String DEVICE_INFO_TAG = "device-info";
+    private static final String BUILD_INFO_TAG = "build-info";
+    private static final String TEST_RESULTS_TAG = "test-results";
+    private static final String TEST_TAG = "test";
+
     private final Context mContext;
 
     private final TestListAdapter mAdapter;
 
-    private final String mVersionName;
-
     TestResultsReport(Context context, TestListAdapter adapter) {
         this.mContext = context;
         this.mAdapter = adapter;
-        this.mVersionName = getVersionName(context);
     }
 
-    private static String getVersionName(Context context) {
-        try {
-            PackageManager packageManager = context.getPackageManager();
-            PackageInfo info = packageManager.getPackageInfo(context.getPackageName(), 0);
-            return info.versionName;
-        } catch (NameNotFoundException e) {
-            throw new RuntimeException("Could not get find package information for "
-                    + context.getPackageName());
-        }
+    String getType() {
+        return "application/xml";
     }
 
     String getSubject() {
-        return new StringBuilder()
-                .append(mContext.getString(R.string.subject_header, mVersionName))
-                .append(' ')
-                .append(Build.FINGERPRINT)
-                .toString();
+        return mContext.getString(R.string.subject_header,
+                Version.getVersionName(mContext),
+                Build.FINGERPRINT);
     }
 
-    String getBody() {
-        StringBuilder builder = new StringBuilder()
-                .append(mContext.getString(R.string.body_header, mVersionName))
-                .append("\n\n")
-                .append(Build.FINGERPRINT)
-                .append("\n\n");
+    String getBody() throws IllegalArgumentException, IllegalStateException, IOException {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 
+        XmlSerializer xml = Xml.newSerializer();
+        xml.setOutput(outputStream, "utf-8");
+        xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+        xml.startDocument("utf-8", true);
+
+        xml.startTag(null, TEST_RESULTS_REPORT_TAG);
+        xml.attribute(null, "report-version", Integer.toString(REPORT_VERSION));
+        xml.attribute(null, "creation-time", DATE_FORMAT.format(new Date()));
+
+        xml.startTag(null, VERIFIER_INFO_TAG);
+        xml.attribute(null, "version-name", Version.getVersionName(mContext));
+        xml.attribute(null, "version-code", Integer.toString(Version.getVersionCode(mContext)));
+        xml.endTag(null, VERIFIER_INFO_TAG);
+
+        xml.startTag(null, DEVICE_INFO_TAG);
+        xml.startTag(null, BUILD_INFO_TAG);
+        xml.attribute(null, "fingerprint", Build.FINGERPRINT);
+        xml.endTag(null, BUILD_INFO_TAG);
+        xml.endTag(null, DEVICE_INFO_TAG);
+
+        xml.startTag(null, TEST_RESULTS_TAG);
         int count = mAdapter.getCount();
         for (int i = 0; i < count; i++) {
             TestListItem item = mAdapter.getItem(i);
-            if (!item.isTest()) {
-                builder.append(item.title).append('\n');
-            } else {
-                builder.append(item.title)
-                        .append(".....")
-                        .append(getTestResultString(mAdapter.getTestResult(i)))
-                        .append('\n');
-            }
-
-            if (i + 1 < count && !mAdapter.getItem(i + 1).isTest()) {
-                builder.append('\n');
+            if (item.isTest()) {
+                xml.startTag(null, TEST_TAG);
+                xml.attribute(null, "title", item.title);
+                xml.attribute(null, "class-name", item.className);
+                xml.attribute(null, "result", getTestResultString(mAdapter.getTestResult(i)));
+                xml.endTag(null, TEST_TAG);
             }
         }
-        return builder.toString();
+        xml.endTag(null, TEST_RESULTS_TAG);
+
+        xml.endTag(null, TEST_RESULTS_REPORT_TAG);
+        xml.endDocument();
+
+        return outputStream.toString("utf-8");
     }
 
     private String getTestResultString(int testResult) {
-        int resId = 0;
         switch (testResult) {
             case TestResult.TEST_RESULT_PASSED:
-                resId = R.string.pass_result;
-                break;
+                return "pass";
 
             case TestResult.TEST_RESULT_FAILED:
-                resId = R.string.fail_result;
-                break;
+                return "fail";
 
             case TestResult.TEST_RESULT_NOT_EXECUTED:
-                resId = R.string.not_executed_result;
-                break;
+                return "not-executed";
 
             default:
                 throw new IllegalArgumentException("Unknown test result: " + testResult);
         }
-        return mContext.getString(resId);
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/Version.java b/apps/CtsVerifier/src/com/android/cts/verifier/Version.java
new file mode 100644
index 0000000..e7b6121
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/Version.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+class Version {
+
+    static String getVersionName(Context context) {
+        return getPackageInfo(context).versionName;
+    }
+
+    static int getVersionCode(Context context) {
+        return getPackageInfo(context).versionCode;
+    }
+
+    static PackageInfo getPackageInfo(Context context) {
+        try {
+            PackageManager packageManager = context.getPackageManager();
+            return packageManager.getPackageInfo(context.getPackageName(), 0);
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException("Could not get find package information for "
+                    + context.getPackageName());
+        }
+    }
+}
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..49cc7fe
--- /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, false)) {
+            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..5520bb7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java
@@ -0,0 +1,129 @@
+/*
+ * 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 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.lockNow();
+        }
+    }
+
+    @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);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
index b617fc2..2beff93 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
@@ -18,62 +18,21 @@
 
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
 import com.android.cts.verifier.TestResult;
 
 import android.app.AlertDialog;
 import android.bluetooth.BluetoothAdapter;
-import android.content.ContentValues;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.os.AsyncTask;
 import android.os.Bundle;
-import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
 import android.widget.ListView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 
 public class BluetoothTestActivity extends PassFailButtons.ListActivity {
 
-    public static final int TEST_BLUETOOTH_TOGGLE = 0;
-    public static final int TEST_SECURE_SERVER = 1;
-    public static final int TEST_INSECURE_SERVER = 2;
-    public static final int TEST_SECURE_CLIENT = 3;
-    public static final int TEST_INSECURE_CLIENT = 4;
-
-    private static final int START_TOGGLE_BLUETOOTH_TEST_REQUEST = 1;
-    private static final int START_SECURE_SERVER_REQUEST = 2;
-    private static final int START_INSECURE_SERVER_REQUEST = 3;
-    private static final int START_SECURE_PICK_SERVER_REQUEST = 4;
-    private static final int START_INSECURE_PICK_SERVER_REQUEST = 5;
-    private static final int START_SECURE_CLIENT_REQUEST = 6;
-    private static final int START_INSECURE_CLIENT_REQUEST = 7;
-
-    private TestListItem mBluetoothToggleTest;
-    private TestListItem mSecureServerTest;
-    private TestListItem mInsecureServerTest;
-    private TestListItem mSecureClientTest;
-    private TestListItem mInsecureClientTest;
-
-    private static final String TABLE_NAME = "results";
-    private static final String _ID = "_id";
-    private static final String COLUMN_TEST_ID = "test_id";
-    private static final String COLUMN_TEST_RESULT = "test_result";
-    private static final String[] ALL_COLUMNS = {
-          _ID,
-          COLUMN_TEST_ID,
-          COLUMN_TEST_RESULT,
-    };
+    private static final int LAUNCH_TEST_REQUEST_CODE = 1;
 
     private TestListAdapter mAdapter;
 
@@ -84,430 +43,52 @@
         setPassFailButtonClickListeners();
         setInfoResources(R.string.bluetooth_test, R.string.bluetooth_test_info, -1);
 
-        mBluetoothToggleTest = TestListItem.newTest(R.string.bt_toggle_bluetooth,
-                TEST_BLUETOOTH_TOGGLE);
-        mSecureServerTest = TestListItem.newTest(R.string.bt_secure_server,
-                TEST_SECURE_SERVER);
-        mInsecureServerTest = TestListItem.newTest(R.string.bt_insecure_server,
-                TEST_INSECURE_SERVER);
-        mSecureClientTest = TestListItem.newTest(R.string.bt_secure_client,
-                TEST_SECURE_CLIENT);
-        mInsecureClientTest = TestListItem.newTest(R.string.bt_insecure_client,
-                TEST_INSECURE_CLIENT);
-
-        mAdapter = new TestListAdapter(this);
-        mAdapter.add(TestListItem.newCategory(R.string.bt_control));
-        mAdapter.add(mBluetoothToggleTest);
-
-        mAdapter.add(TestListItem.newCategory(R.string.bt_device_communication));
-        mAdapter.add(mSecureServerTest);
-        mAdapter.add(mInsecureServerTest);
-        mAdapter.add(mSecureClientTest);
-        mAdapter.add(mInsecureClientTest);
-
+        mAdapter = new TestListAdapter(this, getClass().getName());
         setListAdapter(mAdapter);
-        refreshTestResults();
+        mAdapter.loadTestResults();
 
         if (BluetoothAdapter.getDefaultAdapter() == null) {
             showNoBluetoothDialog();
         }
     }
 
-    private void refreshTestResults() {
-        new RefreshTask().execute();
-    }
-
     private void showNoBluetoothDialog() {
         new AlertDialog.Builder(this)
-        .setIcon(android.R.drawable.ic_dialog_alert)
-        .setTitle(R.string.bt_not_available_title)
-        .setMessage(R.string.bt_not_available_message)
-        .setCancelable(false)
-        .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                finish();
-            }
-        })
-        .show();
+            .setIcon(android.R.drawable.ic_dialog_alert)
+            .setTitle(R.string.bt_not_available_title)
+            .setMessage(R.string.bt_not_available_message)
+            .setCancelable(false)
+            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    finish();
+                }
+            })
+            .show();
     }
 
     @Override
     protected void onListItemClick(ListView l, View v, int position, long id) {
         super.onListItemClick(l, v, position, id);
         TestListItem testItem = (TestListItem) l.getItemAtPosition(position);
-        switch (testItem.mId) {
-            case TEST_BLUETOOTH_TOGGLE:
-                startToggleBluetoothActivity();
-                break;
-
-            case TEST_SECURE_SERVER:
-                startServerActivity(true);
-                break;
-
-            case TEST_INSECURE_SERVER:
-                startServerActivity(false);
-                break;
-
-            case TEST_SECURE_CLIENT:
-                startDevicePickerActivity(true);
-                break;
-
-            case TEST_INSECURE_CLIENT:
-                startDevicePickerActivity(false);
-                break;
-        }
-    }
-
-    private void startToggleBluetoothActivity() {
-        Intent intent = new Intent(this, BluetoothToggleActivity.class);
-        startActivityForResult(intent, START_TOGGLE_BLUETOOTH_TEST_REQUEST);
-    }
-
-    private void startServerActivity(boolean secure) {
-        Intent intent = new Intent(this, MessageTestActivity.class)
-                .putExtra(MessageTestActivity.EXTRA_SECURE, secure);
-        startActivityForResult(intent, secure
-                ? START_SECURE_SERVER_REQUEST
-                : START_INSECURE_SERVER_REQUEST);
-    }
-
-    private void startDevicePickerActivity(boolean secure) {
-        Intent intent = new Intent(this, DevicePickerActivity.class);
-        startActivityForResult(intent, secure
-                ? START_SECURE_PICK_SERVER_REQUEST
-                : START_INSECURE_PICK_SERVER_REQUEST);
+        Intent intent = testItem.getIntent();
+        startActivityForResult(intent, LAUNCH_TEST_REQUEST_CODE);
     }
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         switch (requestCode) {
-            case START_TOGGLE_BLUETOOTH_TEST_REQUEST:
-                handleEnableBluetoothResult(resultCode, data);
-                break;
-
-            case START_SECURE_SERVER_REQUEST:
-                handleServerResult(resultCode, data, true);
-                break;
-
-            case START_INSECURE_SERVER_REQUEST:
-                handleServerResult(resultCode, data, false);
-                break;
-
-            case START_SECURE_PICK_SERVER_REQUEST:
-                handleDevicePickerResult(resultCode, data, true);
-                break;
-
-            case START_INSECURE_PICK_SERVER_REQUEST:
-                handleDevicePickerResult(resultCode, data, false);
-                break;
-
-            case START_SECURE_CLIENT_REQUEST:
-                handleClientResult(resultCode, data, true);
-                break;
-
-            case START_INSECURE_CLIENT_REQUEST:
-                handleClientResult(resultCode, data, false);
+            case LAUNCH_TEST_REQUEST_CODE:
+                handleLaunchTestResult(resultCode, data);
                 break;
         }
     }
 
-    private void handleEnableBluetoothResult(int resultCode, Intent data) {
-        if (data != null) {
-            TestResult result = TestResult.fromActivityResult(resultCode, data);
-            mBluetoothToggleTest.setResult(result.getResult());
-            updateTest(mBluetoothToggleTest);
-        }
-    }
-
-    private void updateTest(TestListItem item) {
-        new UpdateTask().execute(item.mId, item.mResult);
-    }
-
-    private void handleServerResult(int resultCode, Intent data, boolean secure) {
-        if (data != null) {
-            TestResult result = TestResult.fromActivityResult(resultCode, data);
-            TestListItem test = secure ? mSecureServerTest : mInsecureServerTest;
-            test.setResult(result.getResult());
-            updateTest(test);
-        }
-    }
-
-    private void handleDevicePickerResult(int resultCode, Intent data, boolean secure) {
+    private void handleLaunchTestResult(int resultCode, Intent data) {
         if (resultCode == RESULT_OK) {
-            String address = data.getStringExtra(DevicePickerActivity.EXTRA_DEVICE_ADDRESS);
-            startClientActivity(address, secure);
-        }
-    }
-
-    private void startClientActivity(String address, boolean secure) {
-        Intent intent = new Intent(this, MessageTestActivity.class)
-                .putExtra(MessageTestActivity.EXTRA_DEVICE_ADDRESS, address)
-                .putExtra(MessageTestActivity.EXTRA_SECURE, secure);
-        startActivityForResult(intent, secure
-                ? START_SECURE_CLIENT_REQUEST
-                : START_INSECURE_CLIENT_REQUEST);
-    }
-
-    private void handleClientResult(int resultCode, Intent data, boolean secure) {
-        if (data != null) {
-            TestResult result = TestResult.fromActivityResult(resultCode, data);
-            TestListItem test = secure ? mSecureClientTest : mInsecureClientTest;
-            test.setResult(result.getResult());
-            updateTest(test);
-        }
-    }
-
-    private class UpdateTask extends AsyncTask<Integer, Void, Void> {
-
-        @Override
-        protected Void doInBackground(Integer[] resultPairs) {
-            TestResultsHelper openHelper = new TestResultsHelper(BluetoothTestActivity.this);
-            SQLiteDatabase db = openHelper.getWritableDatabase();
-
-            int testId = resultPairs[0];
-            int testResult = resultPairs[1];
-
-            ContentValues values = new ContentValues(2);
-            values.put(COLUMN_TEST_ID, testId);
-            values.put(COLUMN_TEST_RESULT, testResult);
-
-            try {
-                if (0 == db.update(TABLE_NAME, values, COLUMN_TEST_ID + " = ?",
-                        new String[] {Integer.toString(testId)})) {
-                    db.insert(TABLE_NAME, null, values);
-                }
-            } finally {
-                if (db != null) {
-                    db.close();
-                }
-            }
-
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            super.onPostExecute(result);
-            refreshTestResults();
-        }
-    }
-
-    private class RefreshTask extends AsyncTask<Void, Void, Map<Integer, Integer>> {
-
-        @Override
-        protected Map<Integer, Integer> doInBackground(Void... params) {
-            Map<Integer, Integer> results = new HashMap<Integer, Integer>();
-            TestResultsHelper openHelper = new TestResultsHelper(BluetoothTestActivity.this);
-            SQLiteDatabase db = openHelper.getReadableDatabase();
-            Cursor cursor = null;
-            try {
-                cursor = db.query(TABLE_NAME, ALL_COLUMNS, null, null, null, null, null, null);
-                while (cursor.moveToNext()) {
-                    int testId = cursor.getInt(1);
-                    int testResult = cursor.getInt(2);
-                    results.put(testId, testResult);
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-                if (db != null) {
-                    db.close();
-                }
-            }
-            return results;
-        }
-
-        @Override
-        protected void onPostExecute(Map<Integer, Integer> results) {
-            super.onPostExecute(results);
-            for (Integer testId : results.keySet()) {
-                TestListItem item = mAdapter.getTest(testId);
-                if (item != null) {
-                    item.setResult(results.get(testId));
-                }
-            }
-            mAdapter.notifyDataSetChanged();
-        }
-    }
-
-    static class TestListItem {
-
-        static final int NUM_VIEW_TYPES = 2;
-
-        static final int VIEW_TYPE_CATEGORY = 0;
-
-        static final int VIEW_TYPE_TEST = 1;
-
-        final int mViewType;
-
-        final int mTitle;
-
-        final int mId;
-
-        int mResult;
-
-        static TestListItem newTest(int title, int id) {
-            return new TestListItem(VIEW_TYPE_TEST, title, id);
-        }
-
-        static TestListItem newCategory(int title) {
-            return new TestListItem(VIEW_TYPE_CATEGORY, title, -1);
-        }
-
-        private TestListItem(int viewType, int title, int id) {
-            this.mViewType = viewType;
-            this.mTitle = title;
-            this.mId = id;
-        }
-
-        public boolean isTest() {
-            return mViewType == VIEW_TYPE_TEST;
-        }
-
-        public void setResult(int result) {
-            mResult = result;
-        }
-    }
-
-    static class TestListAdapter extends BaseAdapter {
-
-        private static final int PADDING = 10;
-
-        private final List<TestListItem> mItems = new ArrayList<TestListItem>();
-
-        private final Map<Integer, TestListItem> mTestsById = new HashMap<Integer, TestListItem>();
-
-        private final LayoutInflater mInflater;
-
-        public TestListAdapter(Context context) {
-            mInflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE);
-        }
-
-        public void add(TestListItem item) {
-            mItems.add(item);
-            if (item.isTest()) {
-                mTestsById.put(item.mId, item);
-            }
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            int backgroundResource = 0;
-            int iconResource = 0;
-
-            TestListItem item = getItem(position);
-
-            TextView textView = null;
-            if (convertView == null) {
-                int layout = getLayout(position);
-                textView = (TextView) mInflater.inflate(layout, parent, false);
-            } else {
-                textView = (TextView) convertView;
-            }
-
-            textView.setText(item.mTitle);
-
-            if (item.isTest()) {
-                switch (item.mResult) {
-                    case TestResult.TEST_RESULT_PASSED:
-                        backgroundResource = R.drawable.test_pass_gradient;
-                        iconResource = R.drawable.fs_good;
-                        break;
-
-                    case TestResult.TEST_RESULT_FAILED:
-                        backgroundResource = R.drawable.test_fail_gradient;
-                        iconResource = R.drawable.fs_error;
-                        break;
-
-                    case TestResult.TEST_RESULT_NOT_EXECUTED:
-                        break;
-
-                    default:
-                        throw new IllegalArgumentException("Unknown test result: " + item.mResult);
-                }
-
-                textView.setBackgroundResource(backgroundResource);
-                textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, iconResource, 0);
-                textView.setPadding(PADDING, 0, PADDING, 0);
-                textView.setCompoundDrawablePadding(PADDING);
-            }
-
-            return textView;
-        }
-
-        private int getLayout(int position) {
-            int viewType = getItemViewType(position);
-            switch (viewType) {
-                case TestListItem.VIEW_TYPE_CATEGORY:
-                    return R.layout.test_category_row;
-                case TestListItem.VIEW_TYPE_TEST:
-                    return android.R.layout.simple_list_item_1;
-                default:
-                    throw new IllegalArgumentException("Illegal view type: " + viewType);
-
-            }
-        }
-
-        public TestListItem getTest(int id) {
-            return mTestsById.get(id);
-        }
-
-        @Override
-        public int getCount() {
-            return mItems.size();
-        }
-
-        @Override
-        public TestListItem getItem(int position) {
-            return mItems.get(position);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return getItem(position).mId;
-        }
-
-        @Override
-        public int getViewTypeCount() {
-            return TestListItem.NUM_VIEW_TYPES;
-        }
-
-        @Override
-        public int getItemViewType(int position) {
-            return getItem(position).mViewType;
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            return getItemViewType(position) != TestListItem.VIEW_TYPE_CATEGORY;
-        }
-    }
-
-    class TestResultsHelper extends SQLiteOpenHelper {
-
-        private static final String DATABASE_NAME = "bluetooth_results.db";
-
-        private static final int DATABASE_VERSION = 1;
-
-        TestResultsHelper(Context context) {
-            super(context, DATABASE_NAME, null, DATABASE_VERSION);
-        }
-
-        @Override
-        public void onCreate(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE " + TABLE_NAME + " ("
-                    + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
-                    + COLUMN_TEST_ID + " INTEGER, "
-                    + COLUMN_TEST_RESULT + " INTEGER DEFAULT 0);");
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
-            onCreate(db);
+            TestResult testResult = TestResult.fromActivityResult(resultCode, data);
+            mAdapter.setTestResult(testResult);
         }
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/InsecureClientActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/InsecureClientActivity.java
new file mode 100644
index 0000000..6dfbbea
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/InsecureClientActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.bluetooth;
+
+public class InsecureClientActivity extends MessageTestActivity {
+    public InsecureClientActivity() {
+        super(false, false);
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/InsecureServerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/InsecureServerActivity.java
new file mode 100644
index 0000000..3526e04
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/InsecureServerActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.bluetooth;
+
+public class InsecureServerActivity extends MessageTestActivity {
+    public InsecureServerActivity() {
+        super(false, true);
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/MessageTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/MessageTestActivity.java
index 1d6706d..9405d71 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/MessageTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/MessageTestActivity.java
@@ -32,8 +32,8 @@
 import android.os.Handler;
 import android.os.Message;
 import android.view.View;
-import android.view.Window;
 import android.view.View.OnClickListener;
+import android.view.Window;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.ListView;
@@ -43,16 +43,14 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class MessageTestActivity extends PassFailButtons.Activity {
-
-    static final String EXTRA_DEVICE_ADDRESS = "deviceAddress";
-    static final String EXTRA_SECURE = "secure";
+class MessageTestActivity extends PassFailButtons.Activity {
 
     /** Broadcast action that should only be fired when pairing for a secure connection. */
     private static final String ACTION_PAIRING_REQUEST =
             "android.bluetooth.device.action.PAIRING_REQUEST";
 
     private static final int ENABLE_BLUETOOTH_REQUEST = 1;
+    private static final int PICK_SERVER_DEVICE_REQUEST = 2;
 
     private static final String MESSAGE_DELIMITER = "\n";
     private static final Pattern MESSAGE_PATTERN = Pattern.compile("Message (\\d+) to .*");
@@ -79,6 +77,11 @@
     private String mRemoteDeviceName = "";
     private StringBuilder mMessageBuffer = new StringBuilder();
 
+    MessageTestActivity(boolean secure, boolean server) {
+        mSecure = secure;
+        mServer = server;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -86,9 +89,6 @@
         setContentView(R.layout.bt_messages);
         setPassFailButtonClickListeners();
 
-        mDeviceAddress = getIntent().getStringExtra(EXTRA_DEVICE_ADDRESS);
-        mSecure = getIntent().getBooleanExtra(EXTRA_SECURE, true);
-        mServer = mDeviceAddress == null || mDeviceAddress.isEmpty();
         if (mServer) {
             setTitle(mSecure ? R.string.bt_secure_server : R.string.bt_insecure_server);
         } else {
@@ -127,24 +127,41 @@
         registerReceiver(mPairingActionReceiver, intentFilter);
 
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-        if (mBluetoothAdapter.isEnabled()) {
-            startChatService();
+        if (!mServer) {
+            Intent intent = new Intent(this, DevicePickerActivity.class);
+            startActivityForResult(intent, PICK_SERVER_DEVICE_REQUEST);
         } else {
-            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
-            startActivityForResult(intent, ENABLE_BLUETOOTH_REQUEST);
+            if (mBluetoothAdapter.isEnabled()) {
+                startChatService();
+            } else {
+                Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+                startActivityForResult(intent, ENABLE_BLUETOOTH_REQUEST);
+            }
         }
     }
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
-        if (requestCode == ENABLE_BLUETOOTH_REQUEST) {
-            if (resultCode == RESULT_OK) {
-                startChatService();
-            } else {
-                setResult(RESULT_CANCELED);
-                finish();
-            }
+        switch (requestCode) {
+            case ENABLE_BLUETOOTH_REQUEST:
+                if (resultCode == RESULT_OK) {
+                    startChatService();
+                } else {
+                    setResult(RESULT_CANCELED);
+                    finish();
+                }
+                break;
+
+            case PICK_SERVER_DEVICE_REQUEST:
+                if (resultCode == RESULT_OK) {
+                    mDeviceAddress = data.getStringExtra(DevicePickerActivity.EXTRA_DEVICE_ADDRESS);
+                    startChatService();
+                } else {
+                    setResult(RESULT_CANCELED);
+                    finish();
+                }
+                break;
         }
     }
 
@@ -332,7 +349,9 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        mChatService.stop();
+        if (mChatService != null) {
+            mChatService.stop();
+        }
         unregisterReceiver(mPairingActionReceiver);
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/SecureClientActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/SecureClientActivity.java
new file mode 100644
index 0000000..799f0b8
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/SecureClientActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.bluetooth;
+
+public class SecureClientActivity extends MessageTestActivity {
+    public SecureClientActivity() {
+        super(true, false);
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/SecureServerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/SecureServerActivity.java
new file mode 100644
index 0000000..25e26e6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/SecureServerActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.bluetooth;
+
+public class SecureServerActivity extends MessageTestActivity {
+    public SecureServerActivity() {
+        super(true, true);
+    }
+}
\ No newline at end of file
diff --git a/development/ide/eclipse/.classpath b/development/ide/eclipse/.classpath
index 8d10955..8061867 100644
--- a/development/ide/eclipse/.classpath
+++ b/development/ide/eclipse/.classpath
@@ -12,9 +12,11 @@
     <classpathentry kind="src" path="cts/tests/accessibilityservice/src"/>
     <classpathentry kind="src" path="cts/tests/appsecurity-tests/src"/>
     <classpathentry kind="src" path="cts/tests/core/runner/src"/>
+    <classpathentry kind="src" path="cts/tests/deviceadmin/src"/>
     <classpathentry kind="src" path="cts/tests/src"/>
     <classpathentry kind="src" path="cts/tests/tests/accessibilityservice/src"/>
     <classpathentry kind="src" path="cts/tests/tests/accounts/src"/>
+    <classpathentry kind="src" path="cts/tests/tests/admin/src"/>
     <classpathentry kind="src" path="cts/tests/tests/app/src"/>
     <classpathentry kind="src" path="cts/tests/tests/bluetooth/src"/>
     <classpathentry kind="src" path="cts/tests/tests/content/src"/>
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index bcc81a7..85e10cd 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -80,6 +80,7 @@
     <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.SEND_SMS" />
+    <uses-permission android:name="android.permission.RECEIVE_SMS" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
     <uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS" />
diff --git a/tests/deviceadmin/Android.mk b/tests/deviceadmin/Android.mk
new file mode 100644
index 0000000..7322ad5
--- /dev/null
+++ b/tests/deviceadmin/Android.mk
@@ -0,0 +1,31 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsDeviceAdmin
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tests/deviceadmin/AndroidManifest.xml b/tests/deviceadmin/AndroidManifest.xml
new file mode 100644
index 0000000..f851e6c
--- /dev/null
+++ b/tests/deviceadmin/AndroidManifest.xml
@@ -0,0 +1,48 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.deviceadmin.cts">
+    <application>
+
+        <uses-library android:name="android.test.runner"/>
+
+        <receiver android:name="android.deviceadmin.cts.CtsDeviceAdminReceiver"
+                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>
+        
+        <receiver android:name="android.deviceadmin.cts.CtsDeviceAdminReceiver2"
+                android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                    android:resource="@xml/device_admin_2" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+            android:targetPackage="com.android.cts.admin"
+            android:label="Tests for the device admin APIs."/>
+</manifest>
diff --git a/tests/deviceadmin/res/xml/device_admin.xml b/tests/deviceadmin/res/xml/device_admin.xml
new file mode 100644
index 0000000..263fda6
--- /dev/null
+++ b/tests/deviceadmin/res/xml/device_admin.xml
@@ -0,0 +1,27 @@
+<!--
+ * 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/tests/deviceadmin/res/xml/device_admin_2.xml b/tests/deviceadmin/res/xml/device_admin_2.xml
new file mode 100644
index 0000000..ad7cabc
--- /dev/null
+++ b/tests/deviceadmin/res/xml/device_admin_2.xml
@@ -0,0 +1,24 @@
+<!--
+ * 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 />
+        <reset-password />
+        <wipe-data />
+    </uses-policies>
+</device-admin>
+
diff --git a/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminReceiver.java b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminReceiver.java
new file mode 100644
index 0000000..43485d7
--- /dev/null
+++ b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminReceiver.java
@@ -0,0 +1,22 @@
+/*
+ * 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 android.deviceadmin.cts;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class CtsDeviceAdminReceiver extends DeviceAdminReceiver {
+}
diff --git a/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminReceiver2.java b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminReceiver2.java
new file mode 100644
index 0000000..eadf31b
--- /dev/null
+++ b/tests/deviceadmin/src/android/deviceadmin/cts/CtsDeviceAdminReceiver2.java
@@ -0,0 +1,22 @@
+/*
+ * 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 android.deviceadmin.cts;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class CtsDeviceAdminReceiver2 extends DeviceAdminReceiver {
+}
diff --git a/tests/res/raw/sig_media.bin b/tests/res/raw/sig_media.bin
new file mode 100644
index 0000000..a43bc68
--- /dev/null
+++ b/tests/res/raw/sig_media.bin
Binary files differ
diff --git a/tests/res/raw/sig_platform.bin b/tests/res/raw/sig_platform.bin
new file mode 100644
index 0000000..33a683e
--- /dev/null
+++ b/tests/res/raw/sig_platform.bin
Binary files differ
diff --git a/tests/res/raw/sig_shared.bin b/tests/res/raw/sig_shared.bin
new file mode 100644
index 0000000..094b1b9
--- /dev/null
+++ b/tests/res/raw/sig_shared.bin
Binary files differ
diff --git a/tests/res/raw/sig_testkey.bin b/tests/res/raw/sig_testkey.bin
new file mode 100644
index 0000000..e2bef19
--- /dev/null
+++ b/tests/res/raw/sig_testkey.bin
Binary files differ
diff --git a/tests/src/android/webkit/cts/CtsTestServer.java b/tests/src/android/webkit/cts/CtsTestServer.java
index afcaede..b6d447b 100644
--- a/tests/src/android/webkit/cts/CtsTestServer.java
+++ b/tests/src/android/webkit/cts/CtsTestServer.java
@@ -64,6 +64,10 @@
 import java.util.Date;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -377,11 +381,11 @@
                 .toString();
     }
 
-    public String getLastRequestUrl() {
+    public synchronized String getLastRequestUrl() {
         return mLastQuery;
     }
 
-    public int getRequestCount() {
+    public synchronized int getRequestCount() {
         return mRequestCount;
     }
 
@@ -390,7 +394,7 @@
      * value, the server will include a "Expires" header.
      * @param timeMillis The time, in milliseconds, for which any future response will be valid.
      */
-    public void setDocumentValidity(long timeMillis) {
+    public synchronized void setDocumentValidity(long timeMillis) {
         mDocValidity = timeMillis;
     }
 
@@ -399,7 +403,7 @@
      * a "Last-Modified" header calculated from the value.
      * @param timeMillis The age, in milliseconds, of any document served in the future.
      */
-    public void setDocumentAge(long timeMillis) {
+    public synchronized void setDocumentAge(long timeMillis) {
         mDocAge = timeMillis;
     }
 
@@ -411,10 +415,14 @@
     private HttpResponse getResponse(HttpRequest request) throws InterruptedException, IOException {
         RequestLine requestLine = request.getRequestLine();
         HttpResponse response = null;
-        mRequestCount += 1;
         Log.i(TAG, requestLine.getMethod() + ": " + requestLine.getUri());
         String uriString = requestLine.getUri();
-        mLastQuery = uriString;
+
+        synchronized (this) {
+            mRequestCount += 1;
+            mLastQuery = uriString;
+        }
+
         URI uri = URI.create(uriString);
         String path = uri.getPath();
         String query = uri.getQuery();
@@ -583,15 +591,17 @@
 
     private void setDateHeaders(HttpResponse response) {
         long time = System.currentTimeMillis();
-        if (mDocValidity != 0) {
-            String expires =
-                    DateUtils.formatDate(new Date(time + mDocValidity), DateUtils.PATTERN_RFC1123);
-            response.addHeader("Expires", expires);
-        }
-        if (mDocAge != 0) {
-            String modified =
-                    DateUtils.formatDate(new Date(time - mDocAge), DateUtils.PATTERN_RFC1123);
-            response.addHeader("Last-Modified", modified);
+        synchronized (this) {
+            if (mDocValidity != 0) {
+                String expires = DateUtils.formatDate(new Date(time + mDocValidity),
+                        DateUtils.PATTERN_RFC1123);
+                response.addHeader("Expires", expires);
+            }
+            if (mDocAge != 0) {
+                String modified = DateUtils.formatDate(new Date(time - mDocAge),
+                        DateUtils.PATTERN_RFC1123);
+                response.addHeader("Last-Modified", modified);
+            }
         }
         response.addHeader("Date", DateUtils.formatDate(new Date(), DateUtils.PATTERN_RFC1123));
     }
@@ -599,7 +609,7 @@
     /**
      * Create an empty response with the given status.
      */
-    private HttpResponse createResponse(int status) {
+    private static HttpResponse createResponse(int status) {
         HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_0, status, null);
 
         // Fill in error reason. Avoid use of the ReasonPhraseCatalog, which is Locale-dependent.
@@ -618,7 +628,7 @@
     /**
      * Create a string entity for the given content.
      */
-    private StringEntity createEntity(String content) {
+    private static StringEntity createEntity(String content) {
         try {
             StringEntity entity = new StringEntity(content);
             entity.setContentType("text/html");
@@ -629,7 +639,7 @@
         return null;
     }
 
-    private HttpResponse createTestDownloadResponse(Uri uri) throws IOException {
+    private static HttpResponse createTestDownloadResponse(Uri uri) throws IOException {
         String downloadId = uri.getQueryParameter(DOWNLOAD_ID_PARAMETER);
         int numBytes = uri.getQueryParameter(NUM_BYTES_PARAMETER) != null
                 ? Integer.parseInt(uri.getQueryParameter(NUM_BYTES_PARAMETER))
@@ -640,7 +650,7 @@
         return response;
     }
 
-    private FileEntity createFileEntity(String downloadId, int numBytes) throws IOException {
+    private static FileEntity createFileEntity(String downloadId, int numBytes) throws IOException {
         String storageState = Environment.getExternalStorageState();
         if (Environment.MEDIA_MOUNTED.equalsIgnoreCase(storageState)) {
             File storageDir = Environment.getExternalStorageDirectory();
@@ -666,6 +676,7 @@
         private boolean mIsSsl;
         private boolean mIsCancelled;
         private SSLContext mSslContext;
+        private ExecutorService mExecutorService = Executors.newFixedThreadPool(20);
 
         /**
          * Defines the keystore contents for the server, BKS version. Holds just a
@@ -747,12 +758,13 @@
         }
 
         public void run() {
-            HttpParams params = new BasicHttpParams();
-            params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_0);
             while (!mIsCancelled) {
                 try {
                     Socket socket = mSocket.accept();
+
                     DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
+                    HttpParams params = new BasicHttpParams();
+                    params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_0);
                     conn.bind(socket, params);
 
                     // Determine whether we need to shutdown early before
@@ -762,19 +774,12 @@
                     if (isShutdownRequest(request)) {
                         mIsCancelled = true;
                     }
-
-                    HttpResponse response = mServer.getResponse(request);
-                    conn.sendResponseHeader(response);
-                    conn.sendResponseEntity(response);
-                    conn.close();
-
+                    mExecutorService.submit(new HandleResponseTask(conn, request));
                 } catch (IOException e) {
                     // normal during shutdown, ignore
                     Log.w(TAG, e);
                 } catch (HttpException e) {
                     Log.w(TAG, e);
-                } catch (InterruptedException e) {
-                    Log.w(TAG, e);
                 } catch (UnsupportedOperationException e) {
                     // DefaultHttpServerConnection's close() throws an
                     // UnsupportedOperationException.
@@ -782,18 +787,44 @@
                 }
             }
             try {
+                mExecutorService.shutdown();
+                mExecutorService.awaitTermination(1L, TimeUnit.MINUTES);
                 mSocket.close();
             } catch (IOException ignored) {
                 // safe to ignore
+            } catch (InterruptedException e) {
+                Log.e(TAG, "Shutting down threads", e);
             }
         }
 
-        private boolean isShutdownRequest(HttpRequest request) {
+        private static boolean isShutdownRequest(HttpRequest request) {
             RequestLine requestLine = request.getRequestLine();
             String uriString = requestLine.getUri();
             URI uri = URI.create(uriString);
             String path = uri.getPath();
             return path.equals(SHUTDOWN_PREFIX);
         }
+
+        private class HandleResponseTask implements Callable<Void> {
+
+            private DefaultHttpServerConnection mConnection;
+
+            private HttpRequest mRequest;
+
+            public HandleResponseTask(DefaultHttpServerConnection connection,
+                    HttpRequest request) {
+                this.mConnection = connection;
+                this.mRequest = request;
+            }
+
+            @Override
+            public Void call() throws IOException, InterruptedException, HttpException {
+                HttpResponse response = mServer.getResponse(mRequest);
+                mConnection.sendResponseHeader(response);
+                mConnection.sendResponseEntity(response);
+                mConnection.close();
+                return null;
+            }
+        }
     }
 }
diff --git a/tests/tests/admin/Android.mk b/tests/tests/admin/Android.mk
new file mode 100644
index 0000000..3228d85
--- /dev/null
+++ b/tests/tests/admin/Android.mk
@@ -0,0 +1,33 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsAdminTestCases
+
+LOCAL_INSTRUMENTATION_FOR := CtsDeviceAdmin
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tests/tests/admin/AndroidManifest.xml b/tests/tests/admin/AndroidManifest.xml
new file mode 100644
index 0000000..102c7ec
--- /dev/null
+++ b/tests/tests/admin/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.admin">
+
+  <application>
+
+      <uses-library android:name="android.test.runner"/>
+
+  </application>
+
+  <instrumentation android:name="android.test.InstrumentationTestRunner"
+                   android:targetPackage="android.deviceadmin.cts"
+                   android:label="Tests for the admin APIs."/>
+
+</manifest>
diff --git a/tests/tests/admin/src/android/admin/cts/DeviceAdminInfoTest.java b/tests/tests/admin/src/android/admin/cts/DeviceAdminInfoTest.java
new file mode 100644
index 0000000..b38ef4c
--- /dev/null
+++ b/tests/tests/admin/src/android/admin/cts/DeviceAdminInfoTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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 android.admin.cts;
+
+import android.app.admin.DeviceAdminInfo;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.test.AndroidTestCase;
+
+public class DeviceAdminInfoTest extends AndroidTestCase {
+
+    private PackageManager mPackageManager;
+
+    private ComponentName mComponent;
+
+    private ComponentName mSecondComponent;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mPackageManager = mContext.getPackageManager();
+        mComponent = getReceiverComponent();
+        mSecondComponent = getSecondReceiverComponent();
+    }
+
+    static ComponentName getReceiverComponent() {
+        return new ComponentName("android.deviceadmin.cts",
+                "android.deviceadmin.cts.CtsDeviceAdminReceiver");
+    }
+
+    static ComponentName getSecondReceiverComponent() {
+        return new ComponentName("android.deviceadmin.cts",
+                "android.deviceadmin.cts.CtsDeviceAdminReceiver2");
+    }
+
+    public void testDeviceAdminInfo() throws Exception {
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = mPackageManager.getReceiverInfo(mComponent,
+                PackageManager.GET_META_DATA);
+
+        DeviceAdminInfo info = new DeviceAdminInfo(mContext, resolveInfo);
+        assertEquals(mComponent, info.getComponent());
+        assertEquals(mComponent.getPackageName(), info.getPackageName());
+        assertEquals(mComponent.getClassName(), info.getReceiverName());
+
+        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK));
+        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
+        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD));
+        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN));
+        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA));
+
+        assertEquals("force-lock",
+                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK));
+        assertEquals("limit-password",
+                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
+        assertEquals("reset-password",
+                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD));
+        assertEquals("watch-login",
+                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN));
+        assertEquals("wipe-data",
+                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA));
+    }
+
+    public void testDeviceAdminInfo2() throws Exception {
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = mPackageManager.getReceiverInfo(mSecondComponent,
+                PackageManager.GET_META_DATA);
+
+        DeviceAdminInfo info = new DeviceAdminInfo(mContext, resolveInfo);
+        assertEquals(mSecondComponent, info.getComponent());
+        assertEquals(mSecondComponent.getPackageName(), info.getPackageName());
+        assertEquals(mSecondComponent.getClassName(), info.getReceiverName());
+
+        assertFalse(info.usesPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK));
+        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
+        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD));
+        assertFalse(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN));
+        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA));
+
+        assertEquals("force-lock",
+                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK));
+        assertEquals("limit-password",
+                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
+        assertEquals("reset-password",
+                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD));
+        assertEquals("watch-login",
+                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN));
+        assertEquals("wipe-data",
+                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA));
+    }
+}
diff --git a/tests/tests/admin/src/android/admin/cts/DeviceAdminReceiverTest.java b/tests/tests/admin/src/android/admin/cts/DeviceAdminReceiverTest.java
new file mode 100644
index 0000000..64ca4c3
--- /dev/null
+++ b/tests/tests/admin/src/android/admin/cts/DeviceAdminReceiverTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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 android.admin.cts;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.test.AndroidTestCase;
+
+public class DeviceAdminReceiverTest extends AndroidTestCase {
+
+    private static final String DISABLE_WARNING = "Disable Warning";
+
+    private static final int PASSWORD_CHANGED = 0x1;
+    private static final int PASSWORD_FAILED = 0x2;
+    private static final int PASSWORD_SUCCEEDED = 0x4;
+    private static final int DEVICE_ADMIN_ENABLED = 0x8;
+    private static final int DEVICE_ADMIN_DISABLE_REQUESTED = 0x10;
+    private static final int DEVICE_ADMIN_DISABLED = 0x20;
+
+    private TestReceiver mReceiver;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mReceiver = new TestReceiver();
+    }
+
+    public void testOnReceive() {
+        mReceiver.reset();
+        mReceiver.onReceive(mContext, new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED));
+        assertTrue(mReceiver.hasFlags(PASSWORD_CHANGED));
+
+        mReceiver.reset();
+        mReceiver.onReceive(mContext, new Intent(DeviceAdminReceiver.ACTION_PASSWORD_FAILED));
+        assertTrue(mReceiver.hasFlags(PASSWORD_FAILED));
+
+        mReceiver.reset();
+        mReceiver.onReceive(mContext, new Intent(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED));
+        assertTrue(mReceiver.hasFlags(PASSWORD_SUCCEEDED));
+
+        mReceiver.reset();
+        mReceiver.onReceive(mContext, new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED));
+        assertTrue(mReceiver.hasFlags(DEVICE_ADMIN_ENABLED));
+
+        mReceiver.reset();
+        mReceiver.onReceive(mContext, new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED));
+        assertTrue(mReceiver.hasFlags(DEVICE_ADMIN_DISABLED));
+
+        mReceiver.reset();
+        mReceiver.onReceive(mContext,
+                new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED));
+        assertTrue(mReceiver.hasFlags(DEVICE_ADMIN_DISABLE_REQUESTED));
+        assertNotNull(mReceiver.getResultExtras(false));
+        assertEquals(DISABLE_WARNING, mReceiver.getResultExtras(false)
+                .getString(DeviceAdminReceiver.EXTRA_DISABLE_WARNING));
+    }
+
+    private class TestReceiver extends DeviceAdminReceiver {
+
+        private int mFlags = 0;
+
+        void reset() {
+            mFlags = 0;
+        }
+
+        boolean hasFlags(int flags) {
+            return mFlags == flags;
+        }
+
+        @Override
+        public void onPasswordChanged(Context context, Intent intent) {
+            super.onPasswordChanged(context, intent);
+            mFlags |= PASSWORD_CHANGED;
+        }
+
+        @Override
+        public void onPasswordFailed(Context context, Intent intent) {
+            super.onPasswordFailed(context, intent);
+            mFlags |= PASSWORD_FAILED;
+        }
+
+        @Override
+        public void onPasswordSucceeded(Context context, Intent intent) {
+            super.onPasswordSucceeded(context, intent);
+            mFlags |= PASSWORD_SUCCEEDED;
+        }
+
+        @Override
+        public void onEnabled(Context context, Intent intent) {
+            super.onEnabled(context, intent);
+            mFlags |= DEVICE_ADMIN_ENABLED;
+        }
+
+        @Override
+        public CharSequence onDisableRequested(Context context, Intent intent) {
+            mFlags |= DEVICE_ADMIN_DISABLE_REQUESTED;
+            return DISABLE_WARNING;
+        }
+
+        @Override
+        public void onDisabled(Context context, Intent intent) {
+            super.onDisabled(context, intent);
+            mFlags |= DEVICE_ADMIN_DISABLED;
+        }
+    }
+}
diff --git a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
new file mode 100644
index 0000000..af47299
--- /dev/null
+++ b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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 android.admin.cts;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.test.AndroidTestCase;
+
+import java.util.List;
+
+/**
+ * Test that exercises {@link DevicePolicyManager}. The test requires that the
+ * CtsDeviceAdminReceiver be installed via the CtsDeviceAdmin.apk and be
+ * activated via "Settings > Location & security > Select device administrators".
+ */
+public class DevicePolicyManagerTest extends AndroidTestCase {
+
+    private DevicePolicyManager mDevicePolicyManager;
+
+    private ComponentName mComponent;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDevicePolicyManager = (DevicePolicyManager)
+                mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        mComponent = DeviceAdminInfoTest.getReceiverComponent();
+        setBlankPassword();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        setBlankPassword();
+    }
+
+    private void setBlankPassword() {
+        // Reset the password to nothing for future tests...
+        mDevicePolicyManager.setPasswordQuality(mComponent,
+                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 0);
+        assertTrue(mDevicePolicyManager.resetPassword("", 0));
+    }
+
+    public void testGetActiveAdmins() {
+        List<ComponentName> activeAdmins = mDevicePolicyManager.getActiveAdmins();
+        assertFalse(activeAdmins.isEmpty());
+        assertTrue(activeAdmins.contains(mComponent));
+        assertTrue(mDevicePolicyManager.isAdminActive(mComponent));
+    }
+
+    public void testGetMaximumTimeToLock() {
+        mDevicePolicyManager.setMaximumTimeToLock(mComponent, 3000);
+        assertEquals(3000, mDevicePolicyManager.getMaximumTimeToLock(mComponent));
+
+        mDevicePolicyManager.setMaximumTimeToLock(mComponent, 5000);
+        assertEquals(5000, mDevicePolicyManager.getMaximumTimeToLock(mComponent));
+    }
+
+    public void testGetMaximumFailedPasswordsForWipe() {
+        mDevicePolicyManager.setMaximumFailedPasswordsForWipe(mComponent, 3);
+        assertEquals(3, mDevicePolicyManager.getMaximumFailedPasswordsForWipe(mComponent));
+
+        mDevicePolicyManager.setMaximumFailedPasswordsForWipe(mComponent, 5);
+        assertEquals(5, mDevicePolicyManager.getMaximumFailedPasswordsForWipe(mComponent));
+    }
+
+    public void testPasswordQuality_something() {
+        mDevicePolicyManager.setPasswordQuality(mComponent,
+                DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+        assertEquals(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
+                mDevicePolicyManager.getPasswordQuality(mComponent));
+        assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertTrue(mDevicePolicyManager.resetPassword("123", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 10);
+        assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
+        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertFalse(mDevicePolicyManager.resetPassword("abcd123", 0));
+
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 3);
+        assertEquals(3, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertTrue(mDevicePolicyManager.resetPassword("123", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+    }
+
+    public void testPasswordQuality_numeric() {
+        mDevicePolicyManager.setPasswordQuality(mComponent,
+                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+        assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
+                mDevicePolicyManager.getPasswordQuality(mComponent));
+        assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertTrue(mDevicePolicyManager.resetPassword("123", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 10);
+        assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
+        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertFalse(mDevicePolicyManager.resetPassword("abcd123", 0));
+
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 3);
+        assertEquals(3, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertTrue(mDevicePolicyManager.resetPassword("123", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+    }
+
+    public void testPasswordQuality_alphabetic() {
+        mDevicePolicyManager.setPasswordQuality(mComponent,
+                DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
+        assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
+                mDevicePolicyManager.getPasswordQuality(mComponent));
+        assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 10);
+        assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
+        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertFalse(mDevicePolicyManager.resetPassword("abcd123", 0));
+
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 3);
+        assertEquals(3, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+    }
+
+    public void testPasswordQuality_alphanumeric() {
+        mDevicePolicyManager.setPasswordQuality(mComponent,
+                DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
+        assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
+                mDevicePolicyManager.getPasswordQuality(mComponent));
+        assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
+        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 10);
+        assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
+        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertFalse(mDevicePolicyManager.resetPassword("abcd123", 0));
+
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 3);
+        assertEquals(3, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+
+        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
+        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
+        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+    }
+}
diff --git a/tests/tests/net/src/android/net/http/cts/ApacheHttpClientTest.java b/tests/tests/net/src/android/net/http/cts/ApacheHttpClientTest.java
new file mode 100644
index 0000000..e4846fd
--- /dev/null
+++ b/tests/tests/net/src/android/net/http/cts/ApacheHttpClientTest.java
@@ -0,0 +1,210 @@
+/*
+ * 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 android.net.http.cts;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.webkit.cts.CtsTestServer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ApacheHttpClientTest extends AndroidTestCase {
+
+    private static final String TAG = ApacheHttpClientTest.class.getSimpleName();
+
+    private static final int NUM_DOWNLOADS = 20;
+
+    private static final int SMALL_DOWNLOAD_SIZE = 100 * 1024;
+
+    private CtsTestServer mWebServer;
+
+    private WifiManager mWifiManager;
+
+    private ConnectivityManager mConnectivityManager;
+
+    private boolean mHasTelephony;
+
+    private boolean mHasWifi;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mWebServer = new CtsTestServer(mContext);
+        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+        mConnectivityManager = (ConnectivityManager)
+                mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+        PackageManager packageManager = mContext.getPackageManager();
+        mHasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+        mHasWifi = packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mWebServer.shutdown();
+    }
+
+    public void testExecute_withMobile() throws Exception {
+        if (mHasTelephony) {
+            disconnectWifiToConnectToMobile();
+        }
+
+        downloadMultipleFiles();
+
+        if (mHasWifi) {
+            connectToWifi();
+        }
+    }
+
+    public void testExecute_withWifi() throws Exception {
+        if (mHasWifi) {
+            if (!mWifiManager.isWifiEnabled()) {
+                connectToWifi();
+            }
+            downloadMultipleFiles();
+        }
+    }
+
+    private void downloadMultipleFiles() throws ClientProtocolException, IOException {
+        List<HttpResponse> responses = new ArrayList<HttpResponse>();
+        for (int i = 0; i < NUM_DOWNLOADS; i++) {
+            HttpClient httpClient = new DefaultHttpClient();
+            HttpGet request = new HttpGet(getSmallDownloadUrl(i).toString());
+            HttpResponse response = httpClient.execute(request);
+            responses.add(response);
+        }
+
+        for (int i = 0; i < NUM_DOWNLOADS; i++) {
+            assertDownloadResponse("Download " + i, SMALL_DOWNLOAD_SIZE, responses.get(i));
+        }
+    }
+
+    private Uri getSmallDownloadUrl(int index) {
+        return Uri.parse(mWebServer.getTestDownloadUrl("cts-small-download-" + index,
+                SMALL_DOWNLOAD_SIZE));
+    }
+
+    private void assertDownloadResponse(String message, int expectedNumBytes, HttpResponse response)
+            throws IllegalStateException, IOException {
+        byte[] buffer = new byte[4096];
+        assertEquals(200, response.getStatusLine().getStatusCode());
+
+        InputStream stream = response.getEntity().getContent();
+        int numBytes = 0;
+        while (true) {
+            int bytesRead = stream.read(buffer);
+            if (bytesRead < 0) {
+                break;
+            } else {
+                numBytes += bytesRead;
+            }
+        }
+        assertEquals(message, SMALL_DOWNLOAD_SIZE, numBytes);
+    }
+
+    private void connectToWifi() throws InterruptedException {
+        if (!mWifiManager.isWifiEnabled()) {
+            ConnectivityActionReceiver receiver =
+                    new ConnectivityActionReceiver(ConnectivityManager.TYPE_WIFI, State.CONNECTED);
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+            mContext.registerReceiver(receiver, filter);
+
+            assertTrue(mWifiManager.setWifiEnabled(true));
+            assertTrue("Wifi must be configured to connect to an access point for this test.",
+                    receiver.waitForStateChange());
+
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    private void disconnectWifiToConnectToMobile() throws InterruptedException {
+        if (mHasWifi && mWifiManager.isWifiEnabled()) {
+            ConnectivityActionReceiver connectMobileReceiver =
+                    new ConnectivityActionReceiver(ConnectivityManager.TYPE_MOBILE,
+                            State.CONNECTED);
+            ConnectivityActionReceiver disconnectWifiReceiver =
+                    new ConnectivityActionReceiver(ConnectivityManager.TYPE_WIFI,
+                            State.DISCONNECTED);
+            IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+            mContext.registerReceiver(connectMobileReceiver, filter);
+            mContext.registerReceiver(disconnectWifiReceiver, filter);
+
+            assertTrue(mWifiManager.setWifiEnabled(false));
+            assertTrue(disconnectWifiReceiver.waitForStateChange());
+            assertTrue(connectMobileReceiver.waitForStateChange());
+
+            mContext.unregisterReceiver(connectMobileReceiver);
+            mContext.unregisterReceiver(disconnectWifiReceiver);
+        }
+    }
+
+    /** Receiver that captures the last connectivity change's network type and state. */
+    private class ConnectivityActionReceiver extends BroadcastReceiver {
+
+        private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
+
+        private final int mNetworkType;
+
+        private final State mExpectedState;
+
+        ConnectivityActionReceiver(int networkType, State expectedState) {
+            mNetworkType = networkType;
+            mExpectedState = expectedState;
+        }
+
+        public void onReceive(Context context, Intent intent) {
+            NetworkInfo networkInfo = intent.getExtras()
+                    .getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
+            int networkType = networkInfo.getType();
+            State networkState = networkInfo.getState();
+            Log.i(TAG, "Network type: " + networkType + " State: " + networkInfo.getState());
+            if (networkType == mNetworkType && networkInfo.getState() == mExpectedState) {
+                mReceiveLatch.countDown();
+            }
+        }
+
+        public boolean waitForStateChange() throws InterruptedException {
+            return hasExpectedState() || mReceiveLatch.await(30, TimeUnit.SECONDS);
+        }
+
+        private boolean hasExpectedState() {
+            return mExpectedState == mConnectivityManager.getNetworkInfo(mNetworkType).getState();
+        }
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/NoExecutePermissionTest.java b/tests/tests/os/src/android/os/cts/NoExecutePermissionTest.java
similarity index 81%
rename from tests/tests/permission/src/android/permission/cts/NoExecutePermissionTest.java
rename to tests/tests/os/src/android/os/cts/NoExecutePermissionTest.java
index 5c0a4c3..6450e58 100644
--- a/tests/tests/permission/src/android/permission/cts/NoExecutePermissionTest.java
+++ b/tests/tests/os/src/android/os/cts/NoExecutePermissionTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.permission.cts;
+package android.os.cts;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -29,6 +29,10 @@
 public class NoExecutePermissionTest extends TestCase {
 
     public void testNoExecutePermission() throws FileNotFoundException {
+        if (!cpuHasNxSupport()) {
+            return;
+        }
+
         String heapPermissions = null;
         String stackPermissions = null;
 
@@ -62,4 +66,16 @@
         assertEquals("NX (No Execute) not enabled for heap", "rw-p", heapPermissions);
         assertEquals("NX (No Execute) not enabled for stack", "rw-p", stackPermissions);
     }
+
+    private static boolean cpuHasNxSupport() {
+        if (CpuFeatures.isArmCpu() && !CpuFeatures.isArm7Compatible()) {
+            // ARM processors before v7 do not have NX support.
+            // http://code.google.com/p/android/issues/detail?id=17328
+            return false;
+        }
+
+        // TODO: handle other processors.  For now, assume those processors
+        // have NX support.
+        return true;
+    }
 }
diff --git a/tests/tests/security/src/android/security/cts/BannedFilesTest.java b/tests/tests/security/src/android/security/cts/BannedFilesTest.java
new file mode 100644
index 0000000..7a9c761
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/BannedFilesTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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 android.security.cts;
+
+import android.os.cts.FileUtils;
+
+import junit.framework.TestCase;
+
+public class BannedFilesTest extends TestCase {
+
+    /**
+     * setuid or setgid tcpdump can be used maliciously to monitor
+     * all traffic in and out of the device.
+     */
+    public void testNoSetuidTcpdump() {
+        assertNotSetugid("/system/bin/tcpdump");
+        assertNotSetugid("/system/bin/tcpdump-arm");
+        assertNotSetugid("/system/xbin/tcpdump");
+        assertNotSetugid("/system/xbin/tcpdump-arm");
+    }
+
+    private static void assertNotSetugid(String file) {
+        FileUtils.FileStatus fs = new FileUtils.FileStatus();
+        if (!FileUtils.getFileStatus(file, fs, false)) {
+            return;
+        }
+        assertTrue((fs.mode & FileUtils.S_ISUID) == 0);
+        assertTrue((fs.mode & FileUtils.S_ISGID) == 0);
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
new file mode 100644
index 0000000..4dddd5d
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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 android.security.cts;
+
+import com.android.cts.stub.R;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
+import android.content.res.Resources.NotFoundException;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class PackageSignatureTest extends AndroidTestCase {
+
+    private static final String TAG = PackageSignatureTest.class.getSimpleName();
+
+    public void testPackageSignatures() throws Exception {
+        Set<String> badPackages = new HashSet<String>();
+        Set<Signature> wellKnownSignatures = getWellKnownSignatures();
+
+        PackageManager packageManager = mContext.getPackageManager();
+        List<PackageInfo> allPackageInfos = packageManager.getInstalledPackages(
+                PackageManager.GET_UNINSTALLED_PACKAGES |
+                PackageManager.GET_SIGNATURES);
+        for (PackageInfo packageInfo : allPackageInfos) {
+            String packageName = packageInfo.packageName;
+            if (packageName != null && !isWhitelistedPackage(packageName)) {
+                for (Signature signature : packageInfo.signatures) {
+                    if (wellKnownSignatures.contains(signature)) {
+                        badPackages.add(packageInfo.packageName);
+                    }
+                }
+            }
+        }
+
+        assertTrue("These packages should not be signed with a well known key: " + badPackages,
+                badPackages.isEmpty());
+    }
+
+    private Set<Signature> getWellKnownSignatures() throws NotFoundException, IOException {
+        Set<Signature> wellKnownSignatures = new HashSet<Signature>();
+        wellKnownSignatures.add(getSignature(R.raw.sig_media));
+        wellKnownSignatures.add(getSignature(R.raw.sig_platform));
+        wellKnownSignatures.add(getSignature(R.raw.sig_shared));
+        wellKnownSignatures.add(getSignature(R.raw.sig_testkey));
+        return wellKnownSignatures;
+    }
+
+    private static final Set<String> WHITELISTED_PACKAGES = new HashSet<String>(Arrays.asList(
+            // The accessibility APK required to be installed while running CTS
+            "android.accessibilityservice.delegate",
+
+            // The device management APK required to be installed while running CTS
+            "android.deviceadmin.cts",
+
+            // APK for an activity that collects information printed in the CTS report header
+            "android.tests.devicesetup"
+            ));
+
+    private boolean isWhitelistedPackage(String packageName) {
+        // Don't check the signatures of CTS test packages on the device.
+        // devicesetup is the APK CTS loads to collect information needed in the final report
+        return packageName.startsWith("com.android.cts")
+                || WHITELISTED_PACKAGES.contains(packageName);
+    }
+
+    private static final int DEFAULT_BUFFER_BYTES = 1024 * 4;
+
+    private Signature getSignature(int resId) throws NotFoundException, IOException {
+        InputStream input = mContext.getResources().openRawResource(resId);
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+        try {
+            byte[] buffer = new byte[DEFAULT_BUFFER_BYTES];
+            int numBytes = 0;
+            while ((numBytes = input.read(buffer)) != -1) {
+                output.write(buffer, 0, numBytes);
+            }
+            return new Signature(output.toByteArray());
+        } finally {
+            input.close();
+            output.close();
+        }
+    }
+
+    /**
+     * Writes a package's signature to a file on the device's external storage.
+     * This method was used to generate the well known signatures used by this test.
+     */
+    @SuppressWarnings("unused")
+    private void writeSignature(String packageName, String fileName)
+            throws NameNotFoundException, IOException {
+        PackageManager packageManager = mContext.getPackageManager();
+        PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
+                PackageManager.GET_SIGNATURES);
+        File directory = mContext.getExternalFilesDir(null);
+        int numSignatures = packageInfo.signatures.length;
+        Log.i(TAG, "Will dump " + numSignatures + " signatures to " + directory);
+        for (int i = 0; i < numSignatures; i++) {
+            Signature signature = packageInfo.signatures[i];
+            byte[] signatureBytes = signature.toByteArray();
+            File signatureFile = new File(directory, fileName + "." + i);
+            FileOutputStream output = null;
+            try {
+                output = new FileOutputStream(signatureFile);
+                output.write(signatureBytes);
+            } finally {
+                if (output != null) {
+                    output.close();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/VoldExploitTest.java b/tests/tests/security/src/android/security/cts/VoldExploitTest.java
index df5e58a..38eece7 100644
--- a/tests/tests/security/src/android/security/cts/VoldExploitTest.java
+++ b/tests/tests/security/src/android/security/cts/VoldExploitTest.java
@@ -51,8 +51,8 @@
         devices.addAll(getSysFsPath("/etc/vold.fstab"));
         devices.addAll(getSysFsPath("/system/etc/vold.fstab"));
         if (devices.isEmpty()) {
-          // FIXME: We should be able to detect this security hole
-          // even if there's no vold.fstab entry
+          // This vulnerability is not exploitable if there's
+          // no entry in vold.fstab
           return;
         }
 
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
old mode 100755
new mode 100644
index 510b0dc..c7d9b38
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -31,6 +31,8 @@
 import android.telephony.SmsManager;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
+import android.telephony.SmsMessage;
+import android.os.Bundle;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -54,6 +56,7 @@
 
     private static final String SMS_SEND_ACTION = "CTS_SMS_SEND_ACTION";
     private static final String SMS_DELIVERY_ACTION = "CTS_SMS_DELIVERY_ACTION";
+    private static final String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED";
 
     // List of network operators that don't support SMS delivery report
     private static final List<String> NO_DELIVERY_REPORTS =
@@ -74,7 +77,9 @@
                     "44073",    // KDDI
                     "44074",    // KDDI
                     "44075",    // KDDI
-                    "44076"     // KDDI
+                    "44076",    // KDDI
+                    "311870",   // Boost Mobile
+                    "311220"    // USCC
             );
 
     // List of network operators that doesn't support Data(binary) SMS message
@@ -97,11 +102,14 @@
     private String mText;
     private SmsBroadcastReceiver mSendReceiver;
     private SmsBroadcastReceiver mDeliveryReceiver;
+    private SmsBroadcastReceiver mDataSmsReceiver;
     private PendingIntent mSentIntent;
     private PendingIntent mDeliveredIntent;
     private Intent mSendIntent;
     private Intent mDeliveryIntent;
     private boolean mDeliveryReportSupported;
+    private static boolean mReceivedDataSms;
+    private static String mReceivedText;
 
     private static final int TIME_OUT = 1000 * 60 * 5;
 
@@ -167,12 +175,17 @@
 
         IntentFilter sendIntentFilter = new IntentFilter(SMS_SEND_ACTION);
         IntentFilter deliveryIntentFilter = new IntentFilter(SMS_DELIVERY_ACTION);
+        IntentFilter dataSmsReceivedIntentFilter = new IntentFilter(DATA_SMS_RECEIVED_ACTION);
+        dataSmsReceivedIntentFilter.addDataScheme("sms");
+        dataSmsReceivedIntentFilter.addDataAuthority("localhost", "19989");
 
         mSendReceiver = new SmsBroadcastReceiver(SMS_SEND_ACTION);
         mDeliveryReceiver = new SmsBroadcastReceiver(SMS_DELIVERY_ACTION);
+        mDataSmsReceiver = new SmsBroadcastReceiver(DATA_SMS_RECEIVED_ACTION);
 
         getContext().registerReceiver(mSendReceiver, sendIntentFilter);
         getContext().registerReceiver(mDeliveryReceiver, deliveryIntentFilter);
+        getContext().registerReceiver(mDataSmsReceiver, dataSmsReceivedIntentFilter);
 
         // send single text sms
         init();
@@ -198,6 +211,9 @@
             if (mDeliveryReportSupported) {
                 assertTrue(mDeliveryReceiver.waitForCalls(1, TIME_OUT));
             }
+            mDataSmsReceiver.waitForCalls(1, TIME_OUT);
+            assertTrue(mReceivedDataSms);
+            assertEquals(mReceivedText, mText);
         } else {
             // This GSM network doesn't support Data(binary) SMS message.
             // Skip the test.
@@ -228,6 +244,8 @@
     private void init() {
         mSendReceiver.reset();
         mDeliveryReceiver.reset();
+        mDataSmsReceiver.reset();
+        mReceivedDataSms = false;
         mSentIntent = PendingIntent.getBroadcast(getContext(), 0, mSendIntent,
                 PendingIntent.FLAG_ONE_SHOT);
         mDeliveredIntent = PendingIntent.getBroadcast(getContext(), 0, mDeliveryIntent,
@@ -283,6 +301,25 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
+            if(mAction.equals(DATA_SMS_RECEIVED_ACTION)){
+                StringBuilder sb = new StringBuilder();
+                Bundle bundle = intent.getExtras();
+                if (bundle != null) {
+                    Object[] obj = (Object[]) bundle.get("pdus");
+                    SmsMessage[] message = new SmsMessage[obj.length];
+                    for (int i = 0; i < obj.length; i++) {
+                        message[i] = SmsMessage.createFromPdu((byte[]) obj[i]);
+                    }
+
+                    for (SmsMessage currentMessage : message) {
+                        byte[] binaryContent = currentMessage.getUserData();
+                        String readableContent = new String(binaryContent);
+                        sb.append(readableContent);
+                    }
+                }
+                mReceivedDataSms = true;
+                mReceivedText=sb.toString();
+            }
             if (intent.getAction().equals(mAction)) {
                 synchronized (mLock) {
                     mCalls += 1;
diff --git a/tools/host/src/com/android/cts/Version.java b/tools/host/src/com/android/cts/Version.java
index 9d320f7..fefe762 100644
--- a/tools/host/src/com/android/cts/Version.java
+++ b/tools/host/src/com/android/cts/Version.java
@@ -18,7 +18,7 @@
 
 public class Version {
     // The CTS version string
-    private static final String version = "3.1_r1";
+    private static final String version = "3.1_r2";
 
     private Version() {
         // no instances allowed