Rewritting Holo tests as hostside tests for speed and stability

Change-Id: I3936ad03b6c9249a0331471461cb48d5ba809f59
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 2b61134..b874af0 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -32,6 +32,7 @@
 	CtsAccelerationTestStubs \
 	CtsDelegatingAccessibilityService \
 	CtsDeviceAdmin \
+	CtsHoloDeviceApp \
 	CtsMonkeyApp \
 	CtsMonkeyApp2 \
 	CtsSomeAccessibilityServices \
@@ -109,6 +110,7 @@
 # Host side only tests
 cts_host_libraries := \
 	CtsAppSecurityTests \
+	CtsHoloHostTestCases \
 	CtsMonkeyTestCases
 
 # Native test executables that need to have associated test XMLs.
diff --git a/hostsidetests/holo/Android.mk b/hostsidetests/holo/Android.mk
new file mode 100644
index 0000000..25f7021
--- /dev/null
+++ b/hostsidetests/holo/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2014 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_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_RESOURCE_DIRS := assets/$(current)
+
+LOCAL_MODULE_TAGS := optional
+
+# Must match the package name in CtsTestCaseList.mk
+LOCAL_MODULE := CtsHoloHostTestCases
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed ddmlib-prebuilt tradefed-prebuilt
+
+LOCAL_CTS_TEST_PACKAGE := android.host.holo
+
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/holo/app/Android.mk b/hostsidetests/holo/app/Android.mk
new file mode 100644
index 0000000..a5a7bf1
--- /dev/null
+++ b/hostsidetests/holo/app/Android.mk
@@ -0,0 +1,37 @@
+# Copyright (C) 2014 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)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+# and because it is in data, do not strip classes.dex
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsHoloDeviceApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/holo/app/AndroidManifest.xml b/hostsidetests/holo/app/AndroidManifest.xml
new file mode 100755
index 0000000..70e908c
--- /dev/null
+++ b/hostsidetests/holo/app/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2014 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.holo.app">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".HoloDeviceActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".DisplayInfoActivity"
+                  android:label="@string/display_info" />
+        <activity android:name=".CaptureActivity" />
+    </application>
+
+    <!--  self-instrumenting test package. -->
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="android.holo.app"
+                     android:label="Generates Holo reference images"/>
+
+</manifest>
+
diff --git a/hostsidetests/holo/app/res/drawable-400dpi/display_info.png b/hostsidetests/holo/app/res/drawable-400dpi/display_info.png
new file mode 100644
index 0000000..e5f1f96
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-400dpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-hdpi/display_info.png b/hostsidetests/holo/app/res/drawable-hdpi/display_info.png
new file mode 100644
index 0000000..10b3950
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-hdpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-land-400dpi/display_info.png b/hostsidetests/holo/app/res/drawable-land-400dpi/display_info.png
new file mode 100644
index 0000000..1b74e01
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-land-400dpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-land-hdpi/display_info.png b/hostsidetests/holo/app/res/drawable-land-hdpi/display_info.png
new file mode 100644
index 0000000..a665018
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-land-hdpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-land-ldpi/display_info.png b/hostsidetests/holo/app/res/drawable-land-ldpi/display_info.png
new file mode 100644
index 0000000..64c8f3a
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-land-ldpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-land-mdpi/display_info.png b/hostsidetests/holo/app/res/drawable-land-mdpi/display_info.png
new file mode 100644
index 0000000..f3e6765
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-land-mdpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-land-tvdpi/display_info.png b/hostsidetests/holo/app/res/drawable-land-tvdpi/display_info.png
new file mode 100644
index 0000000..99de970
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-land-tvdpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-land-xhdpi/display_info.png b/hostsidetests/holo/app/res/drawable-land-xhdpi/display_info.png
new file mode 100644
index 0000000..4c0c2b4
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-land-xhdpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-land-xxhdpi/display_info.png b/hostsidetests/holo/app/res/drawable-land-xxhdpi/display_info.png
new file mode 100644
index 0000000..c6f7fd8
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-land-xxhdpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-ldpi/display_info.png b/hostsidetests/holo/app/res/drawable-ldpi/display_info.png
new file mode 100644
index 0000000..af1fda5
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-ldpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-mdpi/display_info.png b/hostsidetests/holo/app/res/drawable-mdpi/display_info.png
new file mode 100644
index 0000000..4378b14
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-mdpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-tvdpi/display_info.png b/hostsidetests/holo/app/res/drawable-tvdpi/display_info.png
new file mode 100644
index 0000000..d9825fb
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-tvdpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-xhdpi/display_info.png b/hostsidetests/holo/app/res/drawable-xhdpi/display_info.png
new file mode 100644
index 0000000..585af2f
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-xhdpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/drawable-xxhdpi/display_info.png b/hostsidetests/holo/app/res/drawable-xxhdpi/display_info.png
new file mode 100644
index 0000000..255c28f
--- /dev/null
+++ b/hostsidetests/holo/app/res/drawable-xxhdpi/display_info.png
Binary files differ
diff --git a/hostsidetests/holo/app/res/layout/button.xml b/hostsidetests/holo/app/res/layout/button.xml
new file mode 100644
index 0000000..75c480c
--- /dev/null
+++ b/hostsidetests/holo/app/res/layout/button.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/button"
+        />
diff --git a/hostsidetests/holo/app/res/layout/checkbox.xml b/hostsidetests/holo/app/res/layout/checkbox.xml
new file mode 100644
index 0000000..8eeac39
--- /dev/null
+++ b/hostsidetests/holo/app/res/layout/checkbox.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
+        android:text="@string/checkbox"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+
diff --git a/hostsidetests/holo/app/res/layout/chronometer.xml b/hostsidetests/holo/app/res/layout/chronometer.xml
new file mode 100644
index 0000000..c4ad8d3
--- /dev/null
+++ b/hostsidetests/holo/app/res/layout/chronometer.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<Chronometer xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
diff --git a/hostsidetests/holo/app/res/layout/display_info.xml b/hostsidetests/holo/app/res/layout/display_info.xml
new file mode 100644
index 0000000..167d935
--- /dev/null
+++ b/hostsidetests/holo/app/res/layout/display_info.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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"
+    >
+    <ImageView
+        android:src="@drawable/display_info"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        />
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        />
+</LinearLayout>
+
diff --git a/hostsidetests/holo/app/res/layout/holo_test.xml b/hostsidetests/holo/app/res/layout/holo_test.xml
new file mode 100644
index 0000000..0ae6953
--- /dev/null
+++ b/hostsidetests/holo/app/res/layout/holo_test.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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"
+        android:id="@+id/base_view">
+</LinearLayout>
diff --git a/hostsidetests/holo/app/res/values/strings.xml b/hostsidetests/holo/app/res/values/strings.xml
new file mode 100644
index 0000000..02b4080
--- /dev/null
+++ b/hostsidetests/holo/app/res/values/strings.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <string name="holo_test_utilities">Holo Test Utilities</string>
+
+    <string name="display_info">Display Info</string>
+    <string name="display_info_text">Density DPI: %1$d\nDensity Bucket: %2$s\nWidth DP: %3$d\nHeight DP: %4$d</string>
+
+    <string name="button">Button</string>
+    <string name="checkbox">CheckBox</string>
+    <string name="chronometer">Chronometer</string>
+    <string name="datepicker">DatePicker</string>
+    <string name="edittext">EditText</string>
+    <string name="progressbar">ProgressBar</string>
+    <string name="progressbar_small">ProgressBar Small</string>
+    <string name="progressbar_large">ProgressBar Large</string>
+    <string name="progressbar_horizontal_0">ProgressBar Horizontal 0%</string>
+    <string name="progressbar_horizontal_50">ProgressBar Horizontal 50%</string>
+    <string name="progressbar_horizontal_100">ProgressBar Horizontal 100%</string>
+    <string name="radiobutton">RadioButton</string>
+    <string name="radiobutton_checked">RadioButton Checked</string>
+    <string name="radiogroup_horizontal">RadioGroup Horizontal</string>
+    <string name="radiogroup_vertical">RadioGroup Vertical</string>
+    <string name="ratingbar_0">RatingBar 0 Stars</string>
+    <string name="ratingbar_2point5">RatingBar 2.5 Stars</string>
+    <string name="ratingbar_5">RatingBar 5 Stars</string>
+    <string name="ratingbar_0_pressed">RatingBar 0 Stars Pressed</string>
+    <string name="ratingbar_2point5_pressed">RatingBar 2.5 Stars Pressed</string>
+    <string name="ratingbar_5_pressed">RatingBar 5 Stars Pressed</string>
+    <string name="searchview">SearchView</string>
+    <string name="searchview_query">SearchView Query</string>
+    <string name="searchview_query_hint">SearchView Query Hint</string>
+    <string name="seekbar_0">SeekBar 0%</string>
+    <string name="seekbar_50">SeekBar 50%</string>
+    <string name="seekbar_100">SeekBar 100%</string>
+    <string name="spinner">Spinner</string>
+    <string name="switch_button">Switch</string>
+    <string name="switch_button_checked">Switch Checked</string>
+    <string name="tabhost">TabHost</string>
+    <string name="textview">TextView</string>
+    <string name="timepicker">TimePicker</string>
+    <string name="togglebutton">ToggleButton</string>
+    <string name="togglebutton_checked">ToggleButton Checked</string>
+    <string name="zoomcontrols">ZoomControls</string>
+
+    <string name="alertdialog_onebutton">AlertDialog One Button</string>
+    <string name="alertdialog_twobuttons">AlertDialog Two Buttons</string>
+    <string name="alertdialog_threebuttons">AlertDialog Three Buttons</string>
+    <string name="alertdialog_list">AlertDialog List</string>
+    <string name="alertdialog_singlechoice">AlertDialog Single Choice</string>
+    <string name="alertdialog_multichoice">AlertDialog Multiple Choice</string>
+    <string name="progressdialog_spinner">ProgressDialog Spinner</string>
+    <string name="progressdialog_horizontal">ProgressDialog Horizontal</string>
+
+    <string name="color_blue_bright">Bright Blue Color</string>
+    <string name="color_blue_dark">Dark Blue Color</string>
+    <string name="color_blue_light">Light Blue Color</string>
+    <string name="color_green_dark">Dark Green Color</string>
+    <string name="color_green_light">Light Green Color</string>
+    <string name="color_orange_dark">Dark Orange Color</string>
+    <string name="color_orange_light">Light Orange Color</string>
+    <string name="color_purple">Purple Color</string>
+    <string name="color_red_dark">Dark Red Color</string>
+    <string name="color_red_light">Light Red Color</string>
+
+</resources>
diff --git a/hostsidetests/holo/app/src/android/holo/app/CaptureActivity.java b/hostsidetests/holo/app/src/android/holo/app/CaptureActivity.java
new file mode 100644
index 0000000..9551877
--- /dev/null
+++ b/hostsidetests/holo/app/src/android/holo/app/CaptureActivity.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 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.holo.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Iterates through all themes and all layouts, starting the Activity to capture the images.
+ */
+public class CaptureActivity extends Activity {
+
+    private static final int REQUEST_CODE = 1;
+
+    private static final int NUM_THEMES = 24;
+
+    private static final int NUM_LAYOUTS = 5;
+
+    private final CountDownLatch mLatch = new CountDownLatch(1);
+
+    private int mCurrentTheme = 0;
+
+    private int mCurrentLayout = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        generateNextImage();
+    }
+
+    /**
+     * Starts the activity to generate the next image.
+     */
+    private void generateNextImage() {
+        Intent intent = new Intent(this, HoloDeviceActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        intent.putExtra(HoloDeviceActivity.EXTRA_THEME, mCurrentTheme);
+        intent.putExtra(HoloDeviceActivity.EXTRA_LAYOUT, mCurrentLayout);
+        startActivityForResult(intent, REQUEST_CODE);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CODE) {
+            if (resultCode == RESULT_OK) {
+                mCurrentLayout++;
+                if (mCurrentLayout >= NUM_LAYOUTS) {
+                    mCurrentLayout = 0;
+                    mCurrentTheme++;
+                }
+                if (mCurrentTheme < NUM_THEMES) {
+                    generateNextImage();
+                } else {
+                    finish();
+                }
+            } else {
+                finish();
+            }
+        }
+    }
+
+    public void finish() {
+        mLatch.countDown();
+        super.finish();
+    }
+
+    public void waitForCompletion() throws InterruptedException {
+        mLatch.await();
+    }
+}
diff --git a/hostsidetests/holo/app/src/android/holo/app/CaptureHolo.java b/hostsidetests/holo/app/src/android/holo/app/CaptureHolo.java
new file mode 100644
index 0000000..228cd9c
--- /dev/null
+++ b/hostsidetests/holo/app/src/android/holo/app/CaptureHolo.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 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.holo.app;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.test.ActivityInstrumentationTestCase2;
+
+public class CaptureHolo extends ActivityInstrumentationTestCase2<CaptureActivity> {
+
+    public CaptureHolo() {
+        super(CaptureActivity.class);
+    }
+
+    public void testCaptureHolo() throws Exception {
+        setActivityInitialTouchMode(true);
+        CaptureActivity activity = getActivity();
+        KeyguardManager keyguardManager =
+                (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
+        keyguardManager.newKeyguardLock("holo_capture").disableKeyguard();
+        activity.waitForCompletion();
+    }
+}
diff --git a/hostsidetests/holo/app/src/android/holo/app/DisplayInfoActivity.java b/hostsidetests/holo/app/src/android/holo/app/DisplayInfoActivity.java
new file mode 100644
index 0000000..4c40d01
--- /dev/null
+++ b/hostsidetests/holo/app/src/android/holo/app/DisplayInfoActivity.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 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.holo.app;
+
+import android.app.Activity;
+import android.holo.app.R;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+/**
+ * An activity to display information about the device, including density bucket and dimensions.
+ */
+public class DisplayInfoActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.display_info);
+
+        WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
+        Display display = windowManager.getDefaultDisplay();
+        DisplayMetrics metrics = new DisplayMetrics();
+        display.getMetrics(metrics);
+
+        DisplayMetrics dm = getResources().getDisplayMetrics();
+        int width = Math.round(dm.widthPixels / dm.density);
+        int height = Math.round(dm.heightPixels / dm.density);
+
+        TextView text = (TextView) findViewById(R.id.text);
+        text.setText(getString(R.string.display_info_text, metrics.densityDpi,
+                getScreenDensityBucket(metrics), width, height));
+    }
+
+    private static String getScreenDensityBucket(DisplayMetrics metrics) {
+        switch (metrics.densityDpi) {
+            case DisplayMetrics.DENSITY_LOW:
+                return "ldpi";
+
+            case DisplayMetrics.DENSITY_MEDIUM:
+                return "mdpi";
+
+            case DisplayMetrics.DENSITY_HIGH:
+                return "hdpi";
+
+            case DisplayMetrics.DENSITY_XHIGH:
+                return "xhdpi";
+
+            case DisplayMetrics.DENSITY_XXHIGH:
+                return "xxhdpi";
+
+            case DisplayMetrics.DENSITY_TV:
+                return "tvdpi";
+
+            default:
+                return "" + metrics.densityDpi;
+        }
+    }
+}
diff --git a/hostsidetests/holo/app/src/android/holo/app/HoloDeviceActivity.java b/hostsidetests/holo/app/src/android/holo/app/HoloDeviceActivity.java
new file mode 100644
index 0000000..bfaf865
--- /dev/null
+++ b/hostsidetests/holo/app/src/android/holo/app/HoloDeviceActivity.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2014 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.holo.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.Canvas;
+import android.holo.app.R;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.Override;
+
+/**
+ * A activity which display various UI elements with Holo theme.
+ */
+public class HoloDeviceActivity extends Activity {
+
+    public static final String EXTRA_THEME = "holo_theme_extra";
+
+    public static final String EXTRA_LAYOUT = "holo_layout_extra";
+
+    public static final String EXTRA_TIMEOUT = "holo_timeout_extra";
+
+    private static final String TAG = HoloDeviceActivity.class.getSimpleName();
+
+    private static final int TIMEOUT = 1 * 1000;//1 sec
+
+    private View mView;
+
+    private String mName;
+
+    private Bitmap mBitmap;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setUpUi(getIntent());
+    }
+
+    @Override
+    public void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        setUpUi(intent);
+    }
+
+    /**
+     * Configures the UI with the given intent
+     */
+    private void setUpUi(Intent intent) {
+        final Theme theme = themes[intent.getIntExtra(EXTRA_THEME, 0)];
+        final Layout layout = layouts[intent.getIntExtra(EXTRA_LAYOUT, 0)];
+        final int timeout = intent.getIntExtra(EXTRA_TIMEOUT, TIMEOUT);
+
+        setTheme(theme.mId);
+        setContentView(R.layout.holo_test);
+
+        final LinearLayout baseView = (LinearLayout) findViewById(R.id.base_view);
+
+        mView = getLayoutInflater().inflate(layout.mId, baseView, false);
+        baseView.addView(mView);
+        if (layout.mModifier != null) {
+            layout.mModifier.modify(mView);
+        }
+        mView.setFocusable(false);
+        mName = String.format("%s_%s", theme.mName, layout.mName);
+
+        final Handler handler = new Handler();
+        handler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                new GenerateBitmapTask().execute();
+            }
+        }, timeout);
+        setResult(RESULT_CANCELED);//On success will be changed to OK
+    }
+
+    /**
+     * A task which gets the UI element to render to a bitmap and then saves that as a png
+     * asynchronously
+     */
+    private class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
+
+        @Override
+        protected void onPreExecute() {
+            final View v = mView;
+            mBitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
+            final Canvas canvas = new Canvas(mBitmap);
+            v.draw(canvas);
+        }
+
+        @Override
+        protected Boolean doInBackground(Void... ignored) {
+            if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                Log.i(TAG, "External storage for saving bitmaps is not mounted");
+                return false;
+            }
+            final File dir = new File(Environment.getExternalStorageDirectory(), "cts-holo-assets");
+            dir.mkdirs();
+            boolean success = false;
+            try {
+                final File file = new File(dir, mName + ".png");
+                FileOutputStream stream = null;
+                try {
+                    stream = new FileOutputStream(file);
+                    mBitmap.compress(CompressFormat.PNG, 100, stream);
+                } finally {
+                    if (stream != null) {
+                        stream.close();
+                    }
+                }
+                success = true;
+            } catch (Exception e) {
+                Log.e(TAG, e.getMessage());
+            } finally {
+                mBitmap.recycle();
+                mBitmap = null;
+            }
+            return success;
+        }
+
+        @Override
+        protected void onPostExecute(Boolean success) {
+            Log.i(TAG, success ? "OKAY" : "ERROR");
+            setResult(RESULT_OK);
+            finish();
+        }
+    }
+
+    /**
+     * A class to encapsulate information about a holo theme.
+     */
+    private static class Theme {
+
+        public final int mId;
+
+        public final String mName;
+
+        private Theme(int id, String name) {
+            mId = id;
+            mName = name;
+        }
+    }
+
+    private static final Theme[] themes = {
+            new Theme(android.R.style.Theme_Holo,
+                    "holo"),
+            new Theme(android.R.style.Theme_Holo_Dialog,
+                    "holo_dialog"),
+            new Theme(android.R.style.Theme_Holo_Dialog_MinWidth,
+                    "holo_dialog_minwidth"),
+            new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar,
+                    "holo_dialog_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
+                    "holo_dialog_noactionbar_minwidth"),
+            new Theme(android.R.style.Theme_Holo_DialogWhenLarge,
+                    "holo_dialogwhenlarge"),
+            new Theme(android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
+                    "holo_dialogwhenlarge_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_InputMethod,
+                    "holo_inputmethod"),
+            new Theme(android.R.style.Theme_Holo_Light,
+                    "holo_light"),
+            new Theme(android.R.style.Theme_Holo_Light_DarkActionBar,
+                    "holo_light_darkactionbar"),
+            new Theme(android.R.style.Theme_Holo_Light_Dialog,
+                    "holo_light_dialog"),
+            new Theme(android.R.style.Theme_Holo_Light_Dialog_MinWidth,
+                    "holo_light_dialog_minwidth"),
+            new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
+                    "holo_light_dialog_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
+                    "holo_light_dialog_noactionbar_minwidth"),
+            new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge,
+                    "holo_light_dialogwhenlarge"),
+            new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
+                    "holo_light_dialogwhenlarge_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_Light_NoActionBar,
+                    "holo_light_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
+                    "holo_light_noactionbar_fullscreen"),
+            new Theme(android.R.style.Theme_Holo_Light_Panel,
+                    "holo_light_panel"),
+            new Theme(android.R.style.Theme_Holo_NoActionBar,
+                    "holo_noactionbar"),
+            new Theme(android.R.style.Theme_Holo_NoActionBar_Fullscreen,
+                    "holo_noactionbar_fullscreen"),
+            new Theme(android.R.style.Theme_Holo_Panel,
+                    "holo_panel"),
+            new Theme(android.R.style.Theme_Holo_Wallpaper,
+                    "holo_wallpaper"),
+            new Theme(android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
+                    "holo_wallpaper_notitlebar")
+    };
+
+    /**
+     * A class to encapsulate information about a holo layout.
+     */
+    private static class Layout {
+
+        public final int mId;
+
+        public final String mName;
+
+        public final Modifier mModifier;
+
+        private Layout(int id, String name, Modifier modifier) {
+            mId = id;
+            mName = name;
+            mModifier = modifier;
+        }
+    }
+
+    private static interface Modifier {
+
+        public void modify(View v);
+    }
+
+    private static final Layout[] layouts = {
+            new Layout(R.layout.button, "button", null),
+            new Layout(R.layout.button, "button_pressed", new Modifier() {
+                @Override
+                public void modify(View v) {
+                    v.setPressed(true);
+                }
+            }),
+            new Layout(R.layout.checkbox, "checkbox", null),
+            new Layout(R.layout.checkbox, "checkbox_checked", new Modifier() {
+                @Override
+                public void modify(View v) {
+                    ((CheckBox) v).setChecked(true);
+                }
+            }),
+            new Layout(R.layout.chronometer, "chronometer", null)
+    };
+}
diff --git a/hostsidetests/holo/assets/17/hdpi.zip b/hostsidetests/holo/assets/17/hdpi.zip
new file mode 100644
index 0000000..1f297a7
--- /dev/null
+++ b/hostsidetests/holo/assets/17/hdpi.zip
Binary files differ
diff --git a/hostsidetests/holo/assets/17/ldpi.zip b/hostsidetests/holo/assets/17/ldpi.zip
new file mode 100644
index 0000000..045cd3a
--- /dev/null
+++ b/hostsidetests/holo/assets/17/ldpi.zip
Binary files differ
diff --git a/hostsidetests/holo/assets/17/mdpi.zip b/hostsidetests/holo/assets/17/mdpi.zip
new file mode 100644
index 0000000..906366b
--- /dev/null
+++ b/hostsidetests/holo/assets/17/mdpi.zip
Binary files differ
diff --git a/hostsidetests/holo/assets/17/tvdpi.zip b/hostsidetests/holo/assets/17/tvdpi.zip
new file mode 100644
index 0000000..9556b84
--- /dev/null
+++ b/hostsidetests/holo/assets/17/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/holo/assets/17/xhdpi.zip b/hostsidetests/holo/assets/17/xhdpi.zip
new file mode 100644
index 0000000..be53054
--- /dev/null
+++ b/hostsidetests/holo/assets/17/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/holo/assets/17/xxhdpi.zip b/hostsidetests/holo/assets/17/xxhdpi.zip
new file mode 100644
index 0000000..cf69fae
--- /dev/null
+++ b/hostsidetests/holo/assets/17/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/holo/src/android/holo/cts/ComparisonTask.java b/hostsidetests/holo/src/android/holo/cts/ComparisonTask.java
new file mode 100644
index 0000000..36f3759
--- /dev/null
+++ b/hostsidetests/holo/src/android/holo/cts/ComparisonTask.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2014 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.holo.cts;
+
+import com.android.ddmlib.Log;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.tradefed.device.ITestDevice;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.lang.String;
+import java.util.concurrent.Callable;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Compares the images generated by the device with the reference images.
+ */
+public class ComparisonTask implements Callable<Boolean> {
+
+    private static final String TAG = ComparisonTask.class.getSimpleName();
+
+    private static final String DEVICE_PATH = "/storage/emulated/legacy/cts-holo-assets/%s.png";
+
+    private static final int IMAGE_THRESHOLD = 2;
+
+    private final ITestDevice mDevice;
+
+    private final File mReference;
+
+    private final String mName;
+
+    public ComparisonTask(ITestDevice device, File reference, String name) {
+        mDevice = device;
+        mReference = reference;
+        mName = name;
+    }
+
+    public Boolean call() {
+        boolean success = false;
+        File generated = null;
+        try {
+            generated = File.createTempFile("gen_" + mName, ".png");
+
+            mDevice.pullFile(String.format(DEVICE_PATH, mName), generated);
+
+            final BufferedImage ref = ImageIO.read(mReference);
+            final BufferedImage gen = ImageIO.read(generated);
+            if (compare(ref, gen, IMAGE_THRESHOLD)) {
+                success = true;
+            } else {
+                File diff = File.createTempFile("diff_" + mName, ".png");
+                createDiff(ref, gen, diff);
+                Log.logAndDisplay(LogLevel.INFO, TAG, "Diff created: " + diff.getPath());
+            }
+        } catch (Exception e) {
+            Log.logAndDisplay(LogLevel.ERROR, TAG, e.toString());
+        } finally {
+            if (generated != null) {
+                generated.delete();
+            }
+        }
+        return success;
+    }
+
+    private static boolean compare(BufferedImage reference, BufferedImage generated, int threshold) {
+        final int w = generated.getWidth();
+        final int h = generated.getHeight();
+        if (w != reference.getWidth() || h != reference.getHeight()) {
+            return false;
+        }
+
+        for (int i = 0; i < w; i++) {
+            for (int j = 0; j < h; j++) {
+                final int p1 = reference.getRGB(i, j);
+                final int p2 = generated.getRGB(i, j);
+                final int dr = (p1 & 0x000000FF) - (p2 & 0x000000FF);
+                final int dg = ((p1 & 0x0000FF00) - (p2 & 0x0000FF00)) >> 8;
+                final int db = ((p1 & 0x00FF0000) - (p2 & 0x00FF0000)) >> 16;
+                final int da = ((p1 & 0xFF000000) - (p2 & 0xFF000000)) >> 24;
+
+                if (Math.abs(db) > threshold ||
+                        Math.abs(dg) > threshold ||
+                        Math.abs(dr) > threshold ||
+                        Math.abs(da) > threshold) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private static void createDiff(BufferedImage image1, BufferedImage image2, File out)
+            throws Exception {
+        final int w1 = image1.getWidth();
+        final int h1 = image1.getHeight();
+        final int w2 = image2.getWidth();
+        final int h2 = image2.getHeight();
+        final int width = Math.max(w1, w2);
+        final int height = Math.max(h1, h2);
+        // The diff will contain image1, image2 and the difference between the two.
+        final BufferedImage diff = new BufferedImage(width * 3, height, BufferedImage.TYPE_INT_ARGB);
+
+        for (int i = 0; i < width; i++) {
+            for (int j = 0; j < height; j++) {
+                final boolean inBounds1 = i < w1 && j < h1;
+                final boolean inBounds2 = i < w2 && j < h2;
+                int color1 = Color.WHITE.getRGB();
+                int color2 = Color.WHITE.getRGB();
+                int color3;
+                if (inBounds1 && inBounds2) {
+                    color1 = image1.getRGB(i, j);
+                    color2 = image2.getRGB(i, j);
+                    color3 = color1 == color2 ? color1 : Color.RED.getRGB();
+                } else if (inBounds1 && !inBounds2) {
+                    color1 = image1.getRGB(i, j);
+                    color3 = Color.BLUE.getRGB();
+                } else if (!inBounds1 && inBounds2) {
+                    color2 = image2.getRGB(i, j);
+                    color3 = Color.GREEN.getRGB();
+                } else {
+                    color3 = Color.MAGENTA.getRGB();
+                }
+                int x = i;
+                diff.setRGB(x, j, color1);
+                x += width;
+                diff.setRGB(x, j, color2);
+                x += width;
+                diff.setRGB(x, j, color3);
+            }
+        }
+        ImageIO.write(diff, "png", out);
+    }
+
+}
diff --git a/hostsidetests/holo/src/android/holo/cts/HoloHostTest.java b/hostsidetests/holo/src/android/holo/cts/HoloHostTest.java
new file mode 100644
index 0000000..5de3030
--- /dev/null
+++ b/hostsidetests/holo/src/android/holo/cts/HoloHostTest.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2014 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.holo.cts;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmlib.IShellOutputReceiver;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Scanner;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Test to check the Holo theme has not been changed.
+ */
+public class HoloHostTest extends DeviceTestCase implements IBuildReceiver {
+
+    private static final String TAG = HoloHostTest.class.getSimpleName();
+
+    private static final int CAPTURE_TIMEOUT = 1 * 1000;//1sec in ms
+
+    private static final int ADB_TIMEOUT = 10 * 60 * 1000;//10mins in ms
+
+    /** The package name of the APK. */
+    private static final String PACKAGE = "android.holo.app";
+
+    /** The file name of the APK. */
+    private static final String APK = "CtsHoloDeviceApp.apk";
+
+    /** The class name of the main activity in the APK. */
+    private static final String CLASS = "HoloDeviceActivity";
+
+    /** The command to launch the main activity. */
+    private static final String START_CMD = String.format(
+            "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
+
+    private static final String STOP_CMD = String.format("am force-stop %s", PACKAGE);
+
+    private static final String DENSITY_PROP = "ro.sf.lcd_density";
+
+    // Intent extras
+    protected final static String INTENT_STRING_EXTRA = " --es %s %s";
+
+    protected final static String INTENT_BOOLEAN_EXTRA = " --ez %s %b";
+
+    protected final static String INTENT_INTEGER_EXTRA = " --ei %s %d";
+
+    // Intent extra keys
+    private static final String EXTRA_THEME = "holo_theme_extra";
+
+    private static final String EXTRA_LAYOUT = "holo_layout_extra";
+
+    private static final String EXTRA_TIMEOUT = "holo_timeout_extra";
+
+    private static final String[] THEMES = {
+            "holo",
+            "holo_dialog",
+            "holo_dialog_minwidth",
+            "holo_dialog_noactionbar",
+            "holo_dialog_noactionbar_minwidth",
+            "holo_dialogwhenlarge",
+            "holo_dialogwhenlarge_noactionbar",
+            "holo_inputmethod",
+            "holo_light",
+            "holo_light_darkactionbar",
+            "holo_light_dialog",
+            "holo_light_dialog_minwidth",
+            "holo_light_dialog_noactionbar",
+            "holo_light_dialog_noactionbar_minwidth",
+            "holo_light_dialogwhenlarge",
+            "holo_light_dialogwhenlarge_noactionbar",
+            "holo_light_noactionbar",
+            "holo_light_noactionbar_fullscreen",
+            "holo_light_panel",
+            "holo_noactionbar",
+            "holo_noactionbar_fullscreen",
+            "holo_panel",
+            "holo_wallpaper",
+            "holo_wallpaper_notitlebar"
+    };
+
+    private final int NUM_THEMES = THEMES.length;
+
+    private static final String[] LAYOUTS = {
+            "button",
+            "button_pressed",
+            "checkbox",
+            "checkbox_checked",
+            "chronometer"
+    };
+
+    private final int NUM_LAYOUTS = LAYOUTS.length;
+
+    private final HashMap<String, File> mReferences = new HashMap<String, File>();
+
+    /** A reference to the build. */
+    private CtsBuildHelper mBuild;
+
+    /** A reference to the device under test. */
+    private ITestDevice mDevice;
+
+    private ExecutorService mExecutionService;
+
+    private ExecutorCompletionService<Boolean> mCompletionService;
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        // Get the build, this is used to access the APK.
+        mBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // Get the device, this gives a handle to run commands and install APKs.
+        mDevice = getDevice();
+        // Remove any previously installed versions of this APK.
+        mDevice.uninstallPackage(PACKAGE);
+        // Get the APK from the build.
+        File app = mBuild.getTestApp(APK);
+        // Install the APK on the device.
+        mDevice.installPackage(app, false);
+
+        final String zip = String.format("/%s.zip",
+                getDensityBucket(Integer.parseInt(mDevice.getProperty(DENSITY_PROP))));
+        Log.logAndDisplay(LogLevel.INFO, TAG, "Loading resources from " + zip);
+
+        final ZipInputStream in = new ZipInputStream(this.getClass().getResourceAsStream(zip));
+        try {
+            ZipEntry ze;
+            final byte[] buffer = new byte[1024];
+            while ((ze = in.getNextEntry()) != null) {
+                final String name = ze.getName();
+                final File tmp = File.createTempFile("ref_" + name, ".png");
+                final FileOutputStream out = new FileOutputStream(tmp);
+                int count;
+                while ((count = in.read(buffer)) != -1) {
+                    out.write(buffer, 0, count);
+                }
+                out.flush();
+                out.close();
+                mReferences.put(name, tmp);
+            }
+        } finally {
+            in.close();
+        }
+
+        mExecutionService = Executors.newFixedThreadPool(2);// 2 worker threads
+        mCompletionService = new ExecutorCompletionService<Boolean>(mExecutionService);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Delete the temp files
+        for (File ref : mReferences.values()) {
+            ref.delete();
+        }
+        mExecutionService.shutdown();
+        super.tearDown();
+    }
+
+    public void testHoloThemes() throws Exception {
+        int numTasks = 0;
+        for (int i = 0; i < NUM_THEMES; i++) {
+            final String themeName = THEMES[i];
+            for (int j = 0; j < NUM_LAYOUTS; j++) {
+                final String name = String.format("%s_%s", themeName, LAYOUTS[j]);
+                if (runCapture(i, j)) {
+                    final File ref = mReferences.get(name + ".png");
+                    mCompletionService.submit(new ComparisonTask(mDevice, ref, name));
+                    numTasks++;
+                } else {
+                    Log.logAndDisplay(LogLevel.ERROR, TAG, "Capture failed: " + name);
+                }
+            }
+        }
+        boolean success = true;
+        for (int i = 0; i < numTasks; i++) {
+            success = mCompletionService.take().get() && success;
+        }
+        assertTrue("Failures in Holo test", success);
+    }
+
+    private boolean runCapture(int themeId, int layoutId) throws Exception {
+        final StringBuilder sb = new StringBuilder(START_CMD);
+        sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_THEME, themeId));
+        sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_LAYOUT, layoutId));
+        sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_TIMEOUT, CAPTURE_TIMEOUT));
+        final String startCommand = sb.toString();
+        // Clear logcat
+        mDevice.executeAdbCommand("logcat", "-c");
+        // Stop any existing instances
+        mDevice.executeShellCommand(STOP_CMD);
+        // Start activity
+        mDevice.executeShellCommand(startCommand);
+
+        boolean success = false;
+        boolean waiting = true;
+        while (waiting) {
+            // Dump logcat.
+            final String logs = mDevice.executeAdbCommand("logcat", "-d", CLASS + ":I", "*:S");
+            // Search for string.
+            final Scanner in = new Scanner(logs);
+            while (in.hasNextLine()) {
+                final String line = in.nextLine();
+                if (line.startsWith("I/" + CLASS)) {
+                    final String s = line.split(":")[1].trim();
+                    if (s.equals("OKAY")) {
+                        success = true;
+                        waiting = false;
+                    } else if (s.equals("ERROR")) {
+                        success = false;
+                        waiting = false;
+                    }
+                }
+            }
+        }
+
+        return success;
+    }
+
+    private static String getDensityBucket(int density) {
+        switch (density) {
+            case 120:
+                return "ldpi";
+            case 160:
+                return "mdpi";
+            case 213:
+                return "tvdpi";
+            case 240:
+                return "hdpi";
+            case 320:
+                return "xhdpi";
+            case 400:
+                return "400dpi";
+            case 480:
+                return "xxhdpi";
+            case 640:
+                return "xxxhdpi";
+            default:
+                return "" + density;
+        }
+    }
+}