Merge remote branch 'korg/froyo' into manualmerge
diff --git a/.gitignore b/.gitignore
index 542ab17..ca65e7d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 *.pyc
 *.~*
+bin
diff --git a/CtsHostLibraryList.mk b/CtsHostLibraryList.mk
index 36291d5..ac9a700 100644
--- a/CtsHostLibraryList.mk
+++ b/CtsHostLibraryList.mk
@@ -12,5 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-CTS_HOST_LIBRARY_JARS := \
-	$(HOST_OUT_JAVA_LIBRARIES)/CtsTestAnnotationsHostLib.jar
+CTS_HOST_LIBRARY_JARS := 
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index b97bea7..0537d15 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -31,6 +31,7 @@
 CTS_COVERAGE_TEST_CASE_LIST := \
 	CtsAccessibilityServiceTestCases \
 	CtsAccountManagerTestCases \
+	CtsAdminTestCases \
 	CtsAppTestCases \
 	CtsBluetoothTestCases \
 	CtsContentTestCases \
@@ -44,6 +45,7 @@
 	CtsJniTestCases \
 	CtsLocationTestCases \
 	CtsMediaTestCases \
+	CtsNdefTestCases \
 	CtsOsTestCases \
 	CtsPermissionTestCases \
 	CtsPermission2TestCases \
@@ -69,6 +71,7 @@
 CTS_TEST_CASE_LIST := \
 	TestDeviceSetup \
 	CtsDelegatingAccessibilityService \
+	CtsDeviceAdmin \
 	SignatureTest \
 	ApiDemos \
 	ApiDemosReferenceTest \
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index dab331a..bbf7eaf 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PACKAGE_NAME := CtsVerifier
 
-LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni
+LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni libaudioquality
 
 LOCAL_SDK_VERSION := current
 
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 5577f38..68c3682 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -17,12 +17,20 @@
 
 <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="2.3_r5">
       
     <uses-sdk android:minSdkVersion="5"></uses-sdk>
 
-    <application android:label="@string/app_name">
+    <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" />
+    
+    <!-- Needed by the Audio Quality Verifier to store the sound samples that will be mailed. -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application android:label="@string/app_name" android:icon="@drawable/icon" android:debuggable="true">
 
         <activity android:name=".CtsVerifierActivity" android:label="@string/app_name">
             <intent-filter>
@@ -32,9 +40,107 @@
         </activity>
 
         <activity android:name=".TestListActivity" android:label="@string/test_list_title" />
-        
+
         <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">
+            <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_networking" />
+        </activity>
+        
+        <activity android:name=".bluetooth.BluetoothToggleActivity"
+                android:label="@string/bt_toggle_bluetooth"
+                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=".suid.SuidFilesActivity" 
                 android:label="@string/suid_files"
@@ -55,7 +161,7 @@
         </activity>
 
         <activity android:name=".sensors.AccelerometerTestActivity" android:label="@string/snsr_accel_test"
-        android:screenOrientation="nosensor">
+                android:screenOrientation="nosensor">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.cts.intent.category.MANUAL_TEST" />
@@ -63,13 +169,32 @@
             <meta-data android:name="test_category" android:value="@string/test_category_sensors" />
         </activity>
 
-        <activity android:name=".sensors.MagnetometerTestActivity" android:label="@string/snsr_mag_test"
-        android:screenOrientation="nosensor">
+        <activity android:name=".sensors.GyroscopeTestActivity" android:label="@string/snsr_gyro_test"
+                android:screenOrientation="nosensor">
             <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_sensors" />
         </activity>
-    </application>
+
+        <activity android:name=".audioquality.AudioQualityVerifierActivity"
+                android:label="@string/aq_verifier">
+            <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_audio" />
+        </activity>
+
+        <activity android:name=".audioquality.CalibrateVolumeActivity"
+                  android:label="@string/aq_calibrate_volume_name" />
+
+        <activity android:name=".audioquality.ViewResultsActivity"
+                  android:label="@string/aq_view_results_name" />
+
+        <service android:name=".audioquality.ExperimentService" />
+
+   </application>
+
 </manifest> 
diff --git a/apps/CtsVerifier/arduino-helper/Makefile b/apps/CtsVerifier/arduino-helper/Makefile
deleted file mode 100644
index 4159ee6..0000000
--- a/apps/CtsVerifier/arduino-helper/Makefile
+++ /dev/null
@@ -1,274 +0,0 @@
-# Copyright 2010 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
-
-# NOTE: this Makefile is derived from an example included in Arduino 0017.
-# That Makefile bore no license, but the rest of the Arduino is generally LGPL
-# v2 with upgrade option. Since it was unclear what the license for the
-# Makefile itself was intended to be, this version uses ASL2.0, as LGPL is
-# an unusual license choice for a Makefile. ASL2.0 is compatible with LGPL v3
-# so there should be no conflict (due to the upgrade option.)
-
-# Theory of Operation
-# 'Arduino' refers to a hardware platform, and a software IDE. The IDE is
-# pretty simple, and boils down to a crude text editor and some build
-# plumbing. An Arduino 'sketch' is the core of a C (or C++) program; the build
-# plumbing wraps a bunch of boilerplate (like #includes and entry point stubs)
-# around the user's core code, and handles running the gcc cross-compiler and
-# linking in the appropriate goodies to target AVR. It also handles
-# programming the Atmel part on the board, via USB.
-# 
-# The function of this Makefile is simply to replicate all the boilerplate
-# management. An Arduino 'sketch' is stored as a .pde file, but is essentially
-# C code; so this Makefile cats together a full C/C++ file, compiles the
-# standard Arduino libraries, and links them all together, using the AVR
-# cross-compiler toolchain for gcc. (Ubuntu provides a pre-built avr-gcc
-# package.)
-
-# Path settings
-TARGET = $(notdir $(CURDIR))
-ARDUINO_PATH=/usr/share/arduino
-ARDUINO_VERSION=18
-ARDUINO_CORES_PATH = $(ARDUINO_PATH)/hardware/arduino/cores/arduino
-AVR_TOOLS_PATH = /usr/bin
-AVRDUDE_PATH = /usr/bin
-
-# Programming port information
-PORT = /dev/ttyUSB0
-UPLOAD_RATE = 57600
-
-# Target information (settings below work for Duemilanove)
-AVRDUDE_PROGRAMMER = stk500v1
-MCU = atmega328p
-F_CPU = 16000000
-OUT_DIR = out
-
-# C-language libraries to compile and link with all programs
-ARDUINO_C_MODULES_SRC =  \
-$(ARDUINO_CORES_PATH)/wiring_pulse.c \
-$(ARDUINO_CORES_PATH)/wiring_analog.c \
-$(ARDUINO_CORES_PATH)/pins_arduino.c \
-$(ARDUINO_CORES_PATH)/wiring.c \
-$(ARDUINO_CORES_PATH)/wiring_digital.c \
-$(ARDUINO_CORES_PATH)/WInterrupts.c \
-$(ARDUINO_CORES_PATH)/wiring_shift.c
-ARDUINO_C_MODULES_OBJ = \
-$(OUT_DIR)/wiring_pulse.o \
-$(OUT_DIR)/wiring_analog.o \
-$(OUT_DIR)/pins_arduino.o \
-$(OUT_DIR)/wiring.o \
-$(OUT_DIR)/wiring_digital.o \
-$(OUT_DIR)/WInterrupts.o \
-$(OUT_DIR)/wiring_shift.o
-
-# C++-language libraries to compile and link with all programs
-ARDUINO_CPP_MODULES_SRC = \
-$(ARDUINO_CORES_PATH)/Tone.cpp \
-$(ARDUINO_CORES_PATH)/main.cpp \
-$(ARDUINO_CORES_PATH)/WMath.cpp \
-$(ARDUINO_CORES_PATH)/Print.cpp \
-$(ARDUINO_CORES_PATH)/HardwareSerial.cpp
-ARDUINO_CPP_MODULES_OBJ = \
-$(OUT_DIR)/Tone.o \
-$(OUT_DIR)/main.o \
-$(OUT_DIR)/WMath.o \
-$(OUT_DIR)/Print.o \
-$(OUT_DIR)/HardwareSerial.o
-
-TARGET_CPP_SRC = $(OUT_DIR)/$(TARGET).cpp
-FORMAT = ihex
-
-# Name of this Makefile (used for "make depend").
-MAKEFILE = Makefile
-
-# Debugging format.
-# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2.
-# AVR (extended) COFF requires stabs, plus an avr-objcopy run.
-DEBUG =
-OPT = s
-
-# Place -D or -U options here
-CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION)
-CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION)
-
-# Place -I options here
-CINCS = -I$(ARDUINO_CORES_PATH)
-CXXINCS = -I$(ARDUINO_CORES_PATH)
-
-# toolchain flags
-CDEBUG = -g$(DEBUG)
-CWARN = -w      # suppress all warnings
-CTUNING = -ffunction-sections -fdata-sections
-CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections
-CFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CTUNING) $(CDEFS) $(CINCS) $(CSTANDARD)
-CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS)
-#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs
-LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections
-
-# Programming support using avrdude. Settings and variables.
-AVRDUDE_WRITE_FLASH = -U flash:w:$(OUT_DIR)/$(TARGET).hex
-#AVRDUDE_FLAGS = -V -F -C $(ARDUINO_PATH)/hardware/tools/avrdude.conf \
-
-AVRDUDE_FLAGS = -V -F -p $(MCU) -P $(PORT) -c $(AVRDUDE_PROGRAMMER) -b $(UPLOAD_RATE)
-
-# AVR cross-compiler toolchain binaries
-CC = $(AVR_TOOLS_PATH)/avr-gcc
-CXX = $(AVR_TOOLS_PATH)/avr-g++
-LD = $(AVR_TOOLS_PATH)/avr-gcc
-OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy
-OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump
-AR  = $(AVR_TOOLS_PATH)/avr-ar
-SIZE = $(AVR_TOOLS_PATH)/avr-size
-NM = $(AVR_TOOLS_PATH)/avr-nm
-AVRDUDE = $(AVRDUDE_PATH)/avrdude
- 
-# General programs
-REMOVE = rm -f
-RMDIR = rmdir
-MV = mv -f
-
-# Combine all necessary flags and optional flags.
-# Add target processor to flags.
-ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU)
-ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU)
-ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU)
-ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU)
-
-# Default target.
-all: $(OUT_DIR)_files build sizeafter
-build: elf hex
-
-# Concatenates the 'sketch' .pde file with the usual Arduino boilerplate
-$(OUT_DIR)/$(TARGET).cpp: $(TARGET).pde
-	@echo "Generate $(TARGET).cpp from $(TARGET).pde"
-	@test -d $(OUT_DIR) || mkdir $(OUT_DIR)
-	@echo '#include "WProgram.h"' > $(OUT_DIR)/$(TARGET).cpp
-	@echo 'void setup();' >> $(OUT_DIR)/$(TARGET).cpp
-	@echo 'void loop();' >> $(OUT_DIR)/$(TARGET).cpp
-	@cat $(TARGET).pde >> $(OUT_DIR)/$(TARGET).cpp
-
-elf: $(OUT_DIR)/$(TARGET).elf
-hex: $(OUT_DIR)/$(TARGET).hex
-eep: $(OUT_DIR)/$(TARGET).eep
-lss: $(OUT_DIR)/$(TARGET).lss
-sym: $(OUT_DIR)/$(TARGET).sym
-
-# Program the device.  
-upload: $(OUT_DIR)/$(TARGET).hex
-	# pulsedtr strobes the DTR line to tell arduino to enter pgming mode
-	python ./pulsedtr.py $(PORT)
-	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)
-
-
-# Display size of file.
-HEXSIZE = $(SIZE) --target=$(FORMAT) $(OUT_DIR)/$(TARGET).hex
-ELFSIZE = $(SIZE)  $(OUT_DIR)/$(TARGET).elf
-sizebefore:
-	@if [ -f $(OUT_DIR)/$(TARGET).elf ]; then echo $(MSG_SIZE_BEFORE); $(HEXSIZE); fi
-
-sizeafter:
-	@if [ -f $(OUT_DIR)/$(TARGET).elf ]; then echo $(MSG_SIZE_AFTER); $(HEXSIZE); fi
-
-
-# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
-COFFCONVERT=$(OBJCOPY) --debugging \
---change-section-address .data-0x800000 \
---change-section-address .bss-0x800000 \
---change-section-address .noinit-0x800000 \
---change-section-address .eeprom-0x810000
-
-
-coff: $(OUT_DIR)/$(TARGET).elf
-	$(COFFCONVERT) -O coff-avr $(OUT_DIR)/$(TARGET).elf $(TARGET).cof
-
-
-extcoff: $(TARGET).elf
-	$(COFFCONVERT) -O coff-ext-avr $(OUT_DIR)/$(TARGET).elf $(TARGET).cof
-
-
-.SUFFIXES: .elf .hex .eep .lss .sym
-
-.elf.hex:
-	$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
-
-.elf.eep:
-	$(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
-	--no-change-warnings \
-	--change-section-lma .eeprom=0 $< $@
-
-# Create extended listing file from ELF output file.
-.elf.lss:
-	$(OBJDUMP) -h -S $< > $@
-
-# Create a symbol table from ELF output file.
-.elf.sym:
-	$(NM) -n $< > $@
-
-	# Link: create ELF output file from library.
-$(OUT_DIR)/$(TARGET).elf: $(OUT_DIR)/$(TARGET).o $(OUT_DIR)/core.a
-	$(LD) $(ALL_LDFLAGS) -o $@ $(OUT_DIR)/$(TARGET).o $(OUT_DIR)/core.a
-
-$(OUT_DIR)/core.a: $(ARDUINO_C_MODULES_OBJ) $(ARDUINO_CPP_MODULES_OBJ)
-	@for i in $(ARDUINO_C_MODULES_OBJ) $(ARDUINO_CPP_MODULES_OBJ); do echo $(AR) rcs $(OUT_DIR)/core.a $$i; $(AR) rcs $(OUT_DIR)/core.a $$i; done
-
-$(ARDUINO_C_MODULES_OBJ):
-	$(CC) -c $(ALL_CFLAGS) $(ARDUINO_CORES_PATH)/$(patsubst %.o,%.c,$(patsubst $(OUT_DIR)/%,%,$@)) -o $@
-
-$(ARDUINO_CPP_MODULES_OBJ):
-	$(CXX) -c $(ALL_CXXFLAGS) $(ARDUINO_CORES_PATH)/$(patsubst %.o,%.cpp,$(patsubst $(OUT_DIR)/%,%,$@)) -o $@
-
-# Compile: create object files from C++ source files.
-.cpp.o:
-	$(CXX) -c $(ALL_CXXFLAGS) $< -o $@
-
-# Compile: create object files from C source files.
-.c.o:
-	$(CC) -c $(ALL_CFLAGS) $< -o $@
-
-
-# Compile: create assembler files from C source files.
-.c.s:
-	$(CC) -S $(ALL_CFLAGS) $< -o $@
-
-
-# Assemble: create object files from assembler source files.
-.S.o:
-	$(CC) -c $(ALL_ASFLAGS) $< -o $@
-
-
-# Automatic dependencies
-%.d: %.c
-	$(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@
-
-%.d: %.cpp
-	$(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@
-
-
-# Target: clean project.
-clean:
-	$(REMOVE) $(OUT_DIR)/$(TARGET).hex $(OUT_DIR)/$(TARGET).eep \
-        $(OUT_DIR)/$(TARGET).cof $(OUT_DIR)/$(TARGET).elf \
-        $(OUT_DIR)/$(TARGET).map $(OUT_DIR)/$(TARGET).sym \
-        $(OUT_DIR)/$(TARGET).lss $(OUT_DIR)/core.a \
-	$(ARDUINO_C_MODULES_SRC:.c=.s) $(ARDUINO_C_MODULES_SRC:.c=.d) \
-        $(ARDUINO_CPP_MODULES_SRC:.cpp=.s) $(ARDUINO_CPP_MODULES_SRC:.cpp=.d) \
-        $(ARDUINO_C_MODULES_OBJ) $(ARDUINO_CPP_MODULES_OBJ) \
-        $(OUT_DIR)/$(TARGET).cpp $(OUT_DIR)/$(TARGET).o
-distclean: clean
-	$(RMDIR) $(OUT_DIR)
-
-.PHONY: all build elf hex eep lss sym program coff extcoff clean $(OUT_DIR)_files sizebefore sizeafter
-
-#include $(ARDUINO_C_MODULES_SRC:.c=.d)
-#include $(ARDUINO_CPP_MODULES_SRC:.cpp=.d) 
-#include $(TARGET_CPP_SRC:.cpp=.d) 
diff --git a/apps/CtsVerifier/arduino-helper/README b/apps/CtsVerifier/arduino-helper/README
deleted file mode 100644
index 8ad1037..0000000
--- a/apps/CtsVerifier/arduino-helper/README
+++ /dev/null
@@ -1,53 +0,0 @@
-The code in this directory is intended to be run on an Arduino, which is a
-simple, open-source microcontroller platform:
-    http://arduino.org/
-
-Essentially, an Arduino is a board for an ATMega microcontroller, and a GUI
-IDE project intended for semi-technical developers. The Arduino software
-essentially boils down to an IDE which is a text editor & build automater for a
-C/C++ library that hides most of the low-level details of configuring a
-program to run on the microcontroller. It uses the AVR toolchain and
-programmer to work with the device from a host PC. This project includes a
-Makefile which replicates work the normally performed by the IDE; that is, it
-builds and links the relevant Arduino boilerplate code that sets up the
-microcontroller, and then links together a final .hex file and prepares it for
-upload to the device.
-
-A BlueSMiRF Bluetooth SPP-to-TTL (or similar) board is also required; this
-device is used to validate Bluetooth API support via the CTS Verifier app.
-    http://www.sparkfun.com/commerce/product_info.php?products_id=582
-
-This code running on a simple, inexpensive, known-good microcontroller
-platform makes it much easier to validate the Bluetooth API for an Android
-candidate device than the "Bluetooth Chat" approach currently required by the
-CDD.
-
-A compatible device can be substituted for some or all of the Arduino
-configuration; however it's worth noting that for approximately US$110, a
-complete test rig can be completed and used with any number of devices under
-test. Organizations are, of course, also free to manufacture their own
-Arduino-like devices.
-
-This code (and especially the Makefile) requires version 0018 of the Arduino
-toolchain. Because this software uses a non-Android-standard build, the
-Android source tree includes in the prebuilt/ directory a .hex file compiled
-from this source suitable for directly flashing to an Arduino
-Duemilanove-compatible device.
-
-The file pulsedtr.py is a Python script using the Serial I/O API that strobes
-the DTR line on the serial port. This is a signal to the Arduino to enter
-programming mode; this script is thus used in flashing the Arduino board (via
-a direct USB connection) with the prebuilt .hex file. If you are building
-from source, 'make upload' will handle this for you.
-
-To build the .hex file on Ubuntu Lucid:
-$ sudo add-apt-repository ppa:arduino-ubuntu-team
-$ sudo apt-get update; sudo apt-get install arduino
-$ make
-$ make upload
-
-Alternatively, the file 'cts-verifier.pde' can be loaded into the Arduino IDE
-as a sketch using the normal GUI workflow.
-
-This Makefile should hypothetically work on a Mac with the appropriate Arduino
-toolchain installed, but it has not been tested. Windows is not supported.
diff --git a/apps/CtsVerifier/arduino-helper/arduino-helper.pde b/apps/CtsVerifier/arduino-helper/arduino-helper.pde
deleted file mode 100644
index 0c41388..0000000
--- a/apps/CtsVerifier/arduino-helper/arduino-helper.pde
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright 2010 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
- */
-
-/*
- * Define the basic structure for messages from the host.
- * Messages are MESSAGE_SIZE bytes, with a 2-byte opcode, a 2-byte
- * unique ID defined by the sender, and the remainder payload.
- * The remaining 2 bytes must be 0xFEEDFACE. This is used by
- * the message handler as a tail sentinel to resync with the
- * sender in case data is lost and the fixed-byte messages
- * get out of sync.
- */
-#define MESSAGE_SIZE 128
-#define MESSAGE_DELIMITER 0xBEEF
-#define MESSAGE_ESCAPE 0x2a
-struct message {
-  uint16_t opcode;
-  uint16_t id;
-  uint8_t data[MESSAGE_SIZE - 6];
-  uint16_t tail;
-};
-struct message CURRENT_MESSAGE;
-
-#define OPCODE_RESET 0x00
-struct reset {
-  uint16_t opcode;
-  uint16_t id;
-  uint8_t unused[MESSAGE_SIZE - 4];
-};
-
-#define OPCODE_INIT_TIME (OPCODE_RESET + 1)
-struct init_time {
-  uint16_t opcode;
-  uint16_t id;
-  uint32_t cur_raw_time;
-  uint8_t unused[MESSAGE_SIZE - 6];
-};
-
-#define OPCODE_CURRENT_TIME (OPCODE_RESET + 2)
-struct current_time { // we never actually use this, but here for consistency
-  uint16_t opcode;
-  uint16_t id;
-  uint8_t unused[MESSAGE_SIZE - 4];
-};
-
-#define OPCODE_SETMODE_PONG (OPCODE_RESET + 3)
-struct setmode_pong {
-  uint16_t opcode;
-  uint16_t id;
-  uint16_t playfield_width;
-  uint16_t playfield_height;
-  uint16_t paddle_width;
-  uint16_t paddle_offset;
-  uint16_t max_paddle_motion;
-  uint8_t unused[MESSAGE_SIZE - 14];
-};
-
-#define OPCODE_PONG_BALL_STATE (OPCODE_RESET + 4)
-struct pong_ball_state {
-  uint16_t opcode;
-  uint16_t id;
-  uint16_t ball_x;
-  uint16_t ball_y;
-  uint8_t unused[MESSAGE_SIZE - 8];
-};
-
-struct wall_time_struct {
-  uint32_t raw; // long == 4-byte on AVR
-  uint8_t initialized;
-  uint8_t hours;
-  uint8_t minutes;
-  uint8_t seconds;
-};
-struct wall_time_struct WALL_TIME;
-
-struct pong_state_struct {
-  uint16_t playfield_width;
-  uint16_t playfield_height;
-  uint16_t paddle_width;
-  uint16_t paddle_offset;
-  uint16_t max_paddle_motion;
-  uint16_t paddle_x;
-  uint16_t last_ball_x;
-  uint16_t last_ball_y;
-};
-struct pong_state_struct PONG_STATE;
-
-
-void print_current_time() {
-  if (WALL_TIME.initialized) {
-    Serial.print("current_time=");
-    Serial.print(WALL_TIME.hours, DEC);
-    Serial.print(":");
-    if (WALL_TIME.minutes < 10)
-      Serial.print("0");
-    Serial.print(WALL_TIME.minutes, DEC);
-    Serial.print(":");
-    if (WALL_TIME.seconds < 10)
-      Serial.print("0");
-    Serial.println(WALL_TIME.seconds, DEC);
-  } else {
-    Serial.println("current_time=00:00:00");
-  }
-}
-
-
-void handle_current_message() {
-  static uint16_t last_id;
-  static struct setmode_pong* setmode_pong_msg;
-  static struct pong_ball_state* pong_ball_state_msg;
-  static uint16_t paddle_half_width;
-  static uint16_t paddle_max;
-  static uint16_t danger;
-  static uint8_t invert;
-  static uint16_t delta;
-
-  if (CURRENT_MESSAGE.id == 0 || CURRENT_MESSAGE.id == last_id) {
-    return;
-  }
-  last_id = CURRENT_MESSAGE.id;
-
-  switch (CURRENT_MESSAGE.opcode) {
-
-    case OPCODE_SETMODE_PONG:
-      memset(&PONG_STATE, 0, sizeof(PONG_STATE));
-      setmode_pong_msg = (struct setmode_pong*)(&CURRENT_MESSAGE);
-      PONG_STATE.playfield_width = setmode_pong_msg->playfield_width;
-      PONG_STATE.playfield_height = setmode_pong_msg->playfield_height;
-      PONG_STATE.paddle_width = setmode_pong_msg->paddle_width;
-      PONG_STATE.paddle_offset = setmode_pong_msg->paddle_offset;
-      PONG_STATE.max_paddle_motion = setmode_pong_msg->max_paddle_motion;
-
-      paddle_half_width = PONG_STATE.paddle_width / 2;
-      paddle_max = PONG_STATE.playfield_width - paddle_half_width;
-
-      Serial.println("message_type=setmode_pong_ack");
-      Serial.print("id=");
-      Serial.println(CURRENT_MESSAGE.id);
-      print_current_time();
-      Serial.println("");
-      break;
-
-    case OPCODE_PONG_BALL_STATE:
-      pong_ball_state_msg = (struct pong_ball_state*)(&CURRENT_MESSAGE);
-      danger = pong_ball_state_msg->ball_x - PONG_STATE.paddle_x;
-      invert = (danger < 0);
-      danger *= invert ? -1 : 1;
-      if (danger < paddle_half_width) {
-        delta = 0;
-      } else if (danger < PONG_STATE.playfield_width / 3) {
-        delta = PONG_STATE.max_paddle_motion / 3;
-      } else if (danger < PONG_STATE.playfield_width * 2 / 3) {
-        delta = PONG_STATE.max_paddle_motion * 2 / 3;
-      } else {
-        delta = PONG_STATE.max_paddle_motion;
-      }
-      delta *= invert ? 1 : -1;
-      PONG_STATE.paddle_x += delta;
-      if (PONG_STATE.paddle_x < paddle_half_width) {
-        PONG_STATE.paddle_x = paddle_half_width;
-      } else if (PONG_STATE.paddle_x > paddle_max) {
-        PONG_STATE.paddle_x = paddle_max;
-      }
-
-      Serial.println("message_type=pong_paddle_state");
-      Serial.print("id=");
-      Serial.println(CURRENT_MESSAGE.id);
-      print_current_time();
-      Serial.print("paddle_x=");
-      Serial.println(PONG_STATE.paddle_x);
-      Serial.println("");
-      break;
-
-    default:
-      break;
-  }
-}
-
-/* This is a temporary buffer used by the message handler */
-struct message_buffer {
-  uint16_t count; // number of bytes read into the buffer
-  uint8_t buffer[MESSAGE_SIZE]; // contents of a 'struct message'
-};
-struct message_buffer MESSAGE_BUFFER;
-
-/*
- * Clears all stateful values, including the wall clock time, current message
- * data, and user/app state. Also clears the message handler's buffer. By
- * "clear" we mean "memset to 0".
- */
-void reset() {
-  memset(&WALL_TIME, 0, sizeof(WALL_TIME));
-  memset(&CURRENT_MESSAGE, 0, sizeof(CURRENT_MESSAGE));
-  memset(&MESSAGE_BUFFER, 0, sizeof(MESSAGE_BUFFER));
-  memset(&PONG_STATE, 0, sizeof(PONG_STATE));
-}
-
-
-/*
- * Pumps the message processor. That is, this function is intended to be
- * called once per loop to read all pending Serial/TTL data, and decode a
- * message from the peer if one is complete. In cases where data is corrupted
- * (such as by dropping bytes), this function also attempts to re-sync with
- * the host by discarding messages until it finds a MESSAGE_DELIMITER, after
- * which is resyncs its buffer on the first subsequent byte.
- *
- * This functional also handles two low-level 'system' messages: a reset
- * instruction which invokes reset(), and an init_time instruction which
- * provides the soft clock with the current time so that it can start keeping
- * time.
- */
-void pump_message_processor() {
-  static uint8_t cur_byte;
-  static uint16_t* cur_word;
-  static int8_t delimiter_index;
-  static char buf[4];
-  while (Serial.available() > 0) { // keep going as long as we might have messages
-    cur_byte = (uint8_t)(Serial.read() & 0x000000ff);
-    MESSAGE_BUFFER.buffer[(MESSAGE_BUFFER.count)++] = cur_byte;
-    Serial.print("booga ");
-    Serial.print(itoa(MESSAGE_BUFFER.count, buf, 10));
-    Serial.print(" ");
-    Serial.print(itoa(Serial.available(), buf, 10));
-    Serial.print(" ");
-    Serial.println(itoa(cur_byte, buf, 10));
-    if (MESSAGE_BUFFER.count >= MESSAGE_SIZE) {
-      if ((*(uint16_t*)(MESSAGE_BUFFER.buffer + MESSAGE_SIZE - 2)) != MESSAGE_DELIMITER) {
-        // whoops, we got out of sync with the transmitter. Scan current
-        // buffer for the delimiter, discard previous message, and shift
-        // partial next message to front of buffer. This loses a message but
-        // gets us back in sync
-        delimiter_index = -2;
-        for (int i = MESSAGE_SIZE - 2; i >= 0; --i) {
-          if (*((uint16_t*)(MESSAGE_BUFFER.buffer + i)) == MESSAGE_DELIMITER) {
-            if (((i - 1) >= 0) && (MESSAGE_BUFFER.buffer[i - 1] != MESSAGE_ESCAPE)) {
-              delimiter_index = i;
-              break;
-            }
-          }
-        }
-        Serial.print("klaxon ");
-        Serial.println(itoa(delimiter_index, buf, 10));
-        Serial.print("klaxon ");
-        Serial.println(itoa(*((uint16_t*)(MESSAGE_BUFFER.buffer + MESSAGE_SIZE - 2)), buf, 10));
-        MESSAGE_BUFFER.count = 0;
-        if (delimiter_index >= 0) {
-          for (int i = delimiter_index + 2; i < MESSAGE_SIZE; ++i, ++(MESSAGE_BUFFER.count)) {
-            MESSAGE_BUFFER.buffer[MESSAGE_BUFFER.count] = MESSAGE_BUFFER.buffer[i];
-          }
-        }
-        memset(MESSAGE_BUFFER.buffer + MESSAGE_BUFFER.count, 0, MESSAGE_SIZE - MESSAGE_BUFFER.count);
-      } else {
-        memcpy(&CURRENT_MESSAGE, MESSAGE_BUFFER.buffer, MESSAGE_SIZE);
-        memset(&MESSAGE_BUFFER, 0, sizeof(MESSAGE_BUFFER));
-        switch (CURRENT_MESSAGE.opcode) {
-          case OPCODE_RESET:
-            reset();
-            return;
-
-          case OPCODE_INIT_TIME:
-            // cast CURRENT_MESSAGE to our time struct to conveniently fetch
-            // out the current time
-            WALL_TIME.raw = ((struct init_time*)(&CURRENT_MESSAGE))->cur_raw_time;
-            WALL_TIME.initialized = 1;
-
-            Serial.println("message_type=init_time_ack");
-            Serial.print("id=");
-            Serial.println(CURRENT_MESSAGE.id);
-            print_current_time();
-            Serial.println("");
-
-            CURRENT_MESSAGE.id = 0;
-            break;
-
-          case OPCODE_CURRENT_TIME:
-            Serial.println("message_type=current_time_ack");
-            Serial.print("id=");
-            Serial.println(CURRENT_MESSAGE.id);
-            print_current_time();
-            Serial.println("");
-
-            CURRENT_MESSAGE.id = 0;
-
-          default:
-            // no-op -- actually means main loop will handle it
-            break;
-        }
-      }
-    }
-  }
-}
-
-/*
- * Pumps the system wall clock. This checks the device's monotonic clock to
- * determine elapsed time since last invocation, and updates wall clock time
- * by dead reckoning. Since the device has no battery backup, a power-off will
- * lose the current time, so timekeeping cannot begin until an INIT_TIME
- * message is received. (The pump_message_processor() function handles that.)
- *
- * Once timekeeping is underway, current time is exposed to user/app code via
- * the WALL_TIME object, which has 24-hour HH/MM/SS fields.
- */
-void pump_clock() {
-  static uint32_t prev_millis = 0;
-  static uint32_t tmp_prev_millis = 0;
-  uint32_t tmp = 0;
-
-  if (millis() / 1000 != tmp_prev_millis) {
-    tmp_prev_millis = millis() / 1000;
-    print_current_time();
-  }
-
-  if (WALL_TIME.initialized) {
-    tmp = millis() / 1000;
-    if (tmp != prev_millis) {
-      prev_millis = tmp;
-      WALL_TIME.raw++;
-    }
-    WALL_TIME.seconds = WALL_TIME.raw % 60;
-    WALL_TIME.minutes = (WALL_TIME.raw / 60) % 60;
-    WALL_TIME.hours = (WALL_TIME.raw / (60 * 60)) % 24;
-  }
-}
-
-
-/*
- * Standard Arduino setup hook.
- */
-void setup() {
-  Serial.begin(115200);
-}
-
-
-/*
- * Standard Arduino loop-pump hook.
- */
-void loop() {
-  static uint16_t last_id = 0;
-
-  // pump the clock and message processor
-  pump_clock();
-  pump_message_processor();
-  
-  // ignore any "system" messages (those with ID == 0) but dispatch app messages
-  if ((last_id != CURRENT_MESSAGE.id) && (CURRENT_MESSAGE.id != 0)) {
-    handle_current_message();
-  }
-  last_id = CURRENT_MESSAGE.id;
-}
diff --git a/apps/CtsVerifier/arduino-helper/pulsedtr.py b/apps/CtsVerifier/arduino-helper/pulsedtr.py
deleted file mode 100644
index 887700f..0000000
--- a/apps/CtsVerifier/arduino-helper/pulsedtr.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright 2010 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
-
-import serial
-import time
-import sys
-
-if len(sys.argv) != 2:
-    print "Please specify a port, e.g. %s /dev/ttyUSB0" % sys.argv[0]
-    sys.exit(-1)
-
-ser = serial.Serial(sys.argv[1])
-ser.setDTR(1)
-time.sleep(0.25)
-ser.setDTR(0)
-ser.close()
diff --git a/apps/CtsVerifier/assets/audioquality/pink_10000_3s b/apps/CtsVerifier/assets/audioquality/pink_10000_3s
new file mode 100644
index 0000000..e248a50
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/pink_10000_3s
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/pink_5000_1s b/apps/CtsVerifier/assets/audioquality/pink_5000_1s
new file mode 100644
index 0000000..6d88505
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/pink_5000_1s
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/pink_5000_2s b/apps/CtsVerifier/assets/audioquality/pink_5000_2s
new file mode 100644
index 0000000..4acb0ca
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/pink_5000_2s
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_1 b/apps/CtsVerifier/assets/audioquality/stim_dt_1
new file mode 100644
index 0000000..65830bd
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_1
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_2 b/apps/CtsVerifier/assets/audioquality/stim_dt_2
new file mode 100644
index 0000000..c61e7c8
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_2
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_3 b/apps/CtsVerifier/assets/audioquality/stim_dt_3
new file mode 100644
index 0000000..be5f755
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_3
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_31 b/apps/CtsVerifier/assets/audioquality/stim_dt_31
new file mode 100644
index 0000000..8ff5b20
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_31
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_4 b/apps/CtsVerifier/assets/audioquality/stim_dt_4
new file mode 100644
index 0000000..a9c0497
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_4
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_5 b/apps/CtsVerifier/assets/audioquality/stim_dt_5
new file mode 100644
index 0000000..73c2777
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_5
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_6 b/apps/CtsVerifier/assets/audioquality/stim_dt_6
new file mode 100644
index 0000000..381c79a
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_6
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_66 b/apps/CtsVerifier/assets/audioquality/stim_dt_66
new file mode 100644
index 0000000..84964ec
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_66
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_7 b/apps/CtsVerifier/assets/audioquality/stim_dt_7
new file mode 100644
index 0000000..c304764
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_7
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_8 b/apps/CtsVerifier/assets/audioquality/stim_dt_8
new file mode 100644
index 0000000..7479f34
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_8
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/stim_dt_9 b/apps/CtsVerifier/assets/audioquality/stim_dt_9
new file mode 100644
index 0000000..02f5276
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/stim_dt_9
Binary files differ
diff --git a/apps/CtsVerifier/assets/audioquality/tone_250 b/apps/CtsVerifier/assets/audioquality/tone_250
new file mode 100644
index 0000000..ed411f5
--- /dev/null
+++ b/apps/CtsVerifier/assets/audioquality/tone_250
Binary files differ
diff --git a/apps/CtsVerifier/jni/Android.mk b/apps/CtsVerifier/jni/Android.mk
index 98a4678..4343259 100644
--- a/apps/CtsVerifier/jni/Android.mk
+++ b/apps/CtsVerifier/jni/Android.mk
@@ -14,19 +14,4 @@
 # limitations under the License.
 #
 
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libctsverifier_jni
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_PRELINK_MODULE := false
-
-LOCAL_SRC_FILES := \
-		CtsVerifierJniOnLoad.cpp \
-		com_android_cts_verifier_os_FileUtils.cpp	
-
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-
-include $(BUILD_SHARED_LIBRARY)
+include $(call all-subdir-makefiles)
diff --git a/apps/CtsVerifier/tests/Android.mk b/apps/CtsVerifier/jni/audioquality/Android.mk
similarity index 67%
copy from apps/CtsVerifier/tests/Android.mk
copy to apps/CtsVerifier/jni/audioquality/Android.mk
index b9572fb..565cec2 100644
--- a/apps/CtsVerifier/tests/Android.mk
+++ b/apps/CtsVerifier/jni/audioquality/Android.mk
@@ -1,4 +1,3 @@
-#
 # Copyright (C) 2010 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,22 +12,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+LOCAL_PATH := $(call my-dir)
 
-LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_CPP_EXTENSION := .cpp
+
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_PRELINK_MODULE := false
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_MODULE    := libaudioquality
+LOCAL_SRC_FILES := Fft.cpp Window.cpp GlitchTest.cpp MeasureRms.cpp \
+   OverflowCheck.cpp LinearityTest.cpp CompareSpectra.cpp \
+   GenerateSinusoid.cpp Wrapper.cpp
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsVerifierTests
-
-LOCAL_INSTRUMENTATION_FOR := CtsVerifier
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/apps/CtsVerifier/jni/audioquality/CompareSpectra.cpp b/apps/CtsVerifier/jni/audioquality/CompareSpectra.cpp
new file mode 100644
index 0000000..262a400
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/CompareSpectra.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+// An amplitude-normalized spectrum comparison method.
+
+#include "Fft.h"
+#include "Window.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* Find the endpoints of the signal stored in data such that the rms of
+   the found segment exceeds signalOnRms.  data is of length n.  The
+   RMS calculations used to find the endpoints use a window of length
+   step and advance step samples.  The approximate, conservative
+   endpoint sample indices are returned in start and end. */
+static void findEndpoints(short* data, int n, int step, float signalOnRms,
+                          int* start, int* end) {
+    int size = step;
+    *start = *end = 0;
+    int last_frame = n - size;
+    for (int frame = 0; frame < last_frame; frame += step) {
+        double sum = 0.0;
+        for (int i=0; i < size; ++i) {
+            float val = data[i + frame];
+            sum += (val * val);
+        }
+        float rms = sqrt(sum / size);
+        if (! *start) {
+            if (rms >= signalOnRms) {
+                *start = frame + size;
+            }
+            continue;
+        } else {
+            if (rms < signalOnRms) {
+                *end = frame - size;
+                // fprintf(stderr, "n:%d onset:%d offset:%d\n", n, *start, *end);
+                return;
+            }
+        }
+    }
+    // Handle the case where the signal does not drop below threshold
+    // after onset.
+    if ((*start > 0) && (! *end)) {
+        *end = n - size - 1;
+    }
+}
+
+// Sum the magnitude squared spectra.
+static void accumulateMagnitude(float* im, float* re, int size, double* mag) {
+    for (int i = 0; i < size; ++i) {
+        mag[i] += ((re[i] * re[i]) + (im[i] * im[i]));
+    }
+}
+
+/* Return a pointer to 1+(fftSize/2) spectrum magnitude values
+   averaged over all of the numSamples in pcm.  It is the
+   responsibility of the caller to free this magnitude array.  Return
+   NULL on failure.  Use 50% overlap on the spectra. An FFT of
+   fftSize points is used to compute the spectra, The overall signal
+   rms over all frequencies between lowestBin and highestBin is
+   returned as a scalar in rms. */
+double* getAverageSpectrum(short* pcm, int numSamples, int fftSize,
+                           int lowestBin, int highestBin, float* rms) {
+    if (numSamples < fftSize) return NULL;
+    int numFrames = 1 + ((2 * (numSamples - fftSize)) / fftSize);
+    int numMag = 1 + (fftSize / 2);
+    float* re = new float[fftSize];
+    float* im = new float[fftSize];
+    double* mag = new double[numMag];
+    for (int i = 0; i < numMag; ++i) {
+        mag[i] = 0.0;
+    }
+    Window wind(fftSize);
+    Fft ft;
+    int pow2 = ft.fftPow2FromWindowSize(fftSize);
+    ft.fftInit(pow2);
+    int input_p = 0;
+    for (int i = 0; i < numFrames; ++i) {
+        wind.window(pcm + input_p, re, 0.0);
+        for (int j = 0; j < fftSize; ++j) {
+            im[j] = 0.0;
+        }
+        ft.fft(re,im);
+        accumulateMagnitude(im, re, numMag, mag);
+        input_p += fftSize / 2;
+    }
+    double averageEnergy = 0.0; // per frame average energy
+    for (int i = 0; i < numMag; ++i) {
+        double e = mag[i]/numFrames;
+        if ((i >= lowestBin) && (i <= highestBin))
+            averageEnergy += e;
+        mag[i] = sqrt(e);
+    }
+    *rms = sqrt(averageEnergy / (highestBin - lowestBin + 1));
+    delete [] re;
+    delete [] im;
+    return mag;
+}
+
+/* Compare the average magnitude spectra of the signals in pcm and
+   refPcm, which are of length numSamples and numRefSamples,
+   respectively; both sampled at sample_rate.  The maximum deviation
+   between average spectra, expressed in dB, is returned in
+   maxDeviation, and the rms of all dB variations is returned in
+   rmsDeviation.  Note that a lower limit is set on the frequencies that
+   are compared so as to ignore irrelevant DC and rumble components.  If
+   the measurement fails for some reason, return 0; else return 1, for
+   success.  Causes for failure include the amplitude of one or both of
+   the signals being too low, or the duration of the signals being too
+   short.
+
+   Note that the expected signal collection scenario is that the phone
+   would be stimulated with a broadband signal as in a recognition
+   attempt, so that there will be some "silence" regions at the start and
+   end of the pcm signals.  The preferred stimulus would be pink noise,
+   but any broadband signal should work. */
+int compareSpectra(short* pcm, int numSamples, short* refPcm,
+                   int numRefSamples, float sample_rate,
+                   float* maxDeviation, float* rmsDeviation) {
+    int fftSize = 512;           // must be a power of 2
+    float lowestFreq = 100.0;    // don't count DC, room rumble, etc.
+    float highestFreq = 3500.0;  // ignore most effects of sloppy anti alias filters.
+    int lowestBin = int(0.5 + (lowestFreq * fftSize / sample_rate));
+    int highestBin = int(0.5 + (highestFreq * fftSize / sample_rate));
+    float signalOnRms = 1000.0; // endpointer RMS on/off threshold
+    int endpointStepSize = int(0.5 + (sample_rate * 0.02)); // 20ms setp
+    float rms1 = 0.0;
+    float rms2 = 0.0;
+    int onset = 0;
+    int offset = 0;
+    findEndpoints(refPcm, numRefSamples, endpointStepSize, signalOnRms,
+                   &onset, &offset);
+    double* spect1 = getAverageSpectrum(refPcm + onset, offset - onset,
+                                        fftSize, lowestBin, highestBin, &rms1);
+    findEndpoints(pcm, numSamples, endpointStepSize, signalOnRms,
+                  &onset, &offset);
+    double* spect2 = getAverageSpectrum(pcm + onset, offset - onset,
+                                        fftSize, lowestBin, highestBin, &rms2);
+    int magSize = 1 + (fftSize/2);
+    if ((rms1 <= 0.0) || (rms2 <= 0.0))
+        return 0; // failure because one or both signals are too short or
+                  // too low in amplitude.
+    float rmsNorm = rms2 / rms1; // compensate for overall gain differences
+    // fprintf(stderr, "Level normalization: %f dB\n", 20.0 * log10(rmsNorm));
+    *maxDeviation = 0.0;
+    float sumDevSquared = 0.0;
+    for (int i = lowestBin; i <= highestBin; ++i) {
+        double val = 1.0;
+        if ((spect1[i] > 0.0) && (spect2[i] > 0.0)) {
+            val = 20.0 * log10(rmsNorm * spect1[i] / spect2[i]);
+        }
+        sumDevSquared += val * val;
+        if (fabs(val) > fabs(*maxDeviation)) {
+            *maxDeviation = val;
+        }
+        // fprintf(stderr, "%d %f\n", i, val);
+    }
+    *rmsDeviation = sqrt(sumDevSquared / (highestBin - lowestBin + 1));
+    delete [] spect1;
+    delete [] spect2;
+    return 1; // success
+}
+
+
diff --git a/apps/CtsVerifier/jni/audioquality/CompareSpectra.h b/apps/CtsVerifier/jni/audioquality/CompareSpectra.h
new file mode 100644
index 0000000..8b473e1
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/CompareSpectra.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef COMPARE_SPECTRA_H
+#define COMPARE_SPECTRA_H
+
+/* Compare the average magnitude spectra of the signals in pcm and
+   refPcm, which are of length numSamples and nRefSamples,
+   respectively; both sampled at sample_rate.  The maximum deviation
+   between average spectra, expressed in dB, is returned in
+   maxDeviation, and the rms of all dB variations is returned in
+   rmsDeviation.  Note that a lower limit is set on the frequencies that
+   are compared so as to ignore irrelevant DC and rumble components.  If
+   the measurement fails for some reason, return 0; else return 1, for
+   success.  Causes for failure include the amplitude of one or both of
+   the signals being too low, or the duration of the signals being too
+   short.
+
+   Note that the expected signal collection scenario is that the phone
+   would be stimulated with a broadband signal as in a recognition
+   attempt, so that there will be some "silence" regions at the start and
+   end of the pcm signals.  The preferred stimulus would be pink noise,
+   but any broadband signal should work. */
+
+int compareSpectra(short* pcm, int numSamples, short* refPcm,
+                   int nRefSamples, float sampleRate,
+                   float* maxDeviation, float* rmsDeviation);
+
+#endif // COMPARE_SPECTRA_H
diff --git a/apps/CtsVerifier/jni/audioquality/Fft.cpp b/apps/CtsVerifier/jni/audioquality/Fft.cpp
new file mode 100644
index 0000000..867bc86
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/Fft.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "Fft.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+Fft::Fft(void) : mCosine(0), mSine(0), mFftSize(0), mFftTableSize(0) { }
+
+Fft::~Fft(void) {
+    fftCleanup();
+}
+
+/* Construct a FFT table suitable to perform a DFT of size 2^power. */
+void Fft::fftInit(int power) {
+    fftCleanup();
+    fftMakeTable(power);
+}
+
+void Fft::fftCleanup() {
+        delete [] mSine;
+        delete [] mCosine;
+        mSine = NULL;
+        mCosine = NULL;
+        mFftTableSize = 0;
+        mFftSize = 0;
+}
+
+/* z <- (10 * log10(x^2 + y^2))    for n elements */
+int Fft::fftLogMag(float *x, float *y, float *z, int n) {
+    float *xp, *yp, *zp, t1, t2, ssq;
+
+    if(x && y && z && n) {
+        for(xp=x+n, yp=y+n, zp=z+n; zp > z;) {
+            t1 = *--xp;
+            t2 = *--yp;
+            ssq = (t1*t1)+(t2*t2);
+            *--zp = (ssq > 0.0)? 10.0 * log10((double)ssq) : -200.0;
+        }
+        return 1;    //true
+    } else {
+        return 0;    // false/fail
+    }
+}
+
+int Fft::fftMakeTable(int pow2) {
+    int lmx, lm;
+    float *c, *s;
+    double scl, arg;
+
+    mFftSize = 1 << pow2;
+    mFftTableSize = lmx = mFftSize/2;
+    mSine = new float[lmx];
+    mCosine = new float[lmx];
+    scl = (M_PI*2.0)/mFftSize;
+    for (s=mSine, c=mCosine, lm=0; lm<lmx; lm++ ) {
+        arg = scl * lm;
+        *s++ = sin(arg);
+        *c++ = cos(arg);
+    }
+    mBase = (mFftTableSize * 2)/mFftSize;
+    mPower2 = pow2;
+    return(mFftTableSize);
+}
+
+
+/* Compute the discrete Fourier transform of the 2**l complex sequence
+ * in x (real) and y (imaginary).  The DFT is computed in place and the
+ * Fourier coefficients are returned in x and y.
+ */
+void Fft::fft( float *x, float *y ) {
+    float c, s, t1, t2;
+    int j1, j2, li, lix, i;
+    int lmx, lo, lixnp, lm, j, nv2, k=mBase, im, jm, l = mPower2;
+
+    for (lmx=mFftSize, lo=0; lo < l; lo++, k *= 2) {
+        lix = lmx;
+        lmx /= 2;
+        lixnp = mFftSize - lix;
+        for (i=0, lm=0; lm<lmx; lm++, i += k ) {
+            c = mCosine[i];
+            s = mSine[i];
+            for ( li = lixnp+lm, j1 = lm, j2 = lm+lmx; j1<=li;
+                  j1+=lix, j2+=lix ) {
+                t1 = x[j1] - x[j2];
+                t2 = y[j1] - y[j2];
+                x[j1] += x[j2];
+                y[j1] += y[j2];
+                x[j2] = (c * t1) + (s * t2);
+                y[j2] = (c * t2) - (s * t1);
+            }
+        }
+    }
+
+    /* Now perform the bit reversal. */
+    j = 1;
+    nv2 = mFftSize/2;
+    for ( i=1; i < mFftSize; i++ ) {
+        if ( j < i ) {
+            jm = j-1;
+            im = i-1;
+            t1 = x[jm];
+            t2 = y[jm];
+            x[jm] = x[im];
+            y[jm] = y[im];
+            x[im] = t1;
+            y[im] = t2;
+        }
+        k = nv2;
+        while ( j > k ) {
+            j -= k;
+            k /= 2;
+        }
+        j += k;
+    }
+}
+
+/* Compute the discrete inverse Fourier transform of the 2**l complex
+ * sequence in x (real) and y (imaginary).  The DFT is computed in
+ * place and the Fourier coefficients are returned in x and y.  Note
+ * that this DOES NOT scale the result by the inverse FFT size.
+ */
+void Fft::ifft(float *x, float *y ) {
+    float c, s, t1, t2;
+    int j1, j2, li, lix, i;
+    int lmx, lo, lixnp, lm, j, nv2, k=mBase, im, jm, l = mPower2;
+
+    for (lmx=mFftSize, lo=0; lo < l; lo++, k *= 2) {
+        lix = lmx;
+        lmx /= 2;
+        lixnp = mFftSize - lix;
+        for (i=0, lm=0; lm<lmx; lm++, i += k ) {
+            c = mCosine[i];
+            s = - mSine[i];
+            for ( li = lixnp+lm, j1 = lm, j2 = lm+lmx; j1<=li;
+                        j1+=lix, j2+=lix ) {
+                t1 = x[j1] - x[j2];
+                t2 = y[j1] - y[j2];
+                x[j1] += x[j2];
+                y[j1] += y[j2];
+                x[j2] = (c * t1) + (s * t2);
+                y[j2] = (c * t2) - (s * t1);
+            }
+        }
+    }
+
+    /* Now perform the bit reversal. */
+    j = 1;
+    nv2 = mFftSize/2;
+    for ( i=1; i < mFftSize; i++ ) {
+        if ( j < i ) {
+            jm = j-1;
+            im = i-1;
+            t1 = x[jm];
+            t2 = y[jm];
+            x[jm] = x[im];
+            y[jm] = y[im];
+            x[im] = t1;
+            y[im] = t2;
+        }
+        k = nv2;
+        while ( j > k ) {
+            j -= k;
+            k /= 2;
+        }
+        j += k;
+    }
+}
+
+int Fft::fftGetSize(void) { return mFftSize; }
+
+int Fft::fftGetPower2(void) { return mPower2; }
diff --git a/apps/CtsVerifier/jni/audioquality/Fft.h b/apps/CtsVerifier/jni/audioquality/Fft.h
new file mode 100644
index 0000000..6a21d9a
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/Fft.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef LEGACY_TALKIN_FFT_H
+#define LEGACY_TALKIN_FFT_H
+
+class Fft {
+public:
+    Fft(void);
+
+    virtual ~Fft(void);
+
+    // Prepare for radix-2 FFT's of size (1<<pow2) 
+    void fftInit(int pow2);
+
+    // Forward fft.  Real time-domain components in x, imaginary in y 
+    void fft(float *x, float *y);
+
+    // Inverse fft.  Real frequency-domain components in x, imaginary in y 
+    void ifft(float *x, float *y);
+
+    // Compute the dB-scaled log-magnitude spectrum from the real spectal
+    // amplitude values in 'x', and imaginary values in 'y'.  Return the
+    // magnitude spectrum in z.  Compute 'n' components. 
+    int fftLogMag(float *x, float *y, float *z, int n);
+
+    int fftGetSize();
+
+    int fftGetPower2();
+
+    // Return the power of 2 required to contain at least size samples.
+    static int fftPow2FromWindowSize(int size) {
+        int pow2 = 1;
+        while ((1 << pow2) < size)
+            pow2++;
+        return pow2;
+    }
+
+private:
+    // Free up memory and reset the static globals. 
+    void fftCleanup();
+
+    // Create the sine/cosine basis tables and return the size of the FFT
+    // corresponding to pow2. 
+    int fftMakeTable(int pow2);
+
+    float* mSine;
+    float* mCosine;
+    int mFftTableSize;
+    int mFftSize;
+    int mPower2;
+    int mBase;
+};
+
+#endif // LEGACY_TALKIN_FFT_H 
diff --git a/apps/CtsVerifier/jni/audioquality/GenerateSinusoid.cpp b/apps/CtsVerifier/jni/audioquality/GenerateSinusoid.cpp
new file mode 100644
index 0000000..1823d58
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/GenerateSinusoid.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+// Generate a sinusoidal signal with optional onset and offset ramps.
+
+#include <stdlib.h>
+#include <math.h>
+
+static inline short clipAndRound(float val) {
+    if (val > 32767.0)
+        return 32767;
+    if (val < -32768.0)
+        return -32768;
+    if (val >= 0.0)
+        return static_cast<short>(0.5 + val);
+    return static_cast<short>(val - 0.5);
+}
+
+/* Return a sinusoid of frequency freq Hz and amplitude ampl in output
+   of length numOutput.  If ramp > 0.0, taper the ends of the signal
+   with a half-raised-cosine function.  It is up to the caller to
+   delete[] output.  If this call fails due to unreasonable arguments,
+   numOutput will be zero, and output will be NULL.  Note that the
+   duration of the up/down ramps will be within the specified
+   duration.  Note that if amplitude is specified outside of the
+   numerical range of int16, the signal will be clipped at +- 32767. */
+void generateSinusoid(float freq, float duration, float sampleRate,
+                      float amplitude, float ramp,
+                      int* numOutput, short** output) {
+    // Sanity check
+    if ((duration < (2.0 * ramp)) || ((freq * 2.0) > sampleRate) || (ramp < 0.0)) {
+        *output = NULL;
+        *numOutput = 0;
+        return;
+    }
+    int numSamples = int(0.5 + (sampleRate * duration));
+    double arg = M_PI * 2.0 * freq / sampleRate;
+    short* wave = new short[numSamples];
+    int numRamp = int(0.5 + (sampleRate * ramp));
+    for (int i = 0; i < numSamples; ++i) {
+        float val = amplitude * sin(arg * i);
+        if (numRamp > 0) {
+            if (i < numRamp) {
+                float gain = (0.5 - (0.5 * cos((0.5 + i) * M_PI / numRamp)));
+                val *= gain;
+            } else {
+                if (i > (numSamples - numRamp - 1)) {
+                    float gain = (0.5 - (0.5 * cos((0.5 + (numSamples - i - 1))
+                            * M_PI / numRamp)));
+                    val *= gain;
+                }
+            }
+        }
+        wave[i] = clipAndRound(val);
+    }
+    *numOutput = numSamples;
+    *output = wave;
+}
diff --git a/apps/CtsVerifier/jni/audioquality/GenerateSinusoid.h b/apps/CtsVerifier/jni/audioquality/GenerateSinusoid.h
new file mode 100644
index 0000000..eeedca6
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/GenerateSinusoid.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef GENERATE_SINUSOID_H
+#define GENERATE_SINUSOID_H
+
+/* Return a sinusoid of frequency freq Hz and amplitude ampl in output
+   of length numOutput.  If ramp > 0.0, taper the ends of the signal
+   with a half-raised-cosine function.  It is up to the caller to
+   delete[] output.  If this call fails due to unreasonable arguments,
+   numOutput will be zero, and output will be NULL.  Note that the
+   duration of the up/down ramps will be within the specified
+   duration.  Note that if amplitude is specified outside of the
+   numerical range of int16, the signal will be clipped at +- 32767. */
+void generateSinusoid(float freq, float duration, float sampleRate,
+                      float amplitude, float ramp,
+                      int* numOutput, short** output);
+
+#endif // GENERATE_SINUSOID_H
+
diff --git a/apps/CtsVerifier/jni/audioquality/GlitchTest.cpp b/apps/CtsVerifier/jni/audioquality/GlitchTest.cpp
new file mode 100644
index 0000000..2605f77
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/GlitchTest.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "GlitchTest.h"
+#include "Window.h"
+#include "Fft.h"
+
+#include <math.h>
+
+GlitchTest::GlitchTest(void) : mRe(0), mIm(0), mFt(0), mWind(0) {}
+
+void GlitchTest::init(float sampleRate, float stimFreq, float onsetThresh,
+                      float dbSnrThresh) {
+    cleanup(); // in case Init gets called multiple times.
+    // Analysis parameters
+    float windowDur = 0.025;    // Duration of the analysis window in seconds. 
+    float frameInterval = 0.01; // Interval in seconds between frame onsets 
+    float lowestFreq = 200.0;   // Lowest frequency to include in
+                                // background noise power integration.
+    mSampleRate = sampleRate;
+    mOnsetThresh = onsetThresh;
+    mDbSnrThresh = dbSnrThresh;
+    mFrameStep = (int)(0.5 + (mSampleRate * frameInterval));
+    mWindowSize = (int)(0.5 + (mSampleRate * windowDur));
+    mWind = new Window(mWindowSize);
+    mFt = new Fft;
+    mFt->fftInit(mFt->fftPow2FromWindowSize(mWindowSize));
+    mFftSize = mFt->fftGetSize();
+    mRe = new float[mFftSize];
+    mIm = new float[mFftSize];
+    float freqInterval = mSampleRate / mFftSize;
+    // We can exclude low frequencies from consideration, since the
+    // phone may have DC offset in the A/D, and there may be rumble in
+    // the testing room.    
+    mLowestSpectrumBin = (int)(0.5 + (lowestFreq / freqInterval));
+    // These are the bin indices within which most of the energy due to
+    // the (windowed) tone should be found. 
+    mLowToneBin = (int)(0.5 + (stimFreq / freqInterval)) - 2;
+    mHighToneBin = (int)(0.5 + (stimFreq / freqInterval)) + 2;
+    if (mLowestSpectrumBin >= mLowToneBin) {
+        mLowestSpectrumBin = mHighToneBin + 1;
+    }
+}
+
+int GlitchTest::checkToneSnr(short* pcm, int numSamples, float* duration,
+                             int* numBadFrames) {
+    *numBadFrames = 0;
+    *duration = 0.0;
+    if (!(mRe && mIm)) {
+        return -1; // not initialized.
+    }
+    int n_frames = 1 + ((numSamples - mWindowSize) / mFrameStep);
+    if (n_frames < 4) { // pathologically short input signal 
+        return -2;
+    }
+    *numBadFrames = 0;
+    int onset = -1;
+    int offset = -1;
+    for (int frame = 0; frame < n_frames; ++frame) {
+        int numSpectra = 0;
+        mWind->window(pcm + frame*mFrameStep, mRe, 0.0);
+        realMagSqSpectrum(mRe, mWindowSize, mRe, &numSpectra);
+        int maxLoc = 0;
+        float maxValue = 0.0;
+        findPeak(mRe, mLowestSpectrumBin, numSpectra, &maxLoc, &maxValue);
+        // possible states: (1) before tone onset; (2) within tone
+        // region; (3) after tone offset.
+        if ((onset < 0) && (offset < 0)) { // (1) 
+            if ((maxLoc >= mLowToneBin) && (maxLoc <= mHighToneBin)
+                    && (maxValue > mOnsetThresh)) {
+                onset = frame;
+            }
+            continue;
+        }
+        if ((onset >= 0) && (offset < 0)) { // (2) 
+            if (frame > (onset + 2)) { // let the framer get completely
+                                       // into the tonal signal
+                double sumNoise = 1.0; // init. to small non-zero vals to
+                                       // avoid log or divz problems
+                double sumSignal = 1.0;
+                float snr = 0.0;
+                if (maxValue < mOnsetThresh) {
+                    offset = frame;
+                    *duration = mFrameStep * (offset - onset) / mSampleRate;
+                    if (*numBadFrames >= 1) {
+                        (*numBadFrames) -= 1; // account for expected glitch at
+                                            // signal offset
+                    }
+                    continue;
+                }
+                for (int i = mLowestSpectrumBin; i < mLowToneBin; ++i) {
+                    sumNoise += mRe[i]; // note that mRe contains the magnitude
+                                        // squared spectrum.
+                }
+                for (int i = mLowToneBin; i <= mHighToneBin; ++i)
+                    sumSignal += mRe[i];
+                for (int i = mHighToneBin + 1; i < numSpectra; ++i) {
+                    sumNoise += mRe[i]; // Note: mRe has the mag squared spectrum.
+                }
+                snr = 10.0 * log10(sumSignal / sumNoise);
+                if (snr < mDbSnrThresh)
+                    (*numBadFrames) += 1;
+            }
+            continue;
+        }
+        if ((onset >= 0) && (offset > 0)) { // (3)
+            if ((maxLoc >= mLowToneBin) && (maxLoc <= mHighToneBin) &&
+                    (maxValue > mOnsetThresh)) { // tone should not pop up again!
+                (*numBadFrames) += 1;
+            }
+            continue;
+        }
+    }
+    if ((onset >= 0) && (offset > 0))
+        return 1;  // Success.
+    if (onset < 0) {
+        return -3; // Signal onset not found.
+    }
+    return -4;     // Signal offset not found.
+}
+
+void GlitchTest::cleanup(void) {
+    delete [] mRe;
+    delete [] mIm;
+    delete mFt;
+    delete mWind;
+    mRe = 0;
+    mIm = 0;
+    mWind = 0;
+    mFt = 0;
+}
+
+int GlitchTest::realMagSqSpectrum(float* data, int numInput,
+                                  float* output, int* numOutput) {
+    *numOutput = 0;
+    if ((numInput <= 0) || (numInput > mFftSize))
+        return 0;
+    int i = 0;
+    for (i = 0; i < numInput; ++i) {
+        mRe[i] = data[i];
+        mIm[i] = 0.0;
+    }
+    for ( ; i < mFftSize; ++i) {
+        mRe[i] = 0.0;
+        mIm[i] = 0.0;
+    }
+    mFt->fft(mRe, mIm);
+    *numOutput = 1 + (mFftSize / 2);
+    for (i = 0; i < *numOutput; ++i) {
+        output[i] = (mRe[i] * mRe[i]) + (mIm[i] * mIm[i]);
+    }
+    return 1;
+}
+
+void GlitchTest::findPeak(float* data, int startSearch, int endSearch,
+                          int* maxLoc, float* maxValue) {
+    float amax = data[startSearch];
+    int loc = startSearch;
+    for (int i = startSearch + 1; i < endSearch; ++i) {
+        if (data[i] > amax) {
+            amax = data[i];
+            loc = i;
+        }
+    }
+    *maxLoc = loc;
+    *maxValue = 10.0 * log10(amax / mWindowSize);
+}
+
diff --git a/apps/CtsVerifier/jni/audioquality/GlitchTest.h b/apps/CtsVerifier/jni/audioquality/GlitchTest.h
new file mode 100644
index 0000000..de96e6e
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/GlitchTest.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef GLITCH_TEST_H
+#define GLITCH_TEST_H
+
+class Fft;
+class Window;
+
+class GlitchTest {
+public:
+    GlitchTest(void);
+
+    virtual ~GlitchTest(void) {
+        cleanup();
+    }
+
+    /* Set up the instance to operate on input test signals sampled at
+       sample_rate that contain a stimulis tone of stim_freq frequency.
+       The signal will be considered on during the interval it exceeds
+       onset_thresh (dB re 1.0).  Any frames containing a tone that have
+       a signal energy to out-of-band energy ratio less than
+       db_snr_thresh are counted as bad frames.  Init must be called
+       before CheckToneSnr. */
+    void init(float sample_rate, float stim_freq, float onset_thresh,
+              float db_snr_thresh);
+
+    /* Analyze the n_samples of the lin16 signal in pcm.  This signal is
+       assumed sampled at sample_rate, and to contain a sinusoid with
+       center frequency stim_freq, embedded somewhere in time, with
+       "silence" intervals before and after.  The contiguous duration of
+       the tone that exceeds onset_thresh (in dB re 1.0) is returned as
+       seconds in duration. The number of frames for which the ratio of
+       the energy at the tone frequency to the energy in the rest of the
+       spectrum is less than db_snr_thresh is returned in n_bad_frames.
+       If the test succeed, the method returns 1, else it returns a
+       negative number that reflects the cause of failure as follows:
+         -1     The instance is not initialized.
+         -2     There are not enough samples to do a reasonable check.
+         -3     The tone signal onset was not found.
+         -4     The tone signal end was not found. */
+    int checkToneSnr(short* pcm, int n_samples, float* duration,
+                     int* n_bad_frames);
+
+private:
+    // Free memory, etc.
+    void cleanup(void);
+
+    /* Do a real FFT on the n_input samples in data, and return n_output
+       power spectral density points in output.  The output points include
+       DC through the Nyquist frequency (i.e. 1 + fft_size/2).  output
+       must be large enough to accommodate this size. If n_input==0 or
+       n_input > fft_size, return 0; else return 1. */
+    int realMagSqSpectrum(float* data, int n_input,
+                             float* output, int* n_output);
+
+    /* Find the largest value in data starting at start_search and ending
+       at end_search-1.  The values in data are assumed to be magnitude
+       squared values from a spectrum computation based on window_size
+       sample points.  Return the index where the largest value was found,
+       and return the dB (re 1.0) equivalent of the highest magnitude. */
+    void findPeak(float* data, int start_search, int end_search,
+                  int* max_loc, float* max_value);
+
+    // Real and Imaginary analysis arrays.
+    float* mRe;
+    float* mIm;
+    // Fourier transform and window.
+    Fft* mFt;
+    Window* mWind;
+    // Derived parameters and other variables.
+    float mSampleRate;
+    int mFrameStep;
+    int mWindowSize;
+    int mFftSize;
+    float mOnsetThresh;
+    float mDbSnrThresh;
+    int mLowestSpectrumBin;
+    int mLowToneBin;
+    int mHighToneBin;
+};
+
+#endif // GLITCH_TEST_H
diff --git a/apps/CtsVerifier/jni/audioquality/LinearityTest.cpp b/apps/CtsVerifier/jni/audioquality/LinearityTest.cpp
new file mode 100644
index 0000000..f8cf9ac
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/LinearityTest.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/* This test accepts a collection of N speech waveforms collected as
+   part of N recognition attempts.  The waveforms are ordered by
+   increasing presentation level.  The test determines the extent to
+   which the peak amplitudes in the waveforms track the change in
+   presentation level.  Failure to track the presentation level within
+   some reasonable margin is an indication of clipping or of automatic
+   gain control in the signal path.
+
+   The speech stimuli that are used for this test should simply be
+   replications of exactly the same speech signal presented at
+   different levels.  It is expected that all recognition attempts on
+   this signal result in correct recognition.  A warning, but not a
+   hard failure, should be issued if any of the attempts fail.  The
+   hard failure criterion for this test should be only based on the
+   amplitude linearity tracking. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+/* Keep a record of the top N absolute values found using a slow
+   bubble-sort.  This is OK, since the use of this program is not time
+   critical, and N is usually small.  Note that the argument n = N-1. */
+static void bubbleUp(int* store, int n, short val) {
+    if (val < store[n])
+        return;
+    for (int i = 0; i <= n; ++i) {
+        if (val >= store[i]) {
+            for (int j = n; j > i ; j--)
+                store[j] = store[j-1];
+            store[i] = val;
+            return;
+        }
+    }
+}
+
+/* Make two measurements on the signal of length numSamples sampled at
+   sampleRate in pcm: the RMS of the highest amplitude 30ms segment
+   (returned in peakRms), and the RMS of the top 50 peak absolute
+   values found (returned in peakAverage).  If the signal is too
+   short to make reasonable measurements, the function returns 0, else
+   it returns 1. */
+static int peakLevels(short* pcm, int numSamples, float sampleRate,
+                      float* peakAverage, float* peakRms) {
+    float rmsFrameSize = 0.03;
+    float rmsFrameStep = 0.01;
+    int frameStep = int(0.5 + (sampleRate * rmsFrameStep));
+    int frameSize = int(0.5 + (sampleRate * rmsFrameSize));
+    int numFrames = 1 + ((numSamples - frameSize) / frameStep);
+
+    if (numFrames < 10) {
+        return 0; // failure for too short signal
+    }
+
+    // Peak RMS calculation
+    double maxEnergy = 0.0;
+    for (int frame = 0; frame < numFrames; ++frame) {
+        double energy = 0.0;
+        int limit = (frame * frameStep) + frameSize;
+        for (int i = frame * frameStep; i < limit; ++i) {
+            double s = pcm[i];
+            energy += s * s;
+        }
+        if (energy > maxEnergy) {
+            maxEnergy = energy;
+        }
+    }
+    *peakRms = sqrt(maxEnergy / frameSize);
+
+    // Find the absolute highest topN peaks in the signal and compute
+    // the RMS of their values.
+    int topN = 50; // The number of highest peaks over which to average.
+    int topM = topN - 1;
+    int* maxVal = new int[topN];
+    for (int i = 0; i < topN; ++i) {
+        maxVal[i] = 0;
+    }
+    for (int i = 0; i < numSamples; ++i) {
+        if (pcm[i] >= 0) {
+            bubbleUp(maxVal, topM, pcm[i]);
+        } else {
+            bubbleUp(maxVal, topM, -pcm[i]);
+        }
+    }
+    float sum = 0.0;
+    // The RMS is taken bacause we want the values of the highest peaks
+    // to dominate.
+    for (int i = 0; i < topN; ++i) {
+        float fval = maxVal[i];
+        sum += (fval * fval);
+    }
+    delete [] maxVal;
+    *peakAverage = sqrt(sum/topN);
+    return 1; // success
+}
+
+/* There are numSignals int16 signals in pcms.  sampleCounts is an
+   integer array of length numSignals containing their respective
+   lengths in samples.  They are all sampled at sampleRate.  The pcms
+   are ordered by increasing stimulus level.  The level steps between
+   successive stimuli were of size dbStepSize dB.  The signal with
+   index referenceStim (0 <= referenceStim < numSignals) should be in
+   an amplitude range that is reasonably certain to be linear (e.g. at
+   normal speaking levels).  The maximum deviation in linearity found
+   (in dB) is returned in maxDeviation.  The function returns 1 if
+   the measurements could be made, or a negative number that
+   indicates the error, as follows:
+      -1 The input signals or sample counts are missing.
+      -2 The number of input signals is < 2.
+      -3 The specified sample rate is <= 4000.0
+      -4 The dB step size for the increase in stimulus level is <= 0.0
+      -5 The specified reverence stimulus number is out of range.
+      -6 One or more of the stimuli is too short in duration. */
+int linearityTest(short** pcms, int* sampleCounts, int numSignals,
+                  float sampleRate, float dbStepSize, int referenceStim,
+                  float* maxDeviation) {
+    if (!(pcms && sampleCounts)) {
+        return -1; // Input signals or sample counts are missing
+    }
+    if (numSignals < 2) {
+        return -2; // the number of input signals must be >= 2;
+    }
+    if (sampleRate <= 4000.0) {
+        return -3; // The sample rate must be > 4000 Hz.
+    }
+    if (dbStepSize <= 0.0) {
+        return -4; // The dB step size must be > 0.0
+    }
+    if (!((referenceStim >= 0) && (referenceStim < numSignals))) {
+        return -5; // (0 <= referenceStim < numSignals) must be true
+    }
+    float* peakAverage = new float[numSignals];
+    float* peakRms = new float[numSignals];
+    for (int sig = 0; sig < numSignals; ++sig) {
+        if (!peakLevels(pcms[sig], sampleCounts[sig],
+             sampleRate, peakAverage + sig, peakRms + sig)) {
+            return -6; // failure because a signal is too short.
+        }
+    }
+    float peakAverageRef = peakAverage[referenceStim];
+    float peakRmsRef = peakRms[referenceStim];
+    float maxDev = 0.0;
+    for (int i = 0; i < numSignals; ++i) {
+        float dbAverage = 20.0 * log10(peakAverage[i]/peakAverageRef);
+        float dbRms = 20.0 * log10(peakRms[i]/peakRmsRef);
+        float reference = dbStepSize * (i - referenceStim);
+        float average_level = 0.5 * (dbAverage + dbRms);
+        float dev = fabs(average_level - reference);
+        // fprintf(stderr,"dbAverage:%f dbRms:%f reference:%f dev:%f\n",
+        //         dbAverage, dbRms, reference, dev);
+        if (dev > maxDev)
+            maxDev = dev;
+    }
+    *maxDeviation = maxDev;
+    return 1;
+}
diff --git a/apps/CtsVerifier/jni/audioquality/LinearityTest.h b/apps/CtsVerifier/jni/audioquality/LinearityTest.h
new file mode 100644
index 0000000..d92566a
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/LinearityTest.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef LINEARITY_TEST_H
+#define LINEARITY_TEST_H
+
+/* There are numSignals int16 signals in pcms.  sampleCounts is an
+   integer array of length numSignals containing their respective
+   lengths in samples.  They are all sampled at sampleRate.  The pcms
+   are ordered by increasing stimulus level.  The level steps between
+   successive stimuli were of size dbStepSize dB.  The signal with
+   index referenceStim (0 <= referenceStim < numSignals) should be in
+   an amplitude range that is reasonably certain to be linear (e.g. at
+   normal speaking levels).  The maximum deviation in linearity found
+   (in dB) is returned in maxDeviation.  The function returns 1 if
+   the measurements could be made, or a negative number that
+   indicates the error, as follows:
+      -1 The input signals or sample counts are missing.
+      -2 The number of input signals is < 2.
+      -3 The specified sample rate is <= 4000.0
+      -4 The dB step size for the increase in stimulus level is <= 0.0/
+      -5 The specified reverence stimulus number is out of range.
+      -6 One or more of the stimuli is too short in duration. */
+int linearityTest(short** pcms, int* sampleCounts, int numSignals,
+                  float sampleRate, float dbStepSize,
+                  int referenceStim, float* maxDeviation);
+
+
+#endif /* LINEARITY_TEST_H */
diff --git a/apps/CtsVerifier/jni/audioquality/MeasureRms.cpp b/apps/CtsVerifier/jni/audioquality/MeasureRms.cpp
new file mode 100644
index 0000000..640b191
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/MeasureRms.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include <math.h>
+
+/* Return the median of the n values in "values".
+   Uses a stupid bubble sort, but is only called once on small array. */
+float getMedian(float* values, int n) {
+    if (n <= 0)
+        return 0.0;
+    if (n == 1)
+        return values[0];
+    if (n == 2)
+        return 0.5 * (values[0] + values[1]);
+    for (int i = 1; i < n; ++i)
+        for (int j = i; j < n; ++j) {
+            if (values[j] < values[i-1]) {
+                float tmp = values[i-1];
+                values[i-1] = values[j];
+                values[j] = tmp;
+            }
+        }
+    int ind = int(0.5 + (0.5 * n)) - 1;
+    return values[ind];
+}
+
+float computeAndRemoveMean(short* pcm, int numSamples) {
+    float sum = 0.0;
+
+    for (int i = 0; i < numSamples; ++i)
+        sum += pcm[i];
+    short mean;
+    if (sum >= 0.0)
+        mean = (short)(0.5 + (sum / numSamples));
+    else
+        mean = (short)((sum / numSamples) - 0.5);
+    for (int i = 0; i < numSamples; ++i)
+        pcm[i] -= mean;
+    return sum / numSamples;
+}
+
+void measureRms(short* pcm, int numSamples, float sampleRate, float onsetThresh,
+                float* rms, float* stdRms, float* mean, float* duration) {
+    *rms = 0.0;
+    *stdRms = 0.0;
+    *duration = 0.0;
+    float frameDur = 0.025;    // Both the duration and interval of the
+                                // analysis frames.
+    float calInterval = 0.250; // initial part of signal used to
+                                // establish background level (seconds).
+    double sumFrameRms = 1.0;
+    float sumSampleSquares = 0.0;
+    double sumFrameSquares = 1.0; // init. to small number to avoid
+                                    // log and divz problems.
+    int frameSize = (int)(0.5 + (sampleRate * frameDur));
+    int numFrames = numSamples / frameSize;
+    int numCalFrames = int(0.5 + (calInterval / frameDur));
+    if (numCalFrames < 1)
+        numCalFrames = 1;
+    int frame = 0;
+
+    *mean = computeAndRemoveMean(pcm, numSamples);
+
+    if (onsetThresh < 0.0) { // Handle the case where we want to
+                              // simply measure the RMS of the entire
+                              // input sequence.
+        for (frame = 0; frame < numFrames; ++frame) {
+            short* p_data = pcm + (frame * frameSize);
+            int i;
+            for (i = 0, sumSampleSquares = 0.0; i < frameSize; ++i) {
+                float samp = p_data[i];
+                sumSampleSquares += samp * samp;
+            }
+            sumSampleSquares /= frameSize;
+            sumFrameSquares += sumSampleSquares;
+            double localRms = sqrt(sumSampleSquares);
+            sumFrameRms += localRms;
+        }
+        *rms = sumFrameRms / numFrames;
+        *stdRms = sqrt((sumFrameSquares / numFrames) - (*rms * *rms));
+        *duration = frameSize * numFrames / sampleRate;
+        return;
+    }
+
+    /* This handles the case where we look for a target signal against a
+       background, and expect the signal to start some time after the
+       beginning, and to finish some time before the end of the input
+       samples. */
+    if (numFrames < (3 * numCalFrames)) {
+        return;
+    }
+    float* calValues = new float[numCalFrames];
+    float calMedian = 0.0;
+    int onset = -1;
+    int offset = -1;
+
+    for (frame = 0; frame < numFrames; ++frame) {
+        short* p_data = pcm + (frame * frameSize);
+        int i;
+        for (i = 0, sumSampleSquares = 1.0; i < frameSize; ++i) {
+            float samp = p_data[i];
+            sumSampleSquares += samp * samp;
+        }
+        sumSampleSquares /= frameSize;
+        /* We handle three states: (1) before the onset of the signal; (2)
+           within the signal; (3) following the signal.  The signal is
+           assumed to be at least onsetThresh dB above the background
+           noise, and that at least one frame of silence/background
+           precedes the onset of the signal. */
+        if (onset < 0) { // (1)
+            sumFrameSquares += sumSampleSquares;
+            if (frame < numCalFrames ) {
+                calValues[frame] = sumSampleSquares;
+                continue;
+            }
+            if (frame == numCalFrames) {
+                calMedian = getMedian(calValues, numCalFrames);
+                if (calMedian < 10.0)
+                    calMedian = 10.0; // avoid divz, etc.
+            }
+            float ratio = 10.0 * log10(sumSampleSquares / calMedian);
+            if (ratio > onsetThresh) {
+                onset = frame;
+                sumFrameSquares = 1.0;
+                sumFrameRms = 1.0;
+            }
+            continue;
+        }
+        if ((onset > 0) && (offset < 0)) { // (2)
+            int sig_frame = frame - onset;
+            if (sig_frame < numCalFrames) {
+                calValues[sig_frame] = sumSampleSquares;
+            } else {
+                if (sig_frame == numCalFrames) {
+                    calMedian = getMedian(calValues, numCalFrames);
+                    if (calMedian < 10.0)
+                        calMedian = 10.0; // avoid divz, etc.
+                }
+                float ratio = 10.0 * log10(sumSampleSquares / calMedian);
+                int denFrames = frame - onset - 1;
+                if (ratio < (-onsetThresh)) { // found signal end
+                    *rms = sumFrameRms / denFrames;
+                    *stdRms = sqrt((sumFrameSquares / denFrames) - (*rms * *rms));
+                    *duration = frameSize * (frame - onset) / sampleRate;
+                    offset = frame;
+                    continue;
+                }
+            }
+            sumFrameSquares += sumSampleSquares;
+            double localRms = sqrt(sumSampleSquares);
+            sumFrameRms += localRms;
+            continue;
+        }
+        if (offset > 0) { // (3)
+            /* If we have found the real signal end, the level should stay
+               low till data end.  If not, flag this anomaly by increasing the
+               reported duration. */
+            float localRms = 1.0 + sqrt(sumSampleSquares);
+            float localSnr = 20.0 * log10(*rms / localRms);
+            if (localSnr < onsetThresh)
+                *duration = frameSize * (frame - onset) / sampleRate;
+            continue;
+        }
+    }
+    delete [] calValues;
+}
+
diff --git a/apps/CtsVerifier/jni/audioquality/MeasureRms.h b/apps/CtsVerifier/jni/audioquality/MeasureRms.h
new file mode 100644
index 0000000..78b43ae
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/MeasureRms.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef MEASURE_RMS_H
+#define MEASURE_RMS_H
+
+/* Measure the rms of the non-silence segment of the signal in pcm, which
+   is of numSamples length, and sampled at sampleRate.  pcm is assumed to
+   consist of silence - signal - silence, as might be logged during a
+   speech recognition attempt.  The stimulus signal in this case should
+   be approximately a 3-second burst of pink noise presented at a level
+   comparable to normal speaking level.  The RMS is measured using 25ms
+   duration non-overlapping windows.  These are averaged over the whole
+   non-silence part of pcm, and the result is returned in rms.  The
+   standard deviation of this measurement over all frames is returned in
+   stdRms, and the estimated duration of the non-silence region, in
+   seconds, is returned in duration.  The target signal is taken to be
+   that segment that is onsetThresh dB above the background, and is
+   expected to be continuous, once the onset has been found.  If
+   onsetThresh < 0.0, simply make the measurememt over the entire pcm
+   signal.  In both cases, the mean of the entire signal is returned in
+   mean. */
+void measureRms(short* pcm, int numSamples, float sampleRate,
+                float onsetThresh, float* rms, float* stdRms,
+                float* mean, float* duration);
+
+
+#endif /* MEASURE_RMS_H */
diff --git a/apps/CtsVerifier/jni/audioquality/OverflowCheck.cpp b/apps/CtsVerifier/jni/audioquality/OverflowCheck.cpp
new file mode 100644
index 0000000..20e9285
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/OverflowCheck.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/* This assumes an input signal that has at least a few hundred
+   milliseconds of high-amplitude sinusoidal signal.  The signal is
+   expected to be a relatively low-frequency sinusoid (200-400 Hz).
+   If the signal is linearly reproduced or clipped, There will be no
+   large step changes in value from one sample to the next.  On the
+   other hand, if the signal contains numerical overflow
+   (wrap-around), very large excursions will be produced.
+
+   This program first searches for the high-amplitude recorded
+   segment, then examines just that part of the signal for "large
+   excursions", and returns the results of the search as four
+   integers:
+
+   n_wraps signal_size sine_start sine_end
+
+   where n_wraps is the number of anomolous value jumps found,
+   signal_size is the number of lin16 samples found in the file,
+   sine_start and sine_end are the limits of the region searched for
+   anomolous jumps. */
+
+#include <stdlib.h>
+#include <math.h>
+
+// MAX_ALLOWED_STEP is the largest sample-to-sample change that will
+// be considered "normal" for 250 Hz signals sampled at 8kHz.  This is
+// calibrated by the largest sample-to-sample change that is naturally
+// present in a 250 Hz sine wave with amplitude of 40000 (which is
+// actually 7804).
+#define MAX_ALLOWED_STEP 16000
+
+// This is the RMS value that is expected to be exceded by a sinusoid
+// with a peak amplitude of 32767 (actually 23169).
+#define SIGNAL_ON_RMS 12000.0
+
+static void findEndpoints(short* data, int n, int step, int* start, int* end) {
+    int size = step;
+    *start = *end = 0;
+    int last_frame = n - size;
+    for (int frame = 0; frame < last_frame; frame += step) {
+        double sum = 0.0;
+        for (int i=0; i < size; ++i) {
+            float val = data[i + frame];
+            sum += (val * val);
+        }
+        float rms = sqrt(sum / size);
+        if (! *start) {
+            if (rms >= SIGNAL_ON_RMS) {
+                *start = frame + size;
+            }
+            continue;
+        } else {
+            if (rms < SIGNAL_ON_RMS) {
+                *end = frame - size;
+                return;
+            }
+        }
+    }
+    if ((*start > 0) && (! *end)) {
+        *end = n - size - 1;
+    }
+}
+
+static void checkExcursions(short* data, int start, int end, int* numJumps,
+                            int* maxPeak, int* minPeak) {
+    *numJumps = 0;
+    int endm = end - 1;
+    if ((endm - start) < 3) {
+        *numJumps = -1;
+        return;
+    }
+    *maxPeak = *minPeak = data[start];
+    for (int i = start; i < endm; ++i) {
+        int v1 = data[i];
+        int v2 = data[i+1];
+        if (v1 > *maxPeak)
+            *maxPeak = v1;
+        if (v1 < *minPeak)
+            *minPeak = v1;
+        int diff = v2 - v1;
+        if (diff < 0)
+            diff = -diff;
+        if (diff > MAX_ALLOWED_STEP)
+            (*numJumps) += 1;
+    }
+    return;
+}
+
+int overflowCheck(short* pcm, int numSamples, float sampleRate,
+                  float* duration, int* numDeltas, int* onset, int* offset,
+                  int* maxPeak, int* minPeak) {
+    float windSize = 0.020;
+    int minBuff = int(2.0 * sampleRate); // must have 2 sec of data at least.
+
+    if(pcm && (numSamples >= minBuff)) {
+        int step = int(0.5 + (windSize * sampleRate));
+        *onset = 0;
+        *offset = 0;
+
+        findEndpoints(pcm, numSamples, step, onset, offset);
+        *numDeltas = -1;
+        checkExcursions(pcm, *onset, *offset, numDeltas, maxPeak, minPeak);
+        *duration = (*offset - *onset) / sampleRate;
+        return 1; // true/success
+    }
+    return 0; // failure
+}
diff --git a/apps/CtsVerifier/jni/audioquality/OverflowCheck.h b/apps/CtsVerifier/jni/audioquality/OverflowCheck.h
new file mode 100644
index 0000000..ea6d780
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/OverflowCheck.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef OVERFLOW_CHECK_H
+#define OVERFLOW_CHECK_H
+
+/* This is a clipping/overflow check.  This is designed to look for a
+   ~3-second duration ~250 Hz pure tone at a level sufficient to cause
+   about 20% clipping.  It examines the recorded waveform for possible
+   overflow/wraparound.  The input signal of numSamples total sampled at
+   sampleRate is in pcm. The expected signal contains silence - tone -
+   silence.  The approximate duration in seconds of the tone found is
+   returned in duration.  The number of unexpectedly-large jumps within
+   the tone is returned in numDeltas.  The approximate sample numbers of
+   the tone endpoints are returned in onset and offset.  The maximum
+   and minimum found in the signal located between onset and offset are
+   returned in maxPeak and minPeak, respectively.  The function
+   return is 1 if the operation was sucessful, 0 on failure. */
+int overflowCheck(short* pcm, int numSamples, float sampleRate,
+                  float* duration, int* numDeltas, int* onset, int* offset,
+                  int* maxPeak, int* minPeak);
+
+#endif // OVERFLOW_CHECK_H
diff --git a/apps/CtsVerifier/jni/audioquality/Window.cpp b/apps/CtsVerifier/jni/audioquality/Window.cpp
new file mode 100644
index 0000000..8e89ed4
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/Window.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "Window.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+void Window::windowCreate(int size) {
+    if (mWindowWeights && (mWindowSize < size)) {
+        delete [] mWindowWeights;
+        mWindowWeights = NULL;
+        mWindowSize = 0;
+    }
+    if (!mWindowWeights) {
+        mWindowWeights = new float[size];
+    }
+    if (mWindowSize != size) {
+        double arg = M_PI * 2.0 / size;
+        mWindowSize = size;
+        for (int i = 0; i < size; ++i) {
+            mWindowWeights[i] = 0.5 - (0.5 * cos((i + 0.5) * arg));
+        }
+    }
+}
+
+void Window::windowCleanup() {
+    mWindowSize = 0;
+    delete [] mWindowWeights;
+    mWindowWeights = NULL;
+}
+
+/* Multiply the signal in data by the window weights.  Place the
+   resulting mWindowSize floating-point values in output.  If preemp
+   is != 0.0, apply a 1st-order preemphasis filter, and assume that
+   there are mWindowSize+1 samples available in data. */
+void Window::window(short* data, float* output, float preemp) {
+    if (preemp == 0.0) {
+        for (int i = 0; i < mWindowSize; ++i)
+            output[i] = data[i] * mWindowWeights[i];
+    } else {
+        for (int i = 0; i < mWindowSize; ++i)
+            output[i] = (data[i+1] - (preemp * data[i])) * mWindowWeights[i];
+    }
+}
+
diff --git a/apps/CtsVerifier/jni/audioquality/Window.h b/apps/CtsVerifier/jni/audioquality/Window.h
new file mode 100644
index 0000000..0829835
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/Window.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef WINDOW_INTERFACE_H
+#define WINDOW_INTERFACE_H
+
+class Window {
+public:
+    Window(int size) : mWindowWeights(0), mWindowSize(0) {
+        windowCreate(size);
+    }
+
+    Window() : mWindowWeights(0), mWindowSize(0) {
+    }
+
+    virtual ~Window(void) {
+        windowCleanup();
+    }
+
+    /* Create a Hann window of length size.  This allocates memory that
+       should be freed using window_cleanup(). */
+    void windowCreate(int size);
+
+    /* Free up memory and reset size to 0. */
+    void windowCleanup(void);
+
+
+    /* Multiply the signal in data by the window weights.  Place the
+       resulting window_size floating-point values in output.  If preemp
+       is != 0.0, apply a 1st-order preemphasis filter, and assume that
+       there are window_size+1 samples available in data. */
+    void window(short* data, float* output, float preemp);
+
+    int getWindowSize() { return mWindowSize; }
+
+    float* getWindowWeights() { return mWindowWeights; }
+
+private:
+    float* mWindowWeights;
+    int mWindowSize;
+
+};
+
+#endif /* WINDOW_INTERFACE_H */
diff --git a/apps/CtsVerifier/jni/audioquality/Wrapper.cpp b/apps/CtsVerifier/jni/audioquality/Wrapper.cpp
new file mode 100644
index 0000000..bee15c6
--- /dev/null
+++ b/apps/CtsVerifier/jni/audioquality/Wrapper.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+// Wrapper to the native phone test signal processing library, which
+// exposes an interface suitable for calling via JNI.
+
+#include <stdlib.h>
+#include <jni.h>
+
+#include "GenerateSinusoid.h"
+#include "MeasureRms.h"
+#include "GlitchTest.h"
+#include "OverflowCheck.h"
+#include "CompareSpectra.h"
+#include "LinearityTest.h"
+
+typedef short *shortPtr;
+
+extern "C" {
+    JNIEXPORT jshortArray JNICALL
+        Java_com_android_cts_verifier_audioquality_Native_generateSinusoid(
+            JNIEnv *env, jobject obj,
+            jfloat freq, jfloat duration,
+            jfloat sampleRate, jfloat amplitude, jfloat ramp);
+    JNIEXPORT jfloatArray JNICALL
+        Java_com_android_cts_verifier_audioquality_Native_measureRms(
+            JNIEnv *env, jobject obj,
+            jshortArray jpcm, jfloat sampleRate, jfloat onsetThresh);
+    JNIEXPORT jfloatArray JNICALL
+        Java_com_android_cts_verifier_audioquality_Native_glitchTest(
+            JNIEnv *env, jobject obj,
+            jfloat sampleRate, jfloat stimFreq, jfloat onsetThresh,
+            jfloat dbSnrThresh, jshortArray jpcm);
+    JNIEXPORT jfloatArray JNICALL
+        Java_com_android_cts_verifier_audioquality_Native_overflowCheck(
+            JNIEnv *env, jobject obj,
+            jshortArray jpcm, jfloat sampleRate);
+    JNIEXPORT jfloatArray JNICALL
+        Java_com_android_cts_verifier_audioquality_Native_compareSpectra(
+            JNIEnv *env, jobject obj,
+            jshortArray jpcm, jshortArray jrefPcm, jfloat sampleRate);
+    JNIEXPORT jfloat JNICALL
+        Java_com_android_cts_verifier_audioquality_Native_linearityTest(
+            JNIEnv *env, jobject obj,
+            jobjectArray jpcms,
+            jfloat sampleRate, jfloat dbStepSize, jint referenceStim);
+};
+
+/* Returns an array of sinusoidal samples.
+   If the arguments are invalid, returns an empty array. */
+JNIEXPORT jshortArray JNICALL
+    Java_com_android_cts_verifier_audioquality_Native_generateSinusoid(
+        JNIEnv *env, jobject obj,
+        jfloat freq, jfloat duration,
+        jfloat sampleRate, jfloat amplitude, jfloat ramp) {
+    short *wave = NULL;
+    int numSamples = 0;
+
+    generateSinusoid(freq, duration, sampleRate, amplitude, ramp,
+            &numSamples, &wave);
+
+    jshortArray ja;
+    if (!numSamples) {
+        ja = env->NewShortArray(0);
+    } else {
+        ja = env->NewShortArray(numSamples);
+        env->SetShortArrayRegion(ja, 0, numSamples, wave);
+        delete[] wave;
+    }
+    return ja;
+}
+
+/* Returns an array of four floats.
+   ret[0] = RMS
+   ret[1] = standard deviation of the RMS
+   ret[2] = non-silent region duration
+   ret[3] = mean value
+*/
+JNIEXPORT jfloatArray JNICALL
+    Java_com_android_cts_verifier_audioquality_Native_measureRms(
+        JNIEnv *env, jobject obj,
+        jshortArray jpcm, jfloat sampleRate, jfloat onsetThresh) {
+    float ret[4];
+    ret[0] = ret[1] = ret[2] = ret[3] = -1.0;
+    int numSamples = env->GetArrayLength(jpcm);
+    short *pcm = new short[numSamples];
+    env->GetShortArrayRegion(jpcm, 0, numSamples, pcm);
+
+    measureRms(pcm, numSamples, sampleRate, onsetThresh, ret, ret + 1,
+            ret + 3, ret + 2);
+
+    jfloatArray ja = env->NewFloatArray(4);
+    env->SetFloatArrayRegion(ja, 0, 4, ret);
+    return ja;
+}
+
+/* Returns an array of three floats.
+   ret[0] = #bad frames
+   ret[1] = error code
+   ret[2] = duration
+   Error code = 1 for success,
+               -1 if initialization failed,
+               -2 if insufficient samples
+               -3 if tone signal onset not found
+               -4 if tone signal end not found
+*/
+JNIEXPORT jfloatArray JNICALL
+    Java_com_android_cts_verifier_audioquality_Native_glitchTest(
+        JNIEnv *env, jobject obj,
+        jfloat sampleRate, jfloat stimFreq, jfloat onsetThresh,
+        jfloat dbSnrThresh, jshortArray jpcm) {
+    float ret[3];
+    int numSamples = env->GetArrayLength(jpcm);
+    short *pcm = new short[numSamples];
+    env->GetShortArrayRegion(jpcm, 0, numSamples, pcm);
+
+    GlitchTest gt;
+    gt.init(sampleRate, stimFreq, onsetThresh, dbSnrThresh);
+    float duration = -1.0;
+    int badFrames = -1;
+    int success = gt.checkToneSnr(pcm, numSamples, &duration, &badFrames);
+    ret[0] = badFrames;
+    ret[1] = success;
+    ret[2] = duration;
+    jfloatArray ja = env->NewFloatArray(3);
+    env->SetFloatArrayRegion(ja, 0, 3, ret);
+    return ja;
+}
+
+/* Returns an array of seven floats.
+   ret[0] = num deltas
+   ret[1] = error code
+   ret[2] = duration
+   ret[3] = onset
+   ret[4] = offset
+   ret[5] = max peak
+   ret[6] = min peak
+   Error code = 1 for success, -1 for failure. */
+JNIEXPORT jfloatArray JNICALL
+    Java_com_android_cts_verifier_audioquality_Native_overflowCheck(
+        JNIEnv *env, jobject obj,
+        jshortArray jpcm, jfloat sampleRate) {
+    float ret[7];
+    int numSamples = env->GetArrayLength(jpcm);
+    short *pcm = new short[numSamples];
+    env->GetShortArrayRegion(jpcm, 0, numSamples, pcm);
+
+    float duration = -1.0;
+    int numDeltas = -1, onset = -1, offset = -1;
+    int maxPeak = 0, minPeak = 0;
+    int success = overflowCheck(pcm, numSamples, sampleRate,
+            &duration, &numDeltas, &onset, &offset, &maxPeak, &minPeak);
+    ret[0] = numDeltas;
+    ret[1] = success ? 1 : -1;
+    ret[2] = duration;
+    ret[3] = onset;
+    ret[4] = offset;
+    ret[5] = maxPeak;
+    ret[6] = minPeak;
+    jfloatArray ja = env->NewFloatArray(7);
+    env->SetFloatArrayRegion(ja, 0, 7, ret);
+    return ja;
+}
+
+/* Returns an array of three floats.
+   ret[0] = max deviation,
+   ret[1] = error code,
+   ret[2] = rms deviation.
+   Error code = 1 for success, -1 for failure. */
+JNIEXPORT jfloatArray JNICALL
+    Java_com_android_cts_verifier_audioquality_Native_compareSpectra(
+        JNIEnv *env, jobject obj,
+        jshortArray jpcm, jshortArray jrefPcm, jfloat sampleRate) {
+    float ret[3];
+    int numSamples = env->GetArrayLength(jpcm);
+    short *pcm = new short[numSamples];
+    env->GetShortArrayRegion(jpcm, 0, numSamples, pcm);
+    int nRefSamples = env->GetArrayLength(jrefPcm);
+    short *refPcm = new short[nRefSamples];
+    env->GetShortArrayRegion(jrefPcm, 0, nRefSamples, refPcm);
+
+    float maxDeviation = -1.0, rmsDeviation = -1.0;
+    int success = compareSpectra(pcm, numSamples, refPcm, nRefSamples,
+            sampleRate, &maxDeviation, &rmsDeviation);
+    ret[1] = success ? 1 : -1;
+
+    ret[0] = maxDeviation;
+    ret[2] = rmsDeviation;
+    jfloatArray ja = env->NewFloatArray(3);
+    env->SetFloatArrayRegion(ja, 0, 3, ret);
+    return ja;
+}
+
+/* Return maximum deviation from linearity in dB.
+   On failure returns:
+      -1.0 The input signals or sample counts are missing.
+      -2.0 The number of input signals is < 2.
+      -3.0 The specified sample rate is <= 4000.0
+      -4.0 The dB step size for the increase in stimulus level is <= 0.0
+      -5.0 The specified reverence stimulus number is out of range.
+      -6.0 One or more of the stimuli is too short in duration.
+*/
+JNIEXPORT jfloat JNICALL
+    Java_com_android_cts_verifier_audioquality_Native_linearityTest(
+        JNIEnv *env, jobject obj,
+        jobjectArray jpcms,
+        jfloat sampleRate, jfloat dbStepSize, jint referenceStim) {
+    int numSignals = env->GetArrayLength(jpcms);
+    int *sampleCounts = new int[numSignals];
+    short **pcms = new shortPtr[numSignals];
+    jshortArray ja;
+    for (int i = 0; i < numSignals; i++) {
+        ja = (jshortArray) env->GetObjectArrayElement(jpcms, i);
+        sampleCounts[i] = env->GetArrayLength(ja);
+        pcms[i] = new short[sampleCounts[i]];
+        env->GetShortArrayRegion(ja, 0, sampleCounts[i], pcms[i]);
+    }
+
+    float maxDeviation = -1.0;
+    int ret = linearityTest(pcms, sampleCounts, numSignals,
+            sampleRate, dbStepSize, referenceStim, &maxDeviation);
+    delete[] sampleCounts;
+    for (int i = 0; i < numSignals; i++) {
+        delete[] pcms[i];
+    }
+    delete[] pcms;
+    if (ret < 1) return ret;
+
+    return maxDeviation;
+}
diff --git a/apps/CtsVerifier/tests/Android.mk b/apps/CtsVerifier/jni/verifier/Android.mk
similarity index 71%
copy from apps/CtsVerifier/tests/Android.mk
copy to apps/CtsVerifier/jni/verifier/Android.mk
index b9572fb..98a4678 100644
--- a/apps/CtsVerifier/tests/Android.mk
+++ b/apps/CtsVerifier/jni/verifier/Android.mk
@@ -17,18 +17,16 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_MODULE := libctsverifier_jni
+
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_PRELINK_MODULE := false
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_SRC_FILES := \
+		CtsVerifierJniOnLoad.cpp \
+		com_android_cts_verifier_os_FileUtils.cpp	
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 
-LOCAL_PACKAGE_NAME := CtsVerifierTests
-
-LOCAL_INSTRUMENTATION_FOR := CtsVerifier
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/apps/CtsVerifier/jni/CtsVerifierJniOnLoad.cpp b/apps/CtsVerifier/jni/verifier/CtsVerifierJniOnLoad.cpp
similarity index 100%
rename from apps/CtsVerifier/jni/CtsVerifierJniOnLoad.cpp
rename to apps/CtsVerifier/jni/verifier/CtsVerifierJniOnLoad.cpp
diff --git a/apps/CtsVerifier/jni/com_android_cts_verifier_os_FileUtils.cpp b/apps/CtsVerifier/jni/verifier/com_android_cts_verifier_os_FileUtils.cpp
similarity index 100%
rename from apps/CtsVerifier/jni/com_android_cts_verifier_os_FileUtils.cpp
rename to apps/CtsVerifier/jni/verifier/com_android_cts_verifier_os_FileUtils.cpp
diff --git a/apps/CtsVerifier/res/drawable-hdpi/fs_error.png b/apps/CtsVerifier/res/drawable-hdpi/fs_error.png
new file mode 100644
index 0000000..8270104
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/fs_error.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-hdpi/fs_good.png b/apps/CtsVerifier/res/drawable-hdpi/fs_good.png
new file mode 100644
index 0000000..7786ac7
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/fs_good.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-hdpi/fs_indeterminate.png b/apps/CtsVerifier/res/drawable-hdpi/fs_indeterminate.png
new file mode 100644
index 0000000..68f51ba
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/fs_indeterminate.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-hdpi/fs_warning.png b/apps/CtsVerifier/res/drawable-hdpi/fs_warning.png
new file mode 100644
index 0000000..12baca5
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/fs_warning.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-hdpi/icon.png b/apps/CtsVerifier/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..98a3246
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-ldpi/icon.png b/apps/CtsVerifier/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..bece552
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-mdpi/icon.png b/apps/CtsVerifier/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..fee6113
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable/detective.png b/apps/CtsVerifier/res/drawable/detective.png
new file mode 100644
index 0000000..f9417cc
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable/detective.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable/gray_bubble.xml b/apps/CtsVerifier/res/drawable/gray_bubble.xml
new file mode 100644
index 0000000..66eda76
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable/gray_bubble.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+  <solid android:color="#333333" />
+  <stroke android:width="2dip" android:color="#666666" />
+  <corners android:radius="10dip"/>
+</shape>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/aq_row.xml b/apps/CtsVerifier/res/layout/aq_row.xml
new file mode 100644
index 0000000..5db33f3
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/aq_row.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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="horizontal"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:background="#FF000000"
+    >
+    <TextView
+        android:id="@+id/testName"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="22px"
+        android:textColor="#FFFFFF"
+        android:layout_margin="10px"
+        />
+    <TextView
+        android:id="@+id/testScore"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:gravity="right"
+        android:textSize="22px"
+        android:textColor="#FFFFFF"
+        android:layout_margin="10px"
+        />
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/aq_sound_level_meter.xml b/apps/CtsVerifier/res/layout/aq_sound_level_meter.xml
new file mode 100644
index 0000000..c3cc8f9
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/aq_sound_level_meter.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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:gravity="center_horizontal"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:background="#FF000000"
+    >
+    <TextView
+        android:id="@+id/label"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20px"
+        android:gravity="center_horizontal"
+        android:textSize="18px"
+        android:textColor="#FFFFFF"
+        android:text="@string/aq_calibrate_volume_instructions"
+        />
+    <ProgressBar
+        android:id="@+id/slider"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:max="2000"
+        android:progress="1000"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:padding="10px"
+        />
+    <TextView
+        android:id="@+id/status"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:textSize="18px"
+        android:textColor="#FFFFFF"
+        />
+    <Button
+        android:id="@+id/doneButton"
+        android:text="@string/aq_done"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20px"
+        />
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/aq_verifier_activity.xml b/apps/CtsVerifier/res/layout/aq_verifier_activity.xml
new file mode 100644
index 0000000..3961e92
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/aq_verifier_activity.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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:gravity="center_horizontal"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:background="#FF000000"
+    >
+    <FrameLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1.0"
+        >
+        <ListView
+            android:id="@+id/list"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            />
+        <ProgressBar
+            android:id="@+id/progress"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:indeterminate="true"
+            android:layout_gravity="center"
+            android:visibility="invisible"
+            />
+    </FrameLayout>
+
+    <LinearLayout
+        android:gravity="center_horizontal"
+        android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10px"
+        android:layout_marginBottom="10px"
+        >
+        <Button
+            android:id="@+id/calibrateButton"
+            android:text="@string/aq_calibrate"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            />
+        <Button
+            android:id="@+id/runAllButton"
+            android:text="@string/aq_run_all"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            />
+        <Button
+            android:id="@+id/stopButton"
+            android:text="@string/aq_stop"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            />
+        <Button
+            android:id="@+id/viewResultsButton"
+            android:text="@string/aq_view_results"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            />
+        <Button
+            android:id="@+id/clearButton"
+            android:text="@string/aq_clear"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            />
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/aq_view_results.xml b/apps/CtsVerifier/res/layout/aq_view_results.xml
new file mode 100644
index 0000000..b8f020d
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/aq_view_results.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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:gravity="center_horizontal"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:background="#FF000000"
+    >
+    <ScrollView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1.0"
+        >
+        <TextView
+            android:id="@+id/textView"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:textSize="18px"
+            android:textColor="#FFFFFF"
+            />
+    </ScrollView>
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10px"
+        android:layout_marginBottom="10px"
+        >
+        <Button
+            android:id="@+id/dismissButton"
+            android:text="@string/aq_dismiss"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            />
+        <Button
+            android:id="@+id/sendResultsButton"
+            android:text="@string/aq_email_results"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            />
+    </LinearLayout>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/bt_device_name.xml b/apps/CtsVerifier/res/layout/bt_device_name.xml
new file mode 100644
index 0000000..c37eece
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/bt_device_name.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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:padding="5dp"
+        />
diff --git a/apps/CtsVerifier/res/layout/bt_device_picker.xml b/apps/CtsVerifier/res/layout/bt_device_picker.xml
new file mode 100644
index 0000000..ecca0e5
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/bt_device_picker.xml
@@ -0,0 +1,75 @@
+<?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:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        >
+
+    <TextView android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/bt_paired_devices"
+            style="?android:attr/listSeparatorTextViewStyle"
+            />
+    <FrameLayout android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            >
+        <ListView android:id="@+id/bt_paired_devices"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                />
+        <TextView android:id="@+id/bt_empty_paired_devices"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:text="@string/bt_no_devices"
+                android:visibility="gone"
+                />
+    </FrameLayout>
+
+    <TextView android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/bt_new_devices"
+            style="?android:attr/listSeparatorTextViewStyle"
+            />
+    <FrameLayout android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            >
+        <ListView android:id="@+id/bt_new_devices"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                />
+        <TextView android:id="@+id/bt_empty_new_devices"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:text="@string/bt_no_devices"
+                android:visibility="gone"
+                />
+    </FrameLayout>
+            
+    <Button android:id="@+id/bt_scan_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:drawableTop="@android:drawable/ic_menu_search"
+            android:text="@string/bt_scan"            
+            />
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/bt_main.xml b/apps/CtsVerifier/res/layout/bt_main.xml
new file mode 100644
index 0000000..cb65412
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/bt_main.xml
@@ -0,0 +1,30 @@
+<?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="match_parent"
+            android:layout_weight="1"
+            />
+
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/bt_message_row.xml b/apps/CtsVerifier/res/layout/bt_message_row.xml
new file mode 100644
index 0000000..c37eece
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/bt_message_row.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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:padding="5dp"
+        />
diff --git a/apps/CtsVerifier/res/layout/bt_messages.xml b/apps/CtsVerifier/res/layout/bt_messages.xml
new file mode 100644
index 0000000..cb46811
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/bt_messages.xml
@@ -0,0 +1,74 @@
+<?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"
+        >
+    <TextView android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/bt_sent_messages"
+            style="?android:attr/listSeparatorTextViewStyle"
+            />
+    <FrameLayout android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            >
+        <ListView android:id="@+id/bt_sent_messages"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                />
+        <TextView android:id="@+id/bt_empty_sent_messages"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:visibility="gone"
+                />
+    </FrameLayout>
+
+    <TextView android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/bt_received_messages"
+            style="?android:attr/listSeparatorTextViewStyle"
+            />
+    <FrameLayout android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            >
+        <ListView android:id="@+id/bt_received_messages"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                />
+        <TextView android:id="@+id/bt_empty_received_messages"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:visibility="gone"
+                />
+    </FrameLayout>
+
+    <Button android:id="@+id/bt_make_discoverable_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:drawableTop="@android:drawable/ic_menu_mylocation"
+            android:text="@string/bt_make_discoverable"
+            />
+            
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/bt_toggle.xml b/apps/CtsVerifier/res/layout/bt_toggle.xml
new file mode 100644
index 0000000..3a07514
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/bt_toggle.xml
@@ -0,0 +1,44 @@
+<?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:orientation="vertical"
+        android:padding="10dip"
+        >
+    <TextView android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:gravity="center"
+            android:text="@string/bt_toggle_instructions"
+            android:textSize="24dip"
+            />
+
+    <ToggleButton android:id="@+id/bt_toggle_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:textOn="@string/bt_disable_bluetooth"
+            android:textOff="@string/bt_enable_bluetooth"
+            />
+            
+    <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/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 479cc1b..cdfb8f4 100644
--- a/apps/CtsVerifier/res/layout/main.xml
+++ b/apps/CtsVerifier/res/layout/main.xml
@@ -18,18 +18,39 @@
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     >
-    <TextView  
-        android:id="@+id/welcome"
-        android:layout_width="fill_parent" 
-        android:layout_height="wrap_content"
-        android:text="@string/welcome_text"
-        />
     <Button
         android:id="@+id/continue_button"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
-        android:onClick="continueButtonClickHandler"
         android:text="@string/continue_button_text"
         />
+    <TextView
+        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: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"
+        />
+    <ImageView
+        android:id="@+id/detective_logo"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_above="@+id/welcome_text"
+        android:padding="10dip"
+        android:src="@drawable/detective"
+        />
 </RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/pass_fail_buttons.xml b/apps/CtsVerifier/res/layout/pass_fail_buttons.xml
index 5c64e31..d70e839 100644
--- a/apps/CtsVerifier/res/layout/pass_fail_buttons.xml
+++ b/apps/CtsVerifier/res/layout/pass_fail_buttons.xml
@@ -23,7 +23,6 @@
             android:layout_height="wrap_content"
             android:layout_weight="1"            
             android:drawableTop="@drawable/fs_good"
-            android:onClick="passFailButtonsClickHandler"
             android:text="@string/pass_button_text"/>
             
     <Button android:id="@+id/info_button"
@@ -39,7 +38,6 @@
             android:layout_height="wrap_content"
             android:layout_weight="1"            
             android:drawableTop="@drawable/fs_error"
-            android:onClick="passFailButtonsClickHandler"
             android:text="@string/fail_button_text"/>
             
 </LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/snsr_gyro.xml b/apps/CtsVerifier/res/layout/snsr_gyro.xml
new file mode 100644
index 0000000..d4d7000
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/snsr_gyro.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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">
+
+    <include android:id="@+id/pass_fail_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons" />
+
+    <android.opengl.GLSurfaceView android:id="@+id/gl_surface_view"
+            android:layout_alignParentTop="true"
+            android:layout_above="@+id/pass_fail_buttons"
+            android:layout_marginBottom="5dip"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+    <TextView android:id="@+id/sensor_value"
+            android:background="@drawable/gray_bubble"
+            android:drawablePadding="10dip"
+            android:layout_above="@+id/pass_fail_buttons"
+            android:layout_centerInParent="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="10dip"
+            android:paddingLeft="10dip"
+            android:paddingRight="10dip"
+            android:paddingTop="5dip"
+            android:paddingBottom="5dip"
+            android:textSize="28dip"
+            />
+
+    <TextView android:id="@+id/progress"
+            android:background="@drawable/gray_bubble"
+            android:layout_alignParentTop="true"
+            android:layout_centerInParent="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dip"
+            android:paddingLeft="10dip"
+            android:paddingRight="10dip"
+            android:paddingTop="5dip"
+            android:paddingBottom="5dip"
+            android:textSize="28dip"
+            />
+    
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/menu/test_list_menu.xml b/apps/CtsVerifier/res/menu/test_list_menu.xml
index 713455c..495e36f 100644
--- a/apps/CtsVerifier/res/menu/test_list_menu.xml
+++ b/apps/CtsVerifier/res/menu/test_list_menu.xml
@@ -3,7 +3,10 @@
     <item android:id="@+id/clear"
           android:icon="@android:drawable/ic_menu_delete" 
           android:title="@string/clear" />
-    <item android:id="@+id/share"
+    <item android:id="@+id/copy"
+          android:icon="@android:drawable/ic_menu_upload"
+          android:title="@string/copy" />
+    <item android:id="@+id/export"
           android:icon="@android:drawable/ic_menu_share"
-          android:title="@string/share" />
+          android:title="@string/export" />
 </menu>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/colors.xml b/apps/CtsVerifier/res/values/colors.xml
new file mode 100644
index 0000000..220ba29
--- /dev/null
+++ b/apps/CtsVerifier/res/values/colors.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* 
+**
+** Copyright 2008, 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>
+    <color name="green">#00FF00</color>
+    <color name="red">#FF0000</color>
+</resources>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index c386a1b..0f0f645 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,22 +24,111 @@
     <string name="fail_button_text">Fail</string>
 
     <!-- Strings for TestResultsReport -->
-    <string name="subject_header">[CtsVerifier %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>
     <string name="test_category_features">Features</string>
     <string name="test_category_other">Other</string>
     <string name="clear">Clear</string>
     <string name="test_results_cleared">Test results cleared.</string>
-    <string name="share">Share</string>
-    <string name="share_test_results">Share Test Results</string>
+    <string name="copy">Copy</string>
+    <string name="test_results_copied">Test results copied to clipboard.</string>
+    <string name="test_results_error">Couldn\'t create test results report.</string>
+    <string name="export">Export</string>
+    <string name="no_storage">Cannot save report to external storage, see log for details.</string>
+    <string name="report_saved">Report saved to: %s</string>
+
+    <!-- Strings for Device Administration tests -->
+    <string name="da_policy_serialization_test">Policy Serialization Test</string>
+    <string name="da_policy_serialization_info">This test checks that a device policy is properly
+        saved and loaded across reboots.\n\nPress the \"Generate Policy\" button to create
+        a random policy. Then press the \"Apply Policy\" button to apply the policy. Reboot the
+        device and verify that all rows in the policy list are green. Red items indicate policy
+        settings that were not loaded properly.
+    </string>
+    <string name="da_no_policy">1. Press the \"Generate Policy\" to create a random device
+        policy\n\n2. Press \"Apply Policy\" to put the policy into effect.\n\n3. Reboot your
+        device and return to this test in the CTS Verifier.
+    </string>
+    <string name="da_generate_policy">Generate Policy</string>
+    <string name="da_apply_policy">Apply Policy</string>
+    <string name="da_random_policy">Random policy generated.</string>
+    <string name="da_policy_reboot">Reboot your device and return to this CTS Verifier test.</string>
+    <string name="da_password_quality">Password Quality</string>
+    <string name="da_password_quality_alphabetic">Alphabetic</string>
+    <string name="da_password_quality_alphanumeric">Alphanumeric</string>
+    <string name="da_password_quality_numeric">Numeric</string>
+    <string name="da_password_quality_something">Something</string>
+    <string name="da_password_quality_unspecified">Unspecified</string>
+    <string name="da_password_minimum_length">Minimum Password Length</string>
+    <string name="da_maximum_failed_passwords_for_wipe">Maximum Failed Passwords for Wipe</string>
+    <string name="da_maximum_time_to_lock">Maximum Time to Lock</string>
+    <string name="da_policy_info">Expected value: %1$s\nActual value: %2$s</string>
+
+    <string name="da_screen_lock_test">Screen Lock Test</string>
+    <string name="da_screen_lock_info">This test checks that the DevicePolicyManager\'s lockNow
+        method immediately locks the screen. It should lock the screen immediately despite any
+        settings that may specify a timeout.\n\nClick the \"Force Lock\" button to change
+        your password to \"12345\" and lock the screen. Your screen should be locked and require
+        the password to be entered.
+    </string>
+    <string name="da_force_lock">Force Lock</string>
+    <string name="da_lock_password_error">Couldn\'t change the password on the device.
+        Is there another active device administrator?</string>
+    <string name="da_lock_success">It appears the screen was locked successfully!</string>
+    <string name="da_lock_error">It does not look like the screen was locked...</string>
+
+    <!-- Strings for BluetoothActivity -->
+    <string name="bluetooth_test">Bluetooth Test</string>
+    <string name="bluetooth_test_info">The Bluetooth Control tests check whether or not the device 
+        can disable and enable Bluetooth properly.\n\nThe Device Communication tests require two
+        devices to pair and exchange messages. The two devices must be:
+        \n\n1. a candidate device implementation running the software build to be tested
+        \n\n2. a separate device implementation already known to be compatible</string>
+
+    <string name="bt_not_available_title">Bluetooth is not available?</string>
+    <string name="bt_not_available_message">If your device is supposed to have Bluetooth, your API implementation is not behaving correctly!</string>
+
+    <string name="bt_control">Bluetooth Control</string>
+    <string name="bt_device_communication">Device Communication</string>
+
+    <string name="bt_toggle_bluetooth">Toggle Bluetooth</string>
+    <string name="bt_toggle_instructions">Disable and enable Bluetooth to successfully complete this test.</string>
+    <string name="bt_enable_bluetooth">Enable Bluetooth</string>
+    <string name="bt_disable_bluetooth">Disable Bluetooth</string>
+    <string name="bt_disabling">Disabling Bluetooth...</string>
+    <string name="bt_disabling_error">Could not disable Bluetooth...</string>
+
+    <string name="bt_secure_server">Secure Server</string>
+    <string name="bt_secure_server_instructions">Start the CTS Verifier on another device, start the Bluetooth test, and choose \"Secure Client\" to complete the test.</string>
+    <string name="bt_insecure_server">Insecure Server</string>
+    <string name="bt_insecure_server_instructions">Start the CTS Verifier on another device, start the Bluetooth test, and choose \"Insecure Client\" to complete the test.</string>
+    <string name="bt_waiting">Waiting for client...</string>
+    <string name="bt_connecting">Connecting...</string>
+    <string name="bt_received_messages">Received Messages</string>
+    <string name="bt_sent_messages">Sent Messages</string>
+    <string name="bt_no_messages">No messages</string>
+    <string name="bt_make_discoverable">Make Discoverable</string>
+    <string name="bt_insecure_pairing_error_title">Pairing dialog shown?</string>
+    <string name="bt_insecure_pairing_error_message">Insecure connections should not show the pairing dialog!</string>
+
+    <string name="bt_secure_client">Secure Client</string>
+    <string name="bt_insecure_client">Insecure Client</string>
+
+    <string name="bt_device_picker">Device Picker</string>
+    <string name="bt_paired_devices">Paired Devices</string>
+    <string name="bt_new_devices">New Devices</string>
+    <string name="bt_no_devices">No devices</string>
+    <string name="bt_scan">Scan for Devices</string>
+    <string name="bt_scanning">Scanning...</string>
+    <string name="bt_unpair">Device must be unpaired via Bluetooth settings before completing the test.\n\nUnpair the device in settings, make the server discoverable, and rescan to pick this device.</string>
+    <string name="bt_settings">Bluetooth Settings</string>
 
     <!-- Strings for FeatureSummaryActivity -->
     <string name="feature_summary">Hardware/Software Feature Summary</string>
@@ -53,11 +143,17 @@
 
     <string name="empty"></string>
 
-    <!-- Strings for AccelerometerTestActivity and MagnetometerTestActivity -->
+    <!-- Strings for AccelerometerTestActivity and GyroscopeTestActivity -->
     <string name="snsr_accel_test">Accelerometer Test</string>
     <string name="snsr_accel_test_info">This test verifies that the accelerometer is working properly. As you move the device around through space, the triangle should always point down (i.e. in the direction of gravity.) If it does not, the accelerometer is improperly configured.</string>
-    <string name="snsr_mag_test">Magnetometer Test</string>
-    <string name="snsr_mag_test_info">This test verifies that the magnetometer (compass) is working properly. As you move the device around through space, the triangle should always point toward the north pole (which may point through the ground.) If it does not, the magnetometer is improperly configured. Be sure not to run this test with the device near any strong magnetic field generators.</string>
+
+    <string name="snsr_gyro_test">Gyroscope Test</string>
+    <string name="snsr_gyro_test_info">This test verifies that the gyroscope is working properly.\n\nRotate your device as shown by the 3D block. A green background or a check mark indicates that the gyroscope\'s value is correct. A red background or a X mark indicates that the gyroscope\'s value is not right.\n\nThere are 6 parts of the test corresponding to each rotation. Press Pass for all the stages to complete this test.</string>
+    <string name="snsr_gyro_test_progress">Test %1$d of %2$d</string>
+    <string name="snsr_gyro_test_no_gyro_title">No gyroscope?</string>
+    <string name="snsr_gyro_test_no_gyro_message">It doesn\'t seem like you have a gyroscope, so you don\'t need to run this test.</string>
+    <string name="snsr_gyro_test_degrees_title">Wrong units?</string>
+    <string name="snsr_gyro_test_degrees_message">These values looks like degrees per second. These should be radians per second!</string>
 
     <!-- Strings for SuidFilesActivity -->
     <string name="suid_files">SUID File Scanner</string>
@@ -67,4 +163,96 @@
     <string name="no_file_status">Could not stat file...</string>
     <string name="congratulations">Congratulations!</string>
     <string name="no_suid_files">No unauthorized suid files detected!</string>
+
+    <!--  Strings for Audio Quality Verifier -->
+    
+     <!-- Title for Audio Quality Verifier activity -->
+    <string name="aq_verifier">Audio Quality Verifier</string>
+    <string name="aq_verifier_info">
+        1. Click \"Calibrate\". Position the phone in front of the center of
+           the speaker cone with the microphone facing the speaker,
+           and adjust the volume of the speaker until the status message
+           indicates it is correct.
+        \n\n2. Click on any test in the list to run it, or \"Run All\" to run
+           each test in sequence.
+        \n\nIf the sound level check fails, go back to the calibration step before
+            running any other test.
+        \n\n3. Click \"Results\" to view the outcomes. A correctly functioning
+           device should pass all tests.
+        \n\n4. Click \"Send by email\" from the results page to send the
+           results to an e-mail address of your choice. The recordings
+           made are also attached as raw 16 bit, 16 kHz audio files to
+           help you diagnose any failed tests.
+    </string>
+    
+    <!-- Button labels for VerifierActivity -->
+    <string name="aq_calibrate">Calibrate</string>
+    <string name="aq_run_all">Run All</string>
+    <string name="aq_stop">Stop</string>
+    <string name="aq_view_results">Results</string>
+    <string name="aq_email_results">Send by email</string>
+    <string name="aq_clear">Clear</string>
+    
+    <!-- Title for ViewResultsActivity -->
+    <string name="aq_view_results_name">Audio Quality Results</string>
+    <!-- Button label for ViewResultsActivity -->
+    <string name="aq_dismiss">Dismiss</string>
+    <!-- E-mail subject line for test results -->
+    <string name="aq_subject">Android Audio Quality Verifier Test Results</string>
+    
+    <!--  Title for CalibrateVolumeActivity -->
+    <string name="aq_calibrate_volume_name">Calibrate Volume</string>
+    <!--  Instructions for calibrating the volume -->
+    <string name="aq_calibrate_volume_instructions">Adjust volume to the central point</string>
+    <!-- Button label for CalibrateVolumeActivity -->
+    <string name="aq_done">Done</string>
+    <!--  Status values for CalibrateVolumeActivity -->
+    <string name="aq_status_unknown">Status: unknown</string>
+    <string name="aq_status_low">Volume too low</string>
+    <string name="aq_status_high">Volume too high</string>
+    <string name="aq_status_ok">Volume OK</string>
+    
+    <!-- Experiment names -->
+    <string name="aq_default_exp">Unnamed experiment</string>
+    <string name="aq_sound_level_exp">Sound level check</string>
+    <string name="aq_spectrum_shape_exp">Spectrum shape test</string>
+    <string name="aq_glitch_exp">Glitch test</string>
+    <string name="aq_linearity_exp">Gain linearity test</string>
+    <string name="aq_overflow_exp">Overflow check</string>
+    <string name="aq_bias_exp">Bias measurement</string>
+    <string name="aq_cold_latency">Cold recording latency</string>
+    <string name="aq_warm_latency">Warm recording latency</string>
+    
+    <!-- Experiment outcomes -->
+    <string name="aq_fail">Fail</string>
+    <string name="aq_pass">Pass</string>
+    <string name="aq_complete">Complete</string>
+    
+    <!-- Experiment reports -->
+    <string name="aq_loopback_report">Experiment ran successfully.</string>
+    <string name="aq_bias_report">Mean = %1$.3g, tolerance = +/- %2$.0f\nRMS = %3$.0f, duration = %4$.1fs</string>
+    <string name="aq_overflow_report_error">Overflow check unsuccessful</string>
+    <string name="aq_overflow_report_short">Insufficient tone detected.\nExpected %1$.1fs tone; observed %2$.1fs</string>
+    <string name="aq_overflow_report_fail">"Overflow check failed due to discontinuities.\nObserved %1$d bad frames\nTone duration %2$.1fs\nMin peak = %3$.0f, max = %4$.0f</string>
+    <string name="aq_overflow_report_pass">"Observed %1$d bad frames\nTone duration %2$.1fs\nMin peak = %3$.0f, max = %4$.0f</string>
+    <string name="aq_linearity_report_error">Experiment failed, error code %1$g</string>
+    <string name="aq_linearity_report_normal">Deviation from linearity = %1$.3g dB\nMax allowed = %2$.1f dB</string>
+    <string name="aq_glitch_report_error">Error performing Glitch test.</string>
+    <string name="aq_glitch_report_exact">%1$d glitches detected; expected %2$d, duration %3$.1fs</string>
+    <string name="aq_glitch_report_range">%1$d glitches detected; expected %2$d-%3$d, duration %4$.1fs</string>
+    <string name="aq_level_report">RMS = %1$.0f, target = %2$.0f\nTolerance = %3$.1f%%\nDuration = %4$.1fs</string>
+    <string name="aq_spectrum_report_error">Cannot perform test.\nCheck volume is sufficiently high?</string>
+    <string name="aq_spectrum_report_normal">RMS deviation = %1$.2f\nMax allowed deviation = %2$.1f</string>
+    <string name="aq_cold_latency_report">Latency = %1$dms, maximum allowed = %2$dms</string>
+    <string name="aq_warm_latency_report_error">RMS = %1$.0f, target = %2$.0f</string>
+    <string name="aq_warm_latency_report_normal">Latency = %1$dms</string>
+    
+    <!-- General experiment messages -->    
+    <string name="aq_audiorecord_buffer_size_error">Error getting minimum AudioRecord buffer size: %1$d</string>
+    <string name="aq_audiotrack_buffer_size_error">Error getting minimum AudioTrack buffer size: %1$d</string>
+    <string name="aq_init_audiorecord_error">Error initializing AudioRecord instance</string>
+    <string name="aq_init_audiotrack_error">Error initializing AudioTrack instance</string>
+    <string name="aq_recording_error">Error reading data from AudioRecord instance</string>
+    <string name="aq_exception_error">Exception thrown during test: %1$s</string>
+
 </resources>
diff --git a/apps/CtsVerifier/res/values/styles.xml b/apps/CtsVerifier/res/values/styles.xml
new file mode 100644
index 0000000..c7fe859
--- /dev/null
+++ b/apps/CtsVerifier/res/values/styles.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <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 9c0566e..ee3184b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierActivity.java
@@ -20,6 +20,9 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
+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 {
@@ -28,10 +31,20 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
         setContentView(R.layout.main);
-    }
 
-    public void continueButtonClickHandler(View target) {
-        startActivity(new Intent(this, TestListActivity.class));
+        OnClickListener clickListener = new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                startActivity(new Intent(CtsVerifierActivity.this, TestListActivity.class));
+            }
+        };
+
+        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/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
index 1c407b8..2b4661e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
@@ -17,15 +17,18 @@
 package com.android.cts.verifier;
 
 import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnCancelListener;
 import android.database.Cursor;
+import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.Button;
 
 /**
  * {@link Activity}s to handle clicks to the pass and fail buttons of the pass fail buttons layout.
@@ -34,7 +37,8 @@
  *     <li>Include the pass fail buttons layout in your layout:
  *         <pre><include layout="@layout/pass_fail_buttons" /></pre>
  *     </li>
- *     <li>Extend one of the activities to get the click handler for the buttons.</li>
+ *     <li>Extend one of the activities and call setPassFailButtonClickListeners after
+ *         setting your content view.</li>
  *     <li>Make sure to call setResult(RESULT_CANCEL) in your Activity initially.</li>
  *     <li>Optionally call setInfoTextResources to add an info button that will show a
  *         dialog with instructional text.</li>
@@ -42,10 +46,23 @@
  */
 public class PassFailButtons {
 
+    private static final int INFO_DIALOG_ID = 1337;
+
+    private static final String INFO_DIALOG_VIEW_ID = "infoDialogViewId";
+    private static final String INFO_DIALOG_TITLE_ID = "infoDialogTitleId";
+    private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId";
+
     // Interface mostly for making documentation and refactoring easier...
     private interface PassFailActivity {
 
         /**
+         * Hooks up the pass and fail buttons to click listeners that will record the test results.
+         * <p>
+         * Call from {@link Activity#onCreate} after {@link Activity #setContentView(int)}.
+         */
+        void setPassFailButtonClickListeners();
+
+        /**
          * Adds an initial informational dialog that appears when entering the test activity for
          * the first time. Also enables the visibility of an "Info" button between the "Pass" and
          * "Fail" buttons that can be clicked to show the information dialog again.
@@ -57,33 +74,68 @@
          */
         void setInfoResources(int titleId, int messageId, int viewId);
 
-        /**
-         * Click handler for the pass and fail buttons. No need to call this ever as the XML
-         * view layout will bind to this automatically.
-         */
-        void passFailButtonsClickHandler(View target);
+        Button getPassButton();
+
+        /* Added to the interface just to make sure it isn't forgotten in the implementations. */
+        Dialog onCreateDialog(int id, Bundle args);
     }
 
     public static class Activity extends android.app.Activity implements PassFailActivity {
 
+        @Override
+        public void setPassFailButtonClickListeners() {
+            setPassFailClickListeners(this);
+        }
+
+        @Override
         public void setInfoResources(int titleId, int messageId, int viewId) {
             setInfo(this, titleId, messageId, viewId);
         }
 
-        public void passFailButtonsClickHandler(View target) {
-            setTestResultAndFinish(this, target);
+        @Override
+        public Button getPassButton() {
+            return getPassButtonView(this);
+        }
+
+        @Override
+        public Dialog onCreateDialog(int id, Bundle args) {
+            return createDialog(this, id, args);
         }
     }
 
     public static class ListActivity extends android.app.ListActivity implements PassFailActivity {
 
+        @Override
+        public void setPassFailButtonClickListeners() {
+            setPassFailClickListeners(this);
+        }
+
+        @Override
         public void setInfoResources(int titleId, int messageId, int viewId) {
             setInfo(this, titleId, messageId, viewId);
         }
 
-        public void passFailButtonsClickHandler(View target) {
-            setTestResultAndFinish(this, target);
+        @Override
+        public Button getPassButton() {
+            return getPassButtonView(this);
         }
+
+        @Override
+        public Dialog onCreateDialog(int id, Bundle args) {
+            return createDialog(this, id, args);
+        }
+    }
+
+    private static void setPassFailClickListeners(final android.app.Activity activity) {
+        View.OnClickListener clickListener = new View.OnClickListener() {
+            @Override
+            public void onClick(View target) {
+                setTestResultAndFinish(activity, target);
+            }
+        };
+
+        activity.findViewById(R.id.pass_button).setOnClickListener(clickListener);
+        activity.findViewById(R.id.fail_button).setOnClickListener(clickListener);
     }
 
     private static void setInfo(final android.app.Activity activity, final int titleId,
@@ -92,6 +144,7 @@
         View infoButton = activity.findViewById(R.id.info_button);
         infoButton.setVisibility(View.VISIBLE);
         infoButton.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View view) {
                 showInfoDialog(activity, titleId, messageId, viewId);
             }
@@ -120,6 +173,28 @@
 
     private static void showInfoDialog(final android.app.Activity activity, int titleId,
             int messageId, int viewId) {
+        Bundle args = new Bundle();
+        args.putInt(INFO_DIALOG_TITLE_ID, titleId);
+        args.putInt(INFO_DIALOG_MESSAGE_ID, messageId);
+        args.putInt(INFO_DIALOG_VIEW_ID, viewId);
+        activity.showDialog(INFO_DIALOG_ID, args);
+    }
+
+    private static Dialog createDialog(final android.app.Activity activity, int id, Bundle args) {
+        switch (id) {
+            case INFO_DIALOG_ID:
+                return createInfoDialog(activity, id, args);
+            default:
+                throw new IllegalArgumentException("Bad dialog id: " + id);
+        }
+    }
+
+    private static Dialog createInfoDialog(final android.app.Activity activity, int id,
+            Bundle args) {
+        int viewId = args.getInt(INFO_DIALOG_VIEW_ID);
+        int titleId = args.getInt(INFO_DIALOG_TITLE_ID);
+        int messageId = args.getInt(INFO_DIALOG_MESSAGE_ID);
+
         AlertDialog.Builder builder = new AlertDialog.Builder(activity).setIcon(
                 android.R.drawable.ic_dialog_info).setTitle(titleId);
         if (viewId > 0) {
@@ -130,15 +205,17 @@
             builder.setMessage(messageId);
         }
         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
             public void onClick(DialogInterface dialog, int which) {
                 markSeenInfoDialog(activity);
             }
         }).setOnCancelListener(new OnCancelListener() {
+            @Override
             public void onCancel(DialogInterface dialog) {
                 markSeenInfoDialog(activity);
             }
-        })
-    	.show();
+        });
+        return builder.create();
     }
 
     private static void markSeenInfoDialog(android.app.Activity activity) {
@@ -171,4 +248,8 @@
 
         activity.finish();
     }
+
+    private static Button getPassButtonView(android.app.Activity activity) {
+        return (Button) activity.findViewById(R.id.pass_button);
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
new file mode 100644
index 0000000..f7db56d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
@@ -0,0 +1,91 @@
+/*
+ * 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.os.AsyncTask;
+import android.os.Environment;
+import android.widget.Toast;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Background task to generate a report and save it to external storage.
+ */
+class ReportExporter extends AsyncTask<Void, Void, String> {
+    protected static final Logger LOG = Logger.getLogger(ReportExporter.class.getName());
+
+    private final Context mContext;
+    private final TestListAdapter mAdapter;
+
+    ReportExporter(Context context, TestListAdapter adapter) {
+        this.mContext = context;
+        this.mAdapter = adapter;
+    }
+
+    @Override
+    protected String doInBackground(Void... params) {
+        if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+            LOG.log(Level.WARNING, "External storage is not writable.");
+            return mContext.getString(R.string.no_storage);
+        }
+        byte[] contents;
+        try {
+            TestResultsReport report = new TestResultsReport(mContext, mAdapter);
+            contents = report.getContents().getBytes();
+        } catch (Exception e) {
+            LOG.log(Level.WARNING, "Couldn't create test results report", e);
+            return mContext.getString(R.string.test_results_error);
+        }
+        File reportPath = new File(Environment.getExternalStorageDirectory(), "ctsVerifierReports");
+        reportPath.mkdirs();
+        File reportFile = new File(reportPath,
+                "ctsVerifierReport-" + System.currentTimeMillis() + ".zip");
+        ZipOutputStream out = null;
+        try {
+            out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(reportFile)));
+            ZipEntry entry = new ZipEntry("ctsVerifierReport.xml");
+            out.putNextEntry(entry);
+            out.write(contents);
+        } catch (IOException e) {
+            LOG.log(Level.WARNING, "I/O exception writing report to storage.", e);
+            return mContext.getString(R.string.no_storage);
+        } finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+            } catch (IOException e) {
+                LOG.log(Level.WARNING, "I/O exception closing report.", e);
+            }
+        }
+
+        return mContext.getString(R.string.report_saved, reportFile.getPath());
+    }
+
+    @Override
+    protected void onPostExecute(String result) {
+        Toast.makeText(mContext, result, Toast.LENGTH_LONG).show();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
index 8509edb..bc7a2b0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
@@ -19,12 +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;
@@ -32,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. */
@@ -58,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) {
@@ -84,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);
         }
     }
 
@@ -113,8 +96,12 @@
                 handleClearItemSelected();
                 return true;
 
-            case R.id.share:
-                handleShareItemSelected();
+            case R.id.copy:
+                handleCopyItemSelected();
+                return true;
+
+            case R.id.export:
+                handleExportItemSelected();
                 return true;
 
             default:
@@ -123,40 +110,24 @@
     }
 
     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 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)));
+    private void handleCopyItemSelected() {
+        try {
+            TestResultsReport report = new TestResultsReport(this, mAdapter);
+            ClipboardManager clipboardManager = (ClipboardManager)
+                    getSystemService(CLIPBOARD_SERVICE);
+            clipboardManager.setText(report.getContents());
+            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, "Couldn't copy test results report", e);
+        }
     }
 
-    /**
-     * {@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();
-        }
+    private void handleExportItemSelected() {
+        new ReportExporter(this, mAdapter).execute();
     }
 }
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..37d4819 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -18,90 +18,130 @@
 
 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 getContents() throws IllegalArgumentException, IllegalStateException, IOException {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 
-    String getSubject() {
-        return new StringBuilder()
-                .append(mContext.getString(R.string.subject_header, mVersionName))
-                .append(' ')
-                .append(Build.FINGERPRINT)
-                .toString();
-    }
+        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);
 
-    String getBody() {
-        StringBuilder builder = new StringBuilder()
-                .append(mContext.getString(R.string.body_header, mVersionName))
-                .append("\n\n")
-                .append(Build.FINGERPRINT)
-                .append("\n\n");
+        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, "board", Build.BOARD);
+        xml.attribute(null, "brand", Build.BRAND);
+        xml.attribute(null, "device", Build.DEVICE);
+        xml.attribute(null, "display", Build.DISPLAY);
+        xml.attribute(null, "fingerprint", Build.FINGERPRINT);
+        xml.attribute(null, "id", Build.ID);
+        xml.attribute(null, "model", Build.MODEL);
+        xml.attribute(null, "product", Build.PRODUCT);
+        xml.attribute(null, "release", Build.VERSION.RELEASE);
+        xml.attribute(null, "sdk", Build.VERSION.SDK);
+        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..1591df3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java
@@ -0,0 +1,503 @@
+/*
+ * 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
+            };
+
+            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);
+                default:
+                    return Integer.toString(value);
+            }
+        }
+    }
+
+    static class PasswordMinimumLengthPolicy extends IntegerPolicyItem {
+
+        PasswordMinimumLengthPolicy(Context context) {
+            super(context, R.string.da_password_minimum_length, "password-minimum-length");
+        }
+
+        @Override
+        protected Integer getRandomExpectedValue(Random random) {
+            return random.nextInt(50);
+        }
+
+        @Override
+        public void applyExpectedValue(DevicePolicyManager deviceManager, ComponentName admin) {
+            deviceManager.setPasswordMinimumLength(admin, getExpectedValue());
+        }
+
+        @Override
+        protected Integer getDeviceManagerValue(DevicePolicyManager deviceManager,
+                ComponentName admin) {
+            return deviceManager.getPasswordMinimumLength(admin);
+        }
+    }
+
+    static class MaximumFailedPasswordsForWipePolicy extends IntegerPolicyItem {
+
+        MaximumFailedPasswordsForWipePolicy(Context context) {
+            super(context, R.string.da_maximum_failed_passwords_for_wipe,
+                    "maximum-failed-passwords-for-wipe");
+        }
+
+        @Override
+        protected Integer getRandomExpectedValue(Random random) {
+            return random.nextInt(50);
+        }
+
+        @Override
+        public void applyExpectedValue(DevicePolicyManager deviceManager, ComponentName admin) {
+            deviceManager.setMaximumFailedPasswordsForWipe(admin, getExpectedValue());
+        }
+
+        @Override
+        protected Integer getDeviceManagerValue(DevicePolicyManager deviceManager,
+                ComponentName admin) {
+            return deviceManager.getMaximumFailedPasswordsForWipe(admin);
+        }
+    }
+
+    static class MaximumTimeToLockPolicy extends LongPolicyItem {
+
+        MaximumTimeToLockPolicy(Context context) {
+            super(context, R.string.da_maximum_time_to_lock, "maximum-time-to-lock");
+        }
+
+        @Override
+        protected Long getRandomExpectedValue(Random random) {
+            return (long)(1000 + random.nextInt(60 * 60 * 1000));
+        }
+
+        @Override
+        public void applyExpectedValue(DevicePolicyManager deviceManager, ComponentName admin) {
+            deviceManager.setMaximumTimeToLock(admin, getExpectedValue());
+        }
+
+        @Override
+        protected Long getDeviceManagerValue(DevicePolicyManager deviceManager,
+                ComponentName admin) {
+            return deviceManager.getMaximumTimeToLock(admin);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java
new file mode 100644
index 0000000..4d6adf3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.admin;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.app.AlertDialog;
+import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+public class ScreenLockTestActivity extends PassFailButtons.Activity {
+
+    private static final String NEW_PASSWORD = "12345";
+
+    private static final int ADD_DEVICE_ADMIN_REQUEST_CODE = 1;
+
+    private ScreenOffReceiver mReceiver;
+
+    private Button mForceLockButton;
+
+    private DevicePolicyManager mDevicePolicyManager;
+
+    private KeyguardManager mKeyguardManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.da_screen_lock_main);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.da_screen_lock_test, R.string.da_screen_lock_info, -1);
+
+        mDevicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
+        mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
+
+        getPassButton().setEnabled(false);
+
+        mForceLockButton = (Button) findViewById(R.id.da_force_lock_button);
+        mForceLockButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                sendAddDeviceAdminIntent();
+            }
+        });
+
+        mReceiver = new ScreenOffReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        registerReceiver(mReceiver, filter);
+    }
+
+    private void sendAddDeviceAdminIntent() {
+        Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
+        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+                TestDeviceAdminReceiver.getComponent(this));
+        startActivityForResult(intent, ADD_DEVICE_ADMIN_REQUEST_CODE);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case ADD_DEVICE_ADMIN_REQUEST_CODE:
+                handleAddDeviceAdminResult(resultCode, data);
+                break;
+        }
+    }
+
+    private void handleAddDeviceAdminResult(int resultCode, Intent data) {
+        if (resultCode == RESULT_OK) {
+            mDevicePolicyManager.setPasswordMinimumLength(TestDeviceAdminReceiver
+                    .getComponent(this), NEW_PASSWORD.length());
+            mDevicePolicyManager.setPasswordQuality(TestDeviceAdminReceiver
+                    .getComponent(this), DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+            if (mDevicePolicyManager.resetPassword(NEW_PASSWORD, 0)) {
+                mDevicePolicyManager.lockNow();
+            } else {
+                new AlertDialog.Builder(this)
+                    .setTitle(R.string.da_screen_lock_test)
+                    .setMessage(R.string.da_lock_password_error)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setPositiveButton(android.R.string.ok, null)
+                    .show();
+            }
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+    }
+
+    private class ScreenOffReceiver extends BroadcastReceiver {
+
+        private static final int LOCK_CHECK_DELAY = 1000;
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mForceLockButton.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    boolean lockSuccess = mKeyguardManager.inKeyguardRestrictedInputMode();
+                    getPassButton().setEnabled(lockSuccess);
+
+                    int iconId = lockSuccess
+                            ? android.R.drawable.ic_dialog_info
+                            : android.R.drawable.ic_dialog_alert;
+                    int messageId = lockSuccess
+                            ? R.string.da_lock_success
+                            : R.string.da_lock_error;
+                    new AlertDialog.Builder(ScreenLockTestActivity.this)
+                        .setTitle(R.string.da_screen_lock_test)
+                        .setMessage(messageId)
+                        .setIcon(iconId)
+                        .setPositiveButton(android.R.string.ok, null)
+                        .show();
+                }
+            }, LOCK_CHECK_DELAY);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/TestDeviceAdminReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/TestDeviceAdminReceiver.java
new file mode 100644
index 0000000..5ecb36d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/TestDeviceAdminReceiver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.admin;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+
+public class TestDeviceAdminReceiver extends DeviceAdminReceiver {
+
+    public static ComponentName getComponent(Context context) {
+        return new ComponentName(context, TestDeviceAdminReceiver.class);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioAssets.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioAssets.java
new file mode 100644
index 0000000..7b7d427
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioAssets.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class AudioAssets {
+
+    private static final String TAG = "AudioQualityVerifier";
+
+    private static final String STIM_BASENAME = "audioquality/stim_dt_";
+
+    public static byte[] getStim(Context context, int which) {
+        return readAsset(context, STIM_BASENAME + which);
+    }
+
+    public static byte[] getPinkNoise(Context context, int ampl, int duration) {
+        return readAsset(context, "audioquality/pink_" + ampl + "_" + duration + "s");
+    }
+
+    private static byte[] readAsset(Context context, String filename) {
+        AssetManager assetManager = context.getAssets();
+        InputStream ais;
+        try {
+            ais = assetManager.open(filename);
+        } catch (IOException e) {
+            Log.e(TAG, "Cannot load asset " + filename, e);
+            return null;
+        }
+        byte[] buffer = Utils.readFile(ais);
+        return buffer;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java
new file mode 100644
index 0000000..7d7c16d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+
+import java.util.ArrayList;
+
+/**
+ * Main UI for the Android Audio Quality Verifier.
+ */
+public class AudioQualityVerifierActivity extends PassFailButtons.Activity
+        implements View.OnClickListener, OnItemClickListener {
+    public static final String TAG = "AudioQualityVerifier";
+
+    public static final int SAMPLE_RATE = 16000;
+    public static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
+    public static final int BYTES_PER_SAMPLE = 2;
+    public static final int PLAYBACK_STREAM = AudioManager.STREAM_MUSIC;
+
+    // Intent Extra definitions, which must match those in
+    // com.google.android.voicesearch.speechservice.RecognitionController
+    public static final String EXTRA_RAW_AUDIO =
+            "android.speech.extras.RAW_AUDIO";
+
+    // Communication with ExperimentService
+    public static final String ACTION_EXP_STARTED =
+            "com.android.cts.verifier.audioquality.EXP_STARTED";
+    public static final String ACTION_EXP_FINISHED =
+            "com.android.cts.verifier.audioquality.EXP_FINISHED";
+    public static final String EXTRA_ABORTED =
+            "com.android.cts.verifier.audioquality.ABORTED";
+    public static final String EXTRA_EXP_ID =
+            "com.android.cts.verifier.audioquality.EXP_ID";
+    public static final String EXTRA_RUN_ALL =
+            "com.android.cts.verifier.audioquality.RUN_ALL";
+
+    // Communication with ViewResultsActivity
+    public static final String EXTRA_RESULTS =
+            "com.android.cts.verifier.audioquality.RESULTS";
+
+    private Button mCalibrateButton;
+    private Button mRunAllButton;
+    private Button mStopButton;
+    private Button mViewResultsButton;
+    private Button mClearButton;
+
+    private ListView mList;
+    private TwoColumnAdapter mAdapter;
+
+    private ProgressBar mProgress;
+
+    private ArrayList<Experiment> mExperiments;
+
+    private boolean mRunningExperiment;
+
+    private BroadcastReceiver mReceiver;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.aq_verifier_activity);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.aq_verifier, R.string.aq_verifier_info, -1);
+
+        mCalibrateButton = (Button) findViewById(R.id.calibrateButton);
+        mRunAllButton = (Button) findViewById(R.id.runAllButton);
+        mStopButton = (Button) findViewById(R.id.stopButton);
+        mViewResultsButton = (Button) findViewById(R.id.viewResultsButton);
+        mClearButton = (Button) findViewById(R.id.clearButton);
+
+        mCalibrateButton.setOnClickListener(this);
+        mRunAllButton.setOnClickListener(this);
+        mStopButton.setOnClickListener(this);
+        mViewResultsButton.setOnClickListener(this);
+        mClearButton.setOnClickListener(this);
+
+        mStopButton.setEnabled(false);
+
+        mProgress = (ProgressBar) findViewById(R.id.progress);
+
+        mList = (ListView) findViewById(R.id.list);
+        mAdapter = new TwoColumnAdapter(this);
+
+        mExperiments = VerifierExperiments.getExperiments(this);
+
+        mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                experimentReplied(intent);
+            }
+        };
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_EXP_STARTED);
+        filter.addAction(ACTION_EXP_FINISHED);
+        registerReceiver(mReceiver, filter);
+
+        fillAdapter();
+        mList.setAdapter(mAdapter);
+        mList.setOnItemClickListener(this);
+        checkNotSilent();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mAdapter.notifyDataSetChanged(); // Update List UI
+        setVolumeControlStream(AudioManager.STREAM_MUSIC);
+        checkNotSilent();
+    }
+
+    private void checkNotSilent() {
+        AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        mgr.setStreamMute(PLAYBACK_STREAM, false);
+        int volume = mgr.getStreamVolume(PLAYBACK_STREAM);
+        int max = mgr.getStreamMaxVolume(PLAYBACK_STREAM);
+        Log.i(TAG, "Volume " + volume + ", max " + max);
+        if (volume <= max / 10) {
+            // Volume level is silent or very quiet; increase to two-thirds
+            mgr.setStreamVolume(PLAYBACK_STREAM, (max * 2) / 3, AudioManager.FLAG_SHOW_UI);
+        }
+    }
+
+    // Called when an experiment has completed
+    private void experimentReplied(Intent intent) {
+        String action = intent.getAction();
+        if (ACTION_EXP_STARTED.equals(action)) {
+            mStopButton.setEnabled(true);
+            mRunAllButton.setEnabled(false);
+        } else if (ACTION_EXP_FINISHED.equals(action)) {
+            boolean mRunAll = intent.getBooleanExtra(EXTRA_RUN_ALL, false);
+            boolean aborted = intent.getBooleanExtra(EXTRA_ABORTED, true);
+            int expID = intent.getIntExtra(EXTRA_EXP_ID, -1);
+            if (mRunAll && !aborted) {
+                while (expID < mExperiments.size() - 1) {
+                    if (runExperiment(++expID, true)) {
+                        // OK, experiment is running
+                        mAdapter.notifyDataSetChanged();
+                        return;
+                    }
+                    // Otherwise, loop back and try the next experiment
+                }
+            }
+            mStopButton.setEnabled(false);
+            mRunAllButton.setEnabled(true);
+            mRunningExperiment = false;
+            mProgress.setVisibility(ProgressBar.INVISIBLE);
+        }
+        mAdapter.notifyDataSetChanged();
+    }
+
+    // Implements AdapterView.OnItemClickListener
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        if (mRunningExperiment) return;
+        runExperiment(position, false);
+    }
+
+    // Begin an experiment. Returns false if the experiment is not enabled.
+    private boolean runExperiment(int which, boolean all) {
+        Experiment exp = mExperiments.get(which);
+        if (!exp.isEnabled()) return false;
+        Intent intent = new Intent(this, ExperimentService.class);
+        intent.putExtra(EXTRA_EXP_ID, which);
+        intent.putExtra(EXTRA_RUN_ALL, all);
+        startService(intent);
+        mRunningExperiment = true;
+        mAdapter.notifyDataSetChanged();
+        mProgress.setVisibility(ProgressBar.VISIBLE);
+        return true;
+    }
+
+    // Implements View.OnClickListener:
+    public void onClick(View v) {
+        if (v == mCalibrateButton) {
+            Intent intent = new Intent(this, CalibrateVolumeActivity.class);
+            startActivity(intent);
+        } else if (v == mRunAllButton) {
+            if (mRunningExperiment) return;
+            int expID = -1;
+            while (expID < mExperiments.size() - 1) {
+                if (runExperiment(++expID, true)) break;
+            }
+        } else if (v == mStopButton) {
+            Intent intent = new Intent(this, ExperimentService.class);
+            stopService(intent);
+        } else if (v == mViewResultsButton) {
+            Intent intent = new Intent(this, ViewResultsActivity.class);
+            intent.putExtra(EXTRA_RESULTS, genReport());
+            startActivity(intent);
+        } else if (v == mClearButton) {
+            clear();
+        }
+    }
+
+    private void fillAdapter() {
+        mAdapter.clear();
+        for (Experiment exp : mExperiments) {
+            mAdapter.add(exp.getName());
+        }
+    }
+
+    class TwoColumnAdapter extends ArrayAdapter<String> {
+        TwoColumnAdapter(Context context) {
+            super(context, R.layout.aq_row);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            LayoutInflater inflater = getLayoutInflater();
+            View row = inflater.inflate(R.layout.aq_row, parent, false);
+            TextView nameField = (TextView) row.findViewById(R.id.testName);
+            TextView scoreField = (TextView) row.findViewById(R.id.testScore);
+            Experiment exp = mExperiments.get(position);
+            nameField.setText(exp.getName());
+            scoreField.setText(exp.getScore());
+            if (exp.isRunning()) {
+                Typeface tf = nameField.getTypeface();
+                nameField.setTypeface(tf, 1);
+            }
+            if (!exp.isEnabled()) {
+                nameField.setTextColor(Color.GRAY);
+            }
+            return row;
+        }
+    }
+
+    private String genReport() {
+        StringBuilder sb = new StringBuilder();
+        for (Experiment exp : mExperiments) {
+            exp.getReport(sb);
+        }
+        return sb.toString();
+    }
+
+    private void clear() {
+        if (mRunningExperiment) {
+            Intent intent = new Intent(this, ExperimentService.class);
+            stopService(intent);
+        }
+        for (Experiment exp : mExperiments) {
+            exp.reset();
+        }
+        mAdapter.notifyDataSetChanged();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/BackgroundAudio.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/BackgroundAudio.java
new file mode 100644
index 0000000..e55f9b7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/BackgroundAudio.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+import android.media.AudioFormat;
+import android.media.AudioTrack;
+import android.util.Log;
+
+/**
+ * Continuously play background noise in a loop, until halt() is called.
+ * Used to simulate noisy environments, and for sound level calibration.
+ */
+public class BackgroundAudio extends Thread {
+    public static final String TAG = "AudioQualityVerifier";
+
+    private static final int BUFFER_TIME = 100; // Time in ms to buffer for
+
+    private boolean mProceed;
+    private AudioTrack mAudioTrack;
+    private byte[] mData;
+    private int mPos;
+    private int mBufferSize;
+
+    public void halt() {
+        mProceed = false;
+    }
+
+    public BackgroundAudio(byte[] data) {
+        mProceed = true;
+        mData = data;
+        mPos = 0;
+
+        // Calculate suitable buffer size:
+        final int minBufferSize = (BUFFER_TIME * AudioQualityVerifierActivity.SAMPLE_RATE
+                * AudioQualityVerifierActivity.BYTES_PER_SAMPLE) / 1000;
+        final int minHardwareBufferSize =
+                AudioTrack.getMinBufferSize(AudioQualityVerifierActivity.SAMPLE_RATE,
+                        AudioFormat.CHANNEL_OUT_MONO, AudioQualityVerifierActivity.AUDIO_FORMAT);
+        mBufferSize = Utils.getAudioTrackBufferSize(minBufferSize);
+        Log.i(TAG, "minBufferSize = " + minBufferSize + ", minHWSize = " + minHardwareBufferSize
+                + ", bufferSize = " + mBufferSize);
+
+        // Start playback:
+        Log.i(TAG, "Looping " + data.length + " bytes of audio, buffer size " + mBufferSize);
+        mAudioTrack = new AudioTrack(AudioQualityVerifierActivity.PLAYBACK_STREAM,
+                AudioQualityVerifierActivity.SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO,
+                AudioQualityVerifierActivity.AUDIO_FORMAT, mBufferSize, AudioTrack.MODE_STREAM);
+        if (mAudioTrack.getState() == AudioTrack.STATE_INITIALIZED) {
+            writeAudio();
+            start(); // Begin background thread to push audio data
+            mAudioTrack.play();
+        } else {
+            Log.e(TAG, "Error initializing audio track.");
+        }
+    }
+
+    @Override
+    public void run() {
+        while (true) {
+            if (!mProceed) {
+                mAudioTrack.stop();
+                return; // End thread
+            }
+            writeAudio();
+        }
+    }
+
+    private void writeAudio() {
+        int len = mData.length;
+        int count;
+        int maxBytes = Math.min(mBufferSize, len - mPos);
+
+        count = mAudioTrack.write(mData, mPos, maxBytes);
+        if (count < 0) {
+            Log.e(TAG, "Error writing looped audio data");
+            halt();
+            return;
+        }
+        mPos += count;
+        if (mPos == len) {
+            mPos = 0; // Wraparound
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/CalibrateVolumeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/CalibrateVolumeActivity.java
new file mode 100644
index 0000000..98e8cd1
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/CalibrateVolumeActivity.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+import com.android.cts.verifier.R;
+
+import android.app.Activity;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+/**
+ * Play a continuous sound and allow the user to monitor the sound level
+ * at the mic in real time, relative to the range the phone can detect.
+ * This is not an absolute sound level meter, but lets the speaker volume
+ * and position be adjusted so that the clipping point is known.
+ */
+public class CalibrateVolumeActivity extends Activity implements View.OnClickListener {
+    public static final String TAG = "AudioQualityVerifier";
+
+    public static final int OUTPUT_AMPL = 5000;
+    public static final float TARGET_RMS = 5000.0f;
+    public static final float TARGET_AMPL = (float) (TARGET_RMS * Math.sqrt(2.0));
+    private static final float FREQ = 625.0f;
+
+    private static final float TOLERANCE = 1.03f;
+
+    private static final int DEBOUNCE_TIME = 500; // Minimum time in ms between status text changes
+    public static final boolean USE_PINK = false;
+
+    private ProgressBar mSlider;
+    private Button mDoneButton;
+    private TextView mStatus;
+    private BackgroundAudio mBackgroundAudio;
+    private Monitor mMonitor;
+    private Handler mHandler;
+
+    private Native mNative;
+
+    enum Status { LOW, OK, HIGH, UNDEFINED }
+    private static int[] mStatusText = {
+        R.string.aq_status_low, R.string.aq_status_ok, R.string.aq_status_high };
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.aq_sound_level_meter);
+
+        mSlider = (ProgressBar) findViewById(R.id.slider);
+        mStatus = (TextView) findViewById(R.id.status);
+        mStatus.setText(R.string.aq_status_unknown);
+
+        mDoneButton = (Button) findViewById(R.id.doneButton);
+        mDoneButton.setOnClickListener(this);
+
+        mNative = Native.getInstance();
+        mHandler = new UpdateHandler();
+    }
+
+    // Implements View.OnClickListener
+    public void onClick(View v) {
+        if (v == mDoneButton) {
+            finish();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        final int DURATION = 1;
+        final float RAMP = 0.0f;
+        byte[] noise;
+        if (USE_PINK) {
+            noise = Utils.getPinkNoise(this, OUTPUT_AMPL, DURATION);
+        } else {
+            short[] sinusoid = mNative.generateSinusoid(FREQ, DURATION,
+                    AudioQualityVerifierActivity.SAMPLE_RATE, OUTPUT_AMPL, RAMP);
+            noise = Utils.shortToByteArray(sinusoid);
+        }
+        float[] results = mNative.measureRms(Utils.byteToShortArray(noise),
+                AudioQualityVerifierActivity.SAMPLE_RATE, -1.0f);
+        float rms = results[Native.MEASURE_RMS_RMS];
+        Log.i(TAG, "Stimulus amplitude " + OUTPUT_AMPL + ", RMS " + rms);
+        mBackgroundAudio = new BackgroundAudio(noise);
+        mMonitor = new Monitor();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mBackgroundAudio.halt();
+        mMonitor.halt();
+    }
+
+    private class UpdateHandler extends Handler {
+        private Status mState = Status.UNDEFINED;
+        private long mTimestamp = 0; // Time of last status change in ms
+
+        @Override
+        public void handleMessage(Message msg) {
+            int rms = msg.arg1;
+            int max = mSlider.getMax();
+            int progress = (max / 2 * rms) / Math.round(TARGET_RMS);
+            if (progress > max) progress = max;
+            mSlider.setProgress(progress);
+
+            Status state;
+            if (rms * TOLERANCE < TARGET_RMS) state = Status.LOW;
+            else if (rms > TARGET_RMS * TOLERANCE) state = Status.HIGH;
+            else state = Status.OK;
+            if (state != mState) {
+                long timestamp = System.currentTimeMillis();
+                if (timestamp - mTimestamp > DEBOUNCE_TIME) {
+                    mStatus.setText(mStatusText[state.ordinal()]);
+                    mState = state;
+                    mTimestamp = timestamp;
+                }
+            }
+        }
+    }
+
+    class Monitor extends Thread {
+        private static final int BUFFER_TIME = 100; // Min time in ms to buffer for
+        private static final int READ_TIME = 25;    // Max length of time in ms to read in one chunk
+        private static final boolean DEBUG = false;
+
+        private AudioRecord mRecord;
+        private int mSamplesToRead;
+        private byte[] mBuffer;
+        private boolean mProceed;
+
+        Monitor() {
+            mProceed = true;
+
+            mSamplesToRead = (READ_TIME * AudioQualityVerifierActivity.SAMPLE_RATE) / 1000;
+            mBuffer = new byte[mSamplesToRead * AudioQualityVerifierActivity.BYTES_PER_SAMPLE];
+
+            final int minBufferSize = (BUFFER_TIME * AudioQualityVerifierActivity.SAMPLE_RATE *
+                    AudioQualityVerifierActivity.BYTES_PER_SAMPLE) / 1000;
+            final int bufferSize = Utils.getAudioRecordBufferSize(minBufferSize);
+
+            mRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_RECOGNITION,
+                    AudioQualityVerifierActivity.SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
+                    AudioQualityVerifierActivity.AUDIO_FORMAT, bufferSize);
+            if (mRecord.getState() != AudioRecord.STATE_INITIALIZED) {
+                Log.e(TAG, "Couldn't open audio for recording");
+                return;
+            }
+            mRecord.startRecording();
+            if (mRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
+                Log.e(TAG, "Couldn't record");
+                return;
+            }
+
+            start(); // Begin background thread
+        }
+
+        public void halt() {
+            mProceed = false;
+        }
+
+        @Override
+        public void run() {
+            int maxBytes = mSamplesToRead * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
+            int bytes;
+            while (true) {
+                if (!mProceed) {
+                    mRecord.stop();
+                    mRecord.release();
+                    return; // End thread
+                }
+                bytes = mRecord.read(mBuffer, 0, maxBytes);
+                if (bytes < 0) {
+                    if (bytes == AudioRecord.ERROR_INVALID_OPERATION) {
+                        Log.e(TAG, "Recording object not initalized");
+                    } else if (bytes == AudioRecord.ERROR_BAD_VALUE) {
+                        Log.e(TAG, "Invalid recording parameters");
+                    } else {
+                        Log.e(TAG, "Error during recording");
+                    }
+                    return;
+                }
+                if (bytes >= 2) {
+                    // Note: this won't work well if bytes is small (we should check)
+                    short[] samples = Utils.byteToShortArray(mBuffer, 0, bytes);
+                    float[] results = mNative.measureRms(samples,
+                            AudioQualityVerifierActivity.SAMPLE_RATE, -1.0f);
+                    float rms = results[Native.MEASURE_RMS_RMS];
+                    float duration = results[Native.MEASURE_RMS_DURATION];
+                    float mean = results[Native.MEASURE_RMS_MEAN];
+                    if (DEBUG) {
+                        // Confirm the RMS calculation
+                        float verifyRms = 0.0f;
+                        for (short x : samples) {
+                            verifyRms += x * x;
+                        }
+                        verifyRms /= samples.length;
+                        Log.i(TAG, "RMS: " + rms + ", bytes: " + bytes
+                                + ", duration: " + duration + ", mean: " + mean
+                                + ", manual RMS: " + Math.sqrt(verifyRms));
+                    }
+                    Message msg = mHandler.obtainMessage();
+                    msg.arg1 = Math.round(rms);
+                    mHandler.sendMessage(msg);
+                }
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Experiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Experiment.java
new file mode 100644
index 0000000..6f2aa83
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Experiment.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * The base class for all audio experiments.
+ */
+public class Experiment implements Runnable {
+    protected static final String TAG = "AudioQualityVerifier";
+
+    private static final int DEFAULT_TIMEOUT = 5; // In seconds
+
+    private String mName;
+    private String mScore;
+    private String mReport;
+    private List<String> mAudioFileNames;
+
+    enum Status { NotStarted, Running, Stopped, Completed }
+    private Status mStatus;
+    private boolean mEnabled;
+
+    protected Native mNative;
+    protected Context mContext;
+    protected Terminator mTerminator;
+
+    public Experiment(boolean enable) {
+        mEnabled = enable;
+        mNative = Native.getInstance();
+        reset();
+    }
+
+    public void init(Context context) {
+        mName = lookupName(context);
+    }
+
+    protected String lookupName(Context context) {
+        return context.getString(R.string.aq_default_exp);
+    }
+
+    protected String getString(int resId) {
+        return mContext.getString(resId);
+    }
+
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    public void reset() {
+        mStatus = Status.NotStarted;
+        mScore = "";
+        mReport = "";
+        mAudioFileNames = new ArrayList<String>();
+    }
+
+    public void start() {
+        mStatus = Status.Running;
+    }
+
+    protected void setScore(String score) {
+        mScore = score;
+    }
+
+    protected void setReport(String report) {
+        mReport = report;
+    }
+
+    // Implements Runnable
+    public void run() {}
+
+    public void run(Context context, Terminator t) {
+        mContext = context;
+        mTerminator = t;
+        Thread thread = new Thread(this);
+        thread.start();
+    }
+
+    public void setRecording(byte[] data) {
+        setRecording(data, -1);
+    }
+
+    public void setRecording(byte[] data, int num) {
+        // Save captured data to file
+        String filename = Utils.getExternalDir(mContext, this) + "/"
+            + Utils.cleanString(getName())
+            + (num == -1 ? "" : "_" + String.valueOf(num)) + ".raw";
+        Log.i(TAG, "Saving recorded data to " + filename);
+        Utils.saveFile(filename, data);
+        mAudioFileNames.add(filename);
+    }
+
+    public List<String> getAudioFileNames() {
+        return mAudioFileNames;
+    }
+
+    // Timeout in seconds
+    public int getTimeout() {
+        return DEFAULT_TIMEOUT;
+    }
+
+    public void cancel() {
+        mStatus = Status.Stopped;
+    }
+
+    public void stop() {
+        mStatus = Status.Completed;
+    }
+
+    public boolean isRunning() {
+        return mStatus == Status.Running;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public String getScore() {
+        switch (mStatus) {
+            case NotStarted:
+                return "-";
+            case Running:
+                return "...";
+            case Stopped:
+                return "-";
+            case Completed:
+                return mScore;
+        }
+        return "";
+    }
+
+    public void getReport(StringBuilder sb) {
+        sb.append(getName());
+        sb.append(": ");
+        sb.append(getScore());
+        sb.append("\n");
+        sb.append(mReport);
+        sb.append("\n\n");
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/ExperimentService.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/ExperimentService.java
new file mode 100644
index 0000000..95fd3b5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/ExperimentService.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Launch an experiment.
+ *
+ * Experiments are decoupled from the UI both so that they can run on a
+ * background thread without freezing the UI, and to support experiments
+ * which take over the screen from the UI (such as invoking Voice Search).
+ */
+public class ExperimentService extends Service implements Terminator {
+    private static final String TAG = "AudioQualityVerifier";
+    private static int BACKGROUND_LOAD = 0;
+
+    private ArrayList<Experiment> mExperiments;
+    private Experiment mExp;
+
+    private TimeoutHandler mHandler = null;
+    private PowerManager.WakeLock mWakeLock = null;
+
+    private boolean mRunAll;
+    private int mExpID;
+
+    private boolean mActive;
+    private LoadGenerator[] mLoadGenerator = null;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        mExperiments = VerifierExperiments.getExperiments(this);
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.i(TAG, "Service destroyed");
+        terminate(true);
+    }
+
+    /**
+     * Implements Terminator, to clean up when the experiment indicates it
+     * has completed.
+     */
+    public void terminate(boolean aborted) {
+        if (mLoadGenerator != null) {
+            for (LoadGenerator generator : mLoadGenerator) {
+                generator.halt();
+            }
+            mLoadGenerator = null;
+        }
+        if (!mActive) return;
+        mActive = false;
+        if (mHandler != null) mHandler.clear();
+        if (aborted) {
+            mExp.cancel();
+        } else {
+            mExp.stop();
+        }
+        Intent intent = new Intent(AudioQualityVerifierActivity.ACTION_EXP_FINISHED);
+        intent.putExtra(AudioQualityVerifierActivity.EXTRA_EXP_ID, mExpID);
+        intent.putExtra(AudioQualityVerifierActivity.EXTRA_RUN_ALL, mRunAll);
+        intent.putExtra(AudioQualityVerifierActivity.EXTRA_ABORTED, aborted);
+        sendBroadcast(intent);
+        if (mWakeLock != null) {
+            mWakeLock.release();
+            mWakeLock = null;
+        }
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        mActive = true;
+
+        // Obtain wakelock
+        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG);
+        mWakeLock.acquire();
+
+        if (BACKGROUND_LOAD > 0) {
+            mLoadGenerator = new LoadGenerator[BACKGROUND_LOAD];
+            for (int i = 0; i < BACKGROUND_LOAD; i++) {
+                mLoadGenerator[i] = new LoadGenerator();
+            }
+        }
+
+        mExpID = intent.getIntExtra(AudioQualityVerifierActivity.EXTRA_EXP_ID, -1);
+        if (mExpID == -1) {
+            Log.e(TAG, "Invalid test ID");
+            System.exit(0);
+        }
+        mRunAll = intent.getBooleanExtra(AudioQualityVerifierActivity.EXTRA_RUN_ALL, false);
+        mExp = mExperiments.get(mExpID);
+        mExp.start();
+
+        // Inform the VerifierActivity Activity that we have started:
+        Intent feedback = new Intent(AudioQualityVerifierActivity.ACTION_EXP_STARTED);
+        feedback.putExtra(AudioQualityVerifierActivity.EXTRA_EXP_ID, mExpID);
+        sendBroadcast(feedback);
+
+        mHandler = new TimeoutHandler();
+        mHandler.delay(mExp.getTimeout());
+        mExp.run(this, this);
+
+        return START_NOT_STICKY;
+    }
+
+    class TimeoutHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            terminate(true);
+            stopSelf();
+        }
+
+        public void delay(int secs) {
+            removeMessages(0);
+            sendMessageDelayed(obtainMessage(0), secs * 1000);
+        }
+
+        public void clear() {
+            removeMessages(0);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/LoadGenerator.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/LoadGenerator.java
new file mode 100644
index 0000000..f19359c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/LoadGenerator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+/**
+ * Simulate a simple load on the CPU, to determine the effect on recording.
+ * A better approach would be to generate network activity, to
+ * trigger interrupts as well.
+ */
+public class LoadGenerator extends Thread {
+    private boolean mProceed;
+
+    public LoadGenerator() {
+        mProceed = true;
+        start(); // Begin thread
+    }
+
+    public void halt() {
+        mProceed = false;
+    }
+
+    @Override
+    public void run() {
+        long counter = 0;
+        while (mProceed) {
+            counter++; // Busy loop
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Native.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Native.java
new file mode 100644
index 0000000..87b11d1
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Native.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+/**
+ * Interface to native (C++) DSP code.
+ */
+public class Native {
+    public native short[] generateSinusoid(float freq, float duration,
+            float sampleRate, float amplitude, float ramp);
+    public native float[] measureRms(short[] pcm, float sampleRate,
+            float onsetThresh);
+    public native float[] glitchTest(float sampleRate, float stimFreq,
+            float onsetThresh, float dbSnrThresh, short[] pcm);
+    public native float[] overflowCheck(short[] pcm, float sampleRate);
+    public native float[] compareSpectra(short[] pcm, short[] refPcm,
+            float sampleRate);
+    public native float linearityTest(short[][] pcms,
+        float sampleRate, float dbStepSize, int referenceStim);
+
+    // The following indexes must match those in wrapper.cc
+    public static final int MEASURE_RMS_RMS = 0;
+    public static final int MEASURE_RMS_STD_DEV = 1;
+    public static final int MEASURE_RMS_DURATION = 2;
+    public static final int MEASURE_RMS_MEAN = 3;
+
+    public static final int OVERFLOW_DELTAS = 0;
+    public static final int OVERFLOW_ERROR = 1;
+    public static final int OVERFLOW_DURATION = 2;
+    public static final int OVERFLOW_ONSET = 3;
+    public static final int OVERFLOW_OFFSET = 4;
+    public static final int OVERFLOW_MAX = 5;
+    public static final int OVERFLOW_MIN = 6;
+
+    public static final int GLITCH_COUNT = 0;
+    public static final int GLITCH_ERROR = 1;
+    public static final int GLITCH_DURATION = 2;
+
+    public static final int SPECTRUM_MAX_DEVIATION = 0;
+    public static final int SPECTRUM_ERROR = 1;
+    public static final int SPECTRUM_RMS_DEVIATION = 2;
+
+    private static Native mInstance = null;
+
+    static {
+        System.loadLibrary("audioquality");
+    }
+
+    private Native() {}
+
+    public static Native getInstance() {
+        if (mInstance == null) {
+            mInstance = new Native();
+        }
+        return mInstance;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/README.txt b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/README.txt
new file mode 100644
index 0000000..3f9a59c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/README.txt
@@ -0,0 +1,96 @@
+Android Audio Quality Verifier App
+==================================
+
+This app runs a set of audio quality tests on an Android device,
+to verify the end-end sound recording path.
+
+If any of these tests fail, the device is probably unsuitable
+for demanding audio tasks such as speech recognition.
+If all the tests pass, the device audio is of a good standard.
+Note that not all possible audio defects can be detected by this
+test suite, so passing does not guarantee ideal audio quality.
+
+Hardware setup
+--------------
+
+The required physical set-up consists of a powered speaker,
+connected to the Android's headphone output by a standard
+audio cable.
+
+For loudspeakers which come in pairs, you only need to use
+one speaker (typically the powered or master speaker); you
+can leave the second speaker disconnected.
+If the speakers are stereo within a single unit (sometimes
+with speakers facing in opposite directions), place the phone
+in front of either of them.
+Speakers with multiple drivers per channel (e.g. a tweeter
+and a woofer) are not suitable.
+
+The phone should be placed in front of the centre of the
+speaker cone. The distance from the speaker will be adjusted
+during calibration; typically you could expect it to be around
+3cm or so.
+Use a supporting platform such as a stack of books to raise
+the phone to the correct height to line up with the speaker.
+
+Bluetooth connection is possible but cable connection is
+usually preferable.
+
+Recommended loudspeakers
+------------------------
+
+Using suitable loudspeakers ensures that test failures highlight
+problems with the Android device under test, and not limitations
+of the loudspeakers. The following loudspeakers work well for this
+purpose:
+
+1. Yamaha NX-B02
+
+Use on AC power, not batteries.
+This speaker works well with Bluetooth as well as a wired connection.
+Note that it's not uncommon for the devices to exhibit different
+bugs under Bluetooth.
+
+2. Cakewalk MA-7A (Edirol / Roland)
+
+The "Bass Enhancer" feature MUST be switched off.
+Note that it turns itself on again every time the speakers are
+powered on, so it is easy to forget to switch it off!
+
+Software setup
+--------------
+
+1. Build the application's apk.
+2. Install the apk using adb.
+3. Run the app.
+4. Click "Calibrate". Position the phone as described in
+   Hardware setup above, with the microphone facing the speaker,
+   and adjust the volume of the speaker until the status message
+   indicates it is correct.
+5. Click on any test in the list to run it, or "Run All" to run
+   each test in sequence.
+6. Click "Results" to view the outcomes. A correctly functioning
+   device should pass all tests.
+7. Click "Send by email" from the results page to send the
+   results to an e-mail address of your choice. The recordings
+   made are also attached as raw 16 bit, 16 kHz audio files to
+   help you diagnose any failed tests.
+
+Q&A
+---
+
+Q. What if the sound level check fails?
+A. Go back to the calibration step before running any other test.
+   Make sure the device has not been moved.
+   We also recommend that once the setup is calibrated there are no
+   moving objects or people near the device under test, since these
+   will change the acoustic properties of the environment from the
+   calibrated state.
+
+Q. Some of the tests sound very loud. Is this normal?
+A. The clipping test will generally be very loud indeed;
+   the others should be at a moderate volume.
+
+Q. What sort of room should the tests be performed in?
+A. Any, as long as the background noise levels are kept low, to
+   avoid interference with the test recordings.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Terminator.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Terminator.java
new file mode 100644
index 0000000..b0edc90
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Terminator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+/**
+ * Interface passed to each Experiment.
+ */
+public interface Terminator {
+    /**
+     * Indicate that the experiment has finished.
+     *
+     * @param aborted is true if the experiment terminated abnormally.
+     */
+    public void terminate(boolean aborted);
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Utils.java
new file mode 100644
index 0000000..704b1df
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Utils.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+import android.content.Context;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.AudioTrack;
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * File and data utilities for the Audio Verifier.
+ */
+public class Utils {
+    public static final String TAG = "AudioQualityVerifier";
+    public static final ByteOrder BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
+
+    /**
+     * @param minBufferSize requested
+     * @return the buffer size or a negative {@link AudioTrack} ERROR value
+     */
+    public static int getAudioTrackBufferSize(int minBufferSize) {
+        int minHardwareBufferSize = AudioTrack.getMinBufferSize(
+                AudioQualityVerifierActivity.SAMPLE_RATE,
+                AudioFormat.CHANNEL_OUT_MONO,
+                AudioQualityVerifierActivity.AUDIO_FORMAT);
+        if (minHardwareBufferSize < 0) {
+            return minHardwareBufferSize;
+        } else {
+            return Math.max(minHardwareBufferSize, minBufferSize);
+        }
+    }
+
+    /**
+     * @param minBufferSize requested
+     * @return the buffer size or a negative {@link AudioRecord} ERROR value
+     */
+    public static int getAudioRecordBufferSize(int minBufferSize) {
+        int minHardwareBufferSize = AudioRecord.getMinBufferSize(
+                AudioQualityVerifierActivity.SAMPLE_RATE,
+                AudioFormat.CHANNEL_IN_MONO,
+                AudioQualityVerifierActivity.AUDIO_FORMAT);
+        if (minHardwareBufferSize < 0) {
+            return minHardwareBufferSize;
+        } else {
+            return Math.max(minHardwareBufferSize, minBufferSize);
+        }
+    }
+
+    /**
+     *  Time delay.
+     *
+     *  @param ms time in milliseconds to pause for
+     */
+    public static void delay(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException e) {}
+    }
+
+    public static String getExternalDir(Context context, Object exp) {
+        checkExternalStorageAvailable();
+        // API level 8:
+        // return context.getExternalFilesDir(null).getAbsolutePath();
+        // API level < 8:
+        String dir = Environment.getExternalStorageDirectory().getAbsolutePath();
+        dir += "/Android/data/" + exp.getClass().getPackage().getName() + "/files";
+        checkMakeDir(dir);
+        return dir;
+    }
+
+    private static void checkExternalStorageAvailable() {
+        String state = Environment.getExternalStorageState();
+        if (!Environment.MEDIA_MOUNTED.equals(state)) {
+            // TODO: Raise a Toast and supply internal storage instead
+        }
+    }
+
+    private static void checkMakeDir(String dir) {
+        File f = new File(dir);
+        if (!f.exists()) {
+            f.mkdirs();
+        }
+    }
+
+    /**
+     * Convert a string (e.g. the name of an experiment) to something more suitable
+     * for use as a filename.
+     *
+     * @param s the string to be cleaned
+     * @return a string which is similar (not necessarily unique) and safe for filename use
+     */
+    public static String cleanString(String s) {
+        StringBuilder sb = new StringBuilder();
+        for (char c : s.toCharArray()) {
+            if (Character.isWhitespace(c)) sb.append('_');
+            else if (Character.isLetterOrDigit(c)) sb.append(c);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Convert a sub-array from bytes to shorts.
+     *
+     * @param data array of bytes to be converted
+     * @param start first index to convert (should be even)
+     * @param len number of bytes to convert (should be even)
+     * @return an array of half the length, containing shorts
+     */
+    public static short[] byteToShortArray(byte[] data, int start, int len) {
+        short[] samples = new short[len / 2];
+        ByteBuffer bb = ByteBuffer.wrap(data, start, len);
+        bb.order(BYTE_ORDER);
+        for (int i = 0; i < len / 2; i++) {
+            samples[i] = bb.getShort();
+        }
+        return samples;
+    }
+
+    /**
+     * Convert a byte array to an array of shorts (suitable for the phone test
+     * native library's audio sample data).
+     *
+     * @param data array of bytes to be converted
+     * @return an array of half the length, containing shorts
+     */
+    public static short[] byteToShortArray(byte[] data) {
+        int len = data.length / 2;
+        short[] samples = new short[len];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.order(BYTE_ORDER);
+        for (int i = 0; i < len; i++) {
+            samples[i] = bb.getShort();
+        }
+        return samples;
+    }
+
+    /**
+     * Convert a short array (as returned by the phone test native library)
+     * to an array of bytes.
+     *
+     * @param samples array of shorts to be converted
+     * @return an array of twice the length, broken out into bytes
+     */
+    public static byte[] shortToByteArray(short[] samples) {
+        int len = samples.length;
+        byte[] data = new byte[len * 2];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.order(BYTE_ORDER);
+        for (int i = 0; i < len; i++) {
+            bb.putShort(samples[i]);
+        }
+        return data;
+    }
+
+    /**
+     * Scale the amplitude of an array of samples.
+     *
+     * @param samples to be scaled
+     * @param db decibels to scale up by (may be negative)
+     * @return the scaled samples
+     */
+    public static short[] scale(short[] samples, float db) {
+        short[] scaled = new short[samples.length];
+        // Convert decibels to a linear ratio:
+        double ratio = Math.pow(10.0, db / 20.0);
+        for (int i = 0; i < samples.length; i++) {
+            scaled[i] = (short) (samples[i] * ratio);
+        }
+        return scaled;
+    }
+
+    /**
+     * Read an entire file into memory.
+     *
+     * @param filename to be opened
+     * @return the file data, or null in case of error
+     */
+    private static byte[] readFile(String filename) {
+        FileInputStream fis;
+        try {
+            fis = new FileInputStream(filename);
+        } catch (FileNotFoundException e1) {
+            return null;
+        }
+
+        File file = new File(filename);
+        int len = (int) file.length();
+        byte[] data = new byte[len];
+
+        int pos = 0;
+        int bytes = 0;
+        int count;
+        while (pos < len) {
+            try {
+                count = fis.read(data, pos, len - pos);
+            } catch (IOException e) {
+                return null;
+            }
+            if (count < 1) return null;
+            pos += count;
+        }
+
+        try {
+            fis.close();
+        } catch (IOException e) {}
+        return data;
+    }
+
+    /**
+     * Read an entire file from an InputStream.
+     * Useful as AssetManager returns these.
+     *
+     * @param stream to read file contents from
+     * @return file data
+     */
+    public static byte[] readFile(InputStream stream) {
+        final int CHUNK_SIZE = 10000;
+        ByteArrayBuilder bab = new ByteArrayBuilder();
+        byte[] buf = new byte[CHUNK_SIZE];
+        int count;
+        while (true) {
+            try {
+                count = stream.read(buf, 0, CHUNK_SIZE);
+            } catch (IOException e) {
+                return null;
+            }
+            if (count == -1) break; // EOF
+            bab.append(buf, count);
+        }
+        return bab.toByteArray();
+    }
+
+    /**
+     * Save binary (audio) data to a file.
+     *
+     * @param filename to be written
+     * @param data contents
+     */
+    public static void saveFile(String filename, byte[] data) {
+        try {
+            FileOutputStream fos = new FileOutputStream(filename);
+            fos.write(data);
+            fos.close();
+        } catch (IOException e) {
+            Log.e(TAG, "Error writing to file " + filename, e);
+        }
+    }
+
+    /**
+     * Push an entire array of audio data to an AudioTrack.
+     *
+     * @param at destination
+     * @param data to be written
+     * @return true if successful, or false on error
+     */
+    public static boolean writeAudio(AudioTrack at, byte[] data) {
+        int pos = 0;
+        int len = data.length;
+        int count;
+
+        while (pos < len) {
+            count = at.write(data, pos, len - pos);
+            if (count < 0) return false;
+            pos += count;
+        }
+        at.flush();
+        return true;
+    }
+
+    /**
+     * Determine the number of audio samples in a file
+     *
+     * @param filename file containing audio data
+     * @return number of samples in file, or 0 if file does not exist
+     */
+    public static int duration(String filename) {
+        File file = new File(filename);
+        int len = (int) file.length();
+        return len / AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
+    }
+
+    /**
+     * Determine the number of audio samples in a stimulus asset
+     *
+     * @param context to look up stimulus
+     * @param stimNum index number of this stimulus
+     * @return number of samples in stimulus
+     */
+    public static int duration(Context context, int stimNum) {
+        byte[] data = AudioAssets.getStim(context, stimNum);
+        return data.length / AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
+    }
+
+    public static void playRawFile(String filename) {
+        byte[] data = readFile(filename);
+        if (data == null) {
+            Log.e(TAG, "Cannot read " + filename);
+            return;
+        }
+        playRaw(data);
+    }
+
+    public static void playStim(Context context, int stimNum) {
+        Utils.playRaw(getStim(context, stimNum));
+    }
+
+    public static byte[] getStim(Context context, int stimNum) {
+        return AudioAssets.getStim(context, stimNum);
+    }
+
+    public static byte[] getPinkNoise(Context context, int ampl, int duration) {
+        return AudioAssets.getPinkNoise(context, ampl, duration);
+    }
+
+    public static void playRaw(byte[] data) {
+        Log.i(TAG, "Playing " + data.length + " bytes of pre-recorded audio");
+        AudioTrack at = new AudioTrack(AudioQualityVerifierActivity.PLAYBACK_STREAM, AudioQualityVerifierActivity.SAMPLE_RATE,
+                AudioFormat.CHANNEL_OUT_MONO, AudioQualityVerifierActivity.AUDIO_FORMAT,
+                data.length, AudioTrack.MODE_STREAM);
+        writeAudio(at, data);
+        at.play();
+    }
+
+    /**
+     * The equivalent of a simplified StringBuilder, but for bytes.
+     */
+    public static class ByteArrayBuilder {
+        private byte[] buf;
+        private int capacity, size;
+
+        public ByteArrayBuilder() {
+            capacity = 100;
+            size = 0;
+            buf = new byte[capacity];
+        }
+
+        public void append(byte[] b, int nBytes) {
+            if (nBytes < 1) return;
+            if (size + nBytes > capacity) expandCapacity(size + nBytes);
+            System.arraycopy(b, 0, buf, size, nBytes);
+            size += nBytes;
+        }
+
+        public byte[] toByteArray() {
+            byte[] result = new byte[size];
+            System.arraycopy(buf, 0, result, 0, size);
+            return result;
+        }
+
+        private void expandCapacity(int min) {
+            capacity *= 2;
+            if (capacity < min) capacity = min;
+            byte[] expanded = new byte[capacity];
+            System.arraycopy(buf, 0, expanded, 0, size);
+            buf = expanded;
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/VerifierExperiments.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/VerifierExperiments.java
new file mode 100644
index 0000000..044bde9
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/VerifierExperiments.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+import com.android.cts.verifier.audioquality.experiments.BiasExperiment;
+import com.android.cts.verifier.audioquality.experiments.ColdLatencyExperiment;
+import com.android.cts.verifier.audioquality.experiments.OverflowExperiment;
+import com.android.cts.verifier.audioquality.experiments.GainLinearityExperiment;
+import com.android.cts.verifier.audioquality.experiments.GlitchExperiment;
+import com.android.cts.verifier.audioquality.experiments.SoundLevelExperiment;
+import com.android.cts.verifier.audioquality.experiments.SpectrumShapeExperiment;
+import com.android.cts.verifier.audioquality.experiments.WarmLatencyExperiment;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+
+/**
+ * Data shared between the VerifierActivity and ExperimentService
+ */
+public class VerifierExperiments {
+
+    private static ArrayList<Experiment> mExperiments = null;
+
+    private VerifierExperiments() {
+    }
+
+    public static ArrayList<Experiment> getExperiments(Context context) {
+        if (mExperiments == null) {
+            mExperiments = new ArrayList<Experiment>();
+            mExperiments.add(new SoundLevelExperiment());
+            mExperiments.add(new BiasExperiment());
+            mExperiments.add(new OverflowExperiment());
+            mExperiments.add(new GainLinearityExperiment());
+            mExperiments.add(new SpectrumShapeExperiment());
+            mExperiments.add(new GlitchExperiment(0));
+            mExperiments.add(new GlitchExperiment(7));
+            mExperiments.add(new ColdLatencyExperiment());
+            mExperiments.add(new WarmLatencyExperiment());
+            for (Experiment exp : mExperiments) {
+                exp.init(context);
+            }
+        }
+        return mExperiments;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/ViewResultsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/ViewResultsActivity.java
new file mode 100644
index 0000000..6563335
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/ViewResultsActivity.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 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.audioquality;
+
+import com.android.cts.verifier.R;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This Activity allows the user to examine the results of the
+ * experiments which have been run so far.
+ */
+public class ViewResultsActivity extends Activity implements View.OnClickListener {
+    private TextView mTextView;
+    private Button mDismissButton;
+    private Button mSendResultsButton;
+    private String mResults;
+
+    private ArrayList<Experiment> mExperiments;
+
+    // The package of the Gmail application
+    private static final String GMAIL_PACKAGE = "com.google.android.gm";
+
+    // The Gmail compose activity name
+    private static final String GMAIL_ACTIVITY = GMAIL_PACKAGE
+            + ".ComposeActivityGmail";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.aq_view_results);
+
+        mDismissButton = (Button) findViewById(R.id.dismissButton);
+        mDismissButton.setOnClickListener(this);
+
+        mSendResultsButton = (Button) findViewById(R.id.sendResultsButton);
+        mSendResultsButton.setOnClickListener(this);
+
+        mTextView = (TextView) findViewById(R.id.textView);
+
+        Intent intent = getIntent();
+        mResults = intent.getStringExtra(AudioQualityVerifierActivity.EXTRA_RESULTS);
+        mTextView.setText(mResults);
+
+        mExperiments = VerifierExperiments.getExperiments(this);
+    }
+
+    private void sendResults() {
+        Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_SEND_MULTIPLE);
+        intent.setComponent(new ComponentName(GMAIL_PACKAGE, GMAIL_ACTIVITY));
+        intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.aq_subject));
+        intent.putExtra(Intent.EXTRA_TEXT, mResults);
+
+        ArrayList<Parcelable> attachments = new ArrayList<Parcelable>();
+        for (Experiment exp : mExperiments) {
+            List<String> filenames = exp.getAudioFileNames();
+            for (String filename : filenames) {
+                attachments.add(Uri.fromFile(new File(filename)));
+            }
+        }
+        intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments);
+
+        startActivity(intent);
+    }
+
+    // Implements View.OnClickListener
+    public void onClick(View v) {
+        if (v == mDismissButton) {
+            finish();
+        } else if (v == mSendResultsButton) {
+            sendResults();
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/BiasExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/BiasExperiment.java
new file mode 100644
index 0000000..bb6f8bf
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/BiasExperiment.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 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.audioquality.experiments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
+import com.android.cts.verifier.audioquality.Native;
+import com.android.cts.verifier.audioquality.Utils;
+
+import android.content.Context;
+
+/**
+ * Experiment to look for excessive DC bias in the recordings.
+ * It calculates the mean of the observed samples.
+ */
+public class BiasExperiment extends LoopbackExperiment {
+    private static final float ONSET_THRESH = 10.0f;
+    private static final float TOLERANCE = 200.0f;
+
+    public BiasExperiment() {
+        super(true);
+    }
+
+    @Override
+    protected String lookupName(Context context) {
+        return context.getString(R.string.aq_bias_exp);
+    }
+
+    @Override
+    protected void compare(byte[] stim, byte[] record) {
+        short[] pcm = Utils.byteToShortArray(record);
+        float[] results = mNative.measureRms(pcm, AudioQualityVerifierActivity.SAMPLE_RATE, ONSET_THRESH);
+        float rms = results[Native.MEASURE_RMS_RMS];
+        float duration = results[Native.MEASURE_RMS_DURATION];
+        float mean = results[Native.MEASURE_RMS_MEAN];
+        if (mean < -TOLERANCE || mean > TOLERANCE) {
+            setScore(getString(R.string.aq_fail));
+        } else {
+            setScore(getString(R.string.aq_pass));
+        }
+        setReport(String.format(getString(R.string.aq_bias_report),
+                mean, TOLERANCE, rms, duration));
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/ColdLatencyExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/ColdLatencyExperiment.java
new file mode 100644
index 0000000..648e6cb
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/ColdLatencyExperiment.java
@@ -0,0 +1,253 @@
+/*
+ * 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.audioquality.experiments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
+import com.android.cts.verifier.audioquality.Experiment;
+import com.android.cts.verifier.audioquality.Utils;
+
+import android.content.Context;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder.AudioSource;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * {@link Experiment} that measures how long it takes for an initialized
+ * {@link AudioRecord} object to enter the recording state.
+ */
+public class ColdLatencyExperiment extends Experiment {
+
+    /**
+     * Rough latency amounts observed:
+     *
+     * N1 2.3.4: 350 ms
+     * NS 2.3.4: 250 ms
+     * Xoom 3.1: 100 ms
+     */
+    private static final int MAXIMUM_LATENCY_ALLOWED_MS = 500;
+
+    /** Enough time to say a short phrase usually entered as a voice command. */
+    private static final int BUFFER_TIME_MS = 25 * 1000;
+
+    /** Milliseconds to pause while repeatedly checking the recording state. */
+    private static final int DELAY_MS = 10;
+
+    /** Milliseconds to record before turning off the recording. */
+    private static final int RECORDING_DELAY_MS = 3000;
+
+    /** Milliseconds to pause before checking the latency after making a sound. */
+    private static final int LATENCY_CHECK_DELAY_MS = 5000;
+
+    private static final int TEST_TIMEOUT_SECONDS = 10;
+
+    public ColdLatencyExperiment() {
+        super(true);
+    }
+
+    @Override
+    protected String lookupName(Context context) {
+        return context.getString(R.string.aq_cold_latency);
+    }
+
+    @Override
+    public void run() {
+        ExecutorService executor = Executors.newCachedThreadPool();
+        RecordingTask recordingTask = new RecordingTask(RECORDING_DELAY_MS);
+
+        try {
+            // 1. Start recording for a couple seconds.
+            Future<Long> recordingFuture = executor.submit(recordingTask);
+            long recordTime = recordingFuture.get(RECORDING_DELAY_MS * 2, TimeUnit.MILLISECONDS);
+            if (recordTime < 0) {
+                setScore(getString(R.string.aq_fail));
+                return;
+            }
+
+            // 2. Wait a bit for the audio hardware to shut down.
+            long startTime = System.currentTimeMillis();
+            while (System.currentTimeMillis() - startTime < LATENCY_CHECK_DELAY_MS) {
+                Utils.delay(DELAY_MS);
+            }
+
+            // 3. Now measure the latency by starting up the hardware again.
+            long latency = getLatency();
+            if (latency < 0) {
+                setScore(getString(R.string.aq_fail));
+            } else {
+                setScore(latency < MAXIMUM_LATENCY_ALLOWED_MS
+                        ? getString(R.string.aq_pass)
+                        : getString(R.string.aq_fail));
+                setReport(String.format(getString(R.string.aq_cold_latency_report), latency,
+                        MAXIMUM_LATENCY_ALLOWED_MS));
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            setScore(getString(R.string.aq_fail));
+        } catch (ExecutionException e) {
+            setScore(getString(R.string.aq_fail));
+        } catch (TimeoutException e) {
+            setScore(getString(R.string.aq_fail));
+        } finally {
+            recordingTask.stopRecording();
+            executor.shutdown();
+            mTerminator.terminate(false);
+        }
+    }
+
+    @Override
+    public int getTimeout() {
+        return TEST_TIMEOUT_SECONDS;
+    }
+
+    /** Task that records for a given length of time. */
+    private class RecordingTask implements Callable<Long> {
+
+        private static final int READ_TIME = 25;
+
+        private final long mRecordMs;
+
+        private final int mSamplesToRead;
+
+        private final byte[] mBuffer;
+
+        private boolean mKeepRecording = true;
+
+        public RecordingTask(long recordMs) {
+            this.mRecordMs = recordMs;
+            this.mSamplesToRead = (READ_TIME * AudioQualityVerifierActivity.SAMPLE_RATE) / 1000;
+            this.mBuffer = new byte[mSamplesToRead * AudioQualityVerifierActivity.BYTES_PER_SAMPLE];
+        }
+
+        public Long call() throws Exception {
+            int minBufferSize = BUFFER_TIME_MS / 1000
+                    * AudioQualityVerifierActivity.SAMPLE_RATE
+                    * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
+            int bufferSize = Utils.getAudioRecordBufferSize(minBufferSize);
+            if (bufferSize < 0) {
+                setReport(getString(R.string.aq_audiorecord_buffer_size_error));
+                return -1L;
+            }
+
+            AudioRecord record = null;
+            try {
+                record = new AudioRecord(AudioSource.VOICE_RECOGNITION,
+                        AudioQualityVerifierActivity.SAMPLE_RATE,
+                        AudioFormat.CHANNEL_IN_MONO,
+                        AudioQualityVerifierActivity.AUDIO_FORMAT,
+                        bufferSize);
+
+                if (record.getRecordingState() != AudioRecord.STATE_INITIALIZED) {
+                    setReport(getString(R.string.aq_init_audiorecord_error));
+                    return -2L;
+                }
+
+                record.startRecording();
+                while (record.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
+                    // Wait until we can start recording...
+                    Utils.delay(DELAY_MS);
+                }
+
+                long startTime = System.currentTimeMillis();
+                int maxBytes = mSamplesToRead * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
+                while (true) {
+                    synchronized (this) {
+                        if (!mKeepRecording) {
+                            break;
+                        }
+                    }
+                    int numBytesRead = record.read(mBuffer, 0, maxBytes);
+                    if (numBytesRead < 0) {
+                        setReport(getString(R.string.aq_recording_error));
+                        return -3L;
+                    } else if (System.currentTimeMillis() - startTime >= mRecordMs) {
+                        return System.currentTimeMillis() - startTime;
+                    }
+                }
+
+                return -4L;
+            } finally {
+                if (record != null) {
+                    if (record.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
+                        record.stop();
+                    }
+                    record.release();
+                    record = null;
+                }
+            }
+        }
+
+        public void stopRecording() {
+            synchronized (this) {
+                mKeepRecording = false;
+            }
+        }
+    }
+
+    /**
+     * @return latency between starting to record and entering the record state or
+     *         -1 if an error occurred
+     */
+    private long getLatency() {
+        int minBufferSize = BUFFER_TIME_MS / 1000
+                * AudioQualityVerifierActivity.SAMPLE_RATE
+                * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
+        int bufferSize = Utils.getAudioRecordBufferSize(minBufferSize);
+        if (bufferSize < 0) {
+            setReport(String.format(getString(R.string.aq_audiorecord_buffer_size_error),
+                    bufferSize));
+            return -1;
+        }
+
+        AudioRecord record = null;
+        try {
+            record = new AudioRecord(AudioSource.VOICE_RECOGNITION,
+                    AudioQualityVerifierActivity.SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
+                    AudioQualityVerifierActivity.AUDIO_FORMAT, bufferSize);
+
+            if (record.getRecordingState() != AudioRecord.STATE_INITIALIZED) {
+                setReport(getString(R.string.aq_init_audiorecord_error));
+                return -1;
+            }
+
+            long startTime = System.currentTimeMillis();
+            record.startRecording();
+            while (record.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
+                Utils.delay(DELAY_MS);
+            }
+            long endTime = System.currentTimeMillis();
+
+            return endTime - startTime;
+        } finally {
+            if (record != null) {
+                if (record.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
+                    record.stop();
+                }
+                record.release();
+                record = null;
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/GainLinearityExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/GainLinearityExperiment.java
new file mode 100644
index 0000000..05f1602
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/GainLinearityExperiment.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 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.audioquality.experiments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
+import com.android.cts.verifier.audioquality.Utils;
+
+import android.content.Context;
+
+/**
+ * Experiment to test the linearity of the microphone gain response.
+ *
+ * This plays a sequence of identical stimuli at increasing volumes and then
+ * analyzes the set of recordings.
+ */
+public class GainLinearityExperiment extends SequenceExperiment {
+    private static final int LEVELS = 4;
+    private static final float DB_STEP_SIZE = 10.0f;
+    private static final float TOLERANCE = 2.0f; // Maximum allowed deviation from linearity in dB
+    private static final int STIM_NUM = 31;
+
+    private short[] mReference = null;
+
+    public GainLinearityExperiment() {
+        super(true);
+    }
+
+    @Override
+    protected String lookupName(Context context) {
+        return context.getString(R.string.aq_linearity_exp);
+    }
+
+    @Override
+    protected int getTrials() {
+        return LEVELS;
+    }
+
+    @Override
+    protected byte[] getStim(Context context, int trial) {
+        float db = (trial - (LEVELS - 1)) * DB_STEP_SIZE;
+        if (mReference == null) {
+            mReference = Utils.byteToShortArray(Utils.getStim(context, STIM_NUM));
+        }
+        short[] samples = Utils.scale(mReference, db);
+        return Utils.shortToByteArray(samples);
+    }
+
+    @Override
+    protected void compare(byte[][] stim, byte[][] record) {
+        short[][] pcms = new short[LEVELS][];
+        for (int i = 0; i < LEVELS; i++) {
+            pcms[i] = Utils.byteToShortArray(record[i]);
+        }
+        // We specify the middle stimulus (LEVELS / 2) as the "reference":
+        float deviation = mNative.linearityTest(pcms, AudioQualityVerifierActivity.SAMPLE_RATE,
+                DB_STEP_SIZE, LEVELS / 2);
+        if (deviation < 0.0f) {
+            setScore(getString(R.string.aq_fail));
+            setReport(String.format(getString(R.string.aq_linearity_report_error), deviation));
+        } else if (deviation > TOLERANCE) {
+            setScore(getString(R.string.aq_fail));
+            setReport(String.format(getString(R.string.aq_linearity_report_normal),
+                    deviation, TOLERANCE));
+        } else {
+            setScore(getString(R.string.aq_pass));
+            setReport(String.format(getString(R.string.aq_linearity_report_normal),
+                    deviation, TOLERANCE));
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/GlitchExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/GlitchExperiment.java
new file mode 100644
index 0000000..143c4a0
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/GlitchExperiment.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2010 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.audioquality.experiments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
+import com.android.cts.verifier.audioquality.Native;
+import com.android.cts.verifier.audioquality.Utils;
+
+import android.content.Context;
+
+import java.util.Random;
+
+/**
+ * Experiment to detect glitches or dropouts in the signal.
+ * A number of artificial glitches can be optionally introduced.
+ */
+public class GlitchExperiment extends LoopbackExperiment {
+    private static final float FREQ = 625.0f;
+    private static final float AMPL = 10000.0f;
+    private static final float ONSET_THRESH = 80.0f;
+    private static final float SNR_THRESH = 20.0f;
+    private static final float RAMP = 0.01f;
+    private static final float DURATION = 3.0f;
+
+    private int mArtificialGlitches;
+
+    public GlitchExperiment(int artificialGlitches) {
+        super(true);
+        mArtificialGlitches = artificialGlitches;
+    }
+
+    @Override
+    protected String lookupName(Context context) {
+        String s = context.getString(R.string.aq_glitch_exp);
+        if (mArtificialGlitches > 0) {
+            s += " (" + mArtificialGlitches + ")";
+        }
+        return s;
+    }
+
+    @Override
+    protected byte[] getStim(Context context) {
+        short[] sinusoid = mNative.generateSinusoid(FREQ, DURATION,
+                AudioQualityVerifierActivity.SAMPLE_RATE, AMPL, RAMP);
+        addGlitches(sinusoid);
+        return Utils.shortToByteArray(sinusoid);
+    }
+
+    private void addGlitches(short[] samples) {
+        Random random = new Random();
+        for (int i = 0; i < mArtificialGlitches; i++) {
+            samples[random.nextInt(samples.length)] = 0;
+        }
+    }
+
+    @Override
+    protected void compare(byte[] stim, byte[] record) {
+        int targetMin = mArtificialGlitches > 0 ? 1 : 0;
+        int targetMax = mArtificialGlitches;
+        short[] pcm = Utils.byteToShortArray(record);
+        float[] ret = mNative.glitchTest(AudioQualityVerifierActivity.SAMPLE_RATE, FREQ,
+                ONSET_THRESH, SNR_THRESH, pcm);
+        int glitches = Math.round(ret[Native.GLITCH_COUNT]);
+        float error = ret[Native.GLITCH_ERROR];
+        float duration = ret[Native.GLITCH_DURATION];
+        if (error < 0.0f) {
+            setScore(getString(R.string.aq_fail));
+            setReport(getString(R.string.aq_glitch_report_error));
+        } else {
+            if (glitches > targetMax || glitches < targetMin) {
+                setScore(getString(R.string.aq_fail));
+            } else {
+                setScore(getString(R.string.aq_pass));
+            }
+            if (targetMin == targetMax) {
+                setReport(String.format(getString(R.string.aq_glitch_report_exact),
+                        glitches, targetMax, duration));
+            } else {
+                setReport(String.format(getString(R.string.aq_glitch_report_range),
+                        glitches, targetMin, targetMax, duration));
+            }
+        }
+   }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/LoopbackExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/LoopbackExperiment.java
new file mode 100644
index 0000000..16039cb
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/LoopbackExperiment.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2010 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.audioquality.experiments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
+import com.android.cts.verifier.audioquality.Experiment;
+import com.android.cts.verifier.audioquality.Utils;
+
+import android.content.Context;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.util.Log;
+
+/**
+ * LoopbackExperiment represents a general class of experiments, all of which
+ * comprise playing an audio stimulus of some kind, whilst simultaneously
+ * recording from the microphone. The recording is then analyzed to determine
+ * the test results (score and report).
+ */
+public class LoopbackExperiment extends Experiment {
+    protected static final int TIMEOUT = 10;
+
+    // Amount of silence in ms before and after playback
+    protected static final int END_DELAY_MS = 500;
+
+    private Recorder mRecorder = null;
+
+    public LoopbackExperiment(boolean enable) {
+        super(enable);
+    }
+
+    protected byte[] getStim(Context context) {
+        int stimNum = 2;
+        byte[] data = Utils.getStim(context, stimNum);
+        return data;
+    }
+
+    @Override
+    public void run() {
+        byte[] playbackData = getStim(mContext);
+        byte[] recordedData = loopback(playbackData);
+
+        compare(playbackData, recordedData);
+        setRecording(recordedData);
+        mTerminator.terminate(false);
+    }
+
+    protected byte[] loopback(byte[] playbackData) {
+        int samples = playbackData.length / 2;
+        int duration = (samples * 1000) / AudioQualityVerifierActivity.SAMPLE_RATE; // In ms
+        int padSamples = (END_DELAY_MS * AudioQualityVerifierActivity.SAMPLE_RATE) / 1000;
+        int totalSamples = samples + 2 * padSamples;
+        byte[] recordedData = new byte[totalSamples * 2];
+
+        mRecorder = new Recorder(recordedData, totalSamples);
+        mRecorder.start();
+        Utils.delay(END_DELAY_MS);
+
+        Utils.playRaw(playbackData);
+
+        int timeout = duration + 2 * END_DELAY_MS;
+        try {
+            mRecorder.join(timeout);
+        } catch (InterruptedException e) {}
+
+        return recordedData;
+    }
+
+    protected void compare(byte[] stim, byte[] record) {
+        setScore(getString(R.string.aq_complete));
+        setReport(getString(R.string.aq_loopback_report));
+    }
+
+    private void halt() {
+        if (mRecorder != null) {
+            mRecorder.halt();
+        }
+    }
+
+    @Override
+    public void cancel() {
+        super.cancel();
+        halt();
+    }
+
+    @Override
+    public void stop() {
+        super.stop();
+        halt();
+    }
+
+    @Override
+    public int getTimeout() {
+        return TIMEOUT;
+    }
+
+    /* Class which records audio in a background thread, to fill the supplied buffer. */
+    class Recorder extends Thread {
+        private AudioRecord mRecord;
+        private int mSamples;
+        private byte[] mBuffer;
+        private boolean mProceed;
+
+        Recorder(byte[] buffer, int samples) {
+            mBuffer = buffer;
+            mSamples = samples;
+            mProceed = true;
+        }
+
+        public void halt() {
+            mProceed = false;
+        }
+
+        @Override
+        public void run() {
+            final int minBufferSize = AudioQualityVerifierActivity.SAMPLE_RATE
+                    * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
+            final int bufferSize = Utils.getAudioRecordBufferSize(minBufferSize);
+
+            mRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_RECOGNITION,
+                    AudioQualityVerifierActivity.SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
+                    AudioQualityVerifierActivity.AUDIO_FORMAT, bufferSize);
+            if (mRecord.getState() != AudioRecord.STATE_INITIALIZED) {
+                Log.e(TAG, "Couldn't open audio for recording");
+                return;
+            }
+            mRecord.startRecording();
+            if (mRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
+                Log.e(TAG, "Couldn't record");
+                return;
+            }
+
+            captureLoop();
+
+            mRecord.stop();
+            mRecord.release();
+            mRecord = null;
+        }
+
+        private void captureLoop() {
+            int totalBytes = mSamples * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
+            Log.i(TAG, "Recording " + totalBytes + " bytes");
+            int position = 0;
+            int bytes;
+            while (position < totalBytes && mProceed) {
+                bytes = mRecord.read(mBuffer, position, totalBytes - position);
+                if (bytes < 0) {
+                    if (bytes == AudioRecord.ERROR_INVALID_OPERATION) {
+                        Log.e(TAG, "Recording object not initalized");
+                    } else if (bytes == AudioRecord.ERROR_BAD_VALUE) {
+                        Log.e(TAG, "Invalid recording parameters");
+                    } else {
+                        Log.e(TAG, "Error during recording");
+                    }
+                    return;
+                }
+                position += bytes;
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/OverflowExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/OverflowExperiment.java
new file mode 100644
index 0000000..71deac8
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/OverflowExperiment.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 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.audioquality.experiments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
+import com.android.cts.verifier.audioquality.CalibrateVolumeActivity;
+import com.android.cts.verifier.audioquality.Native;
+import com.android.cts.verifier.audioquality.Utils;
+
+import android.content.Context;
+
+/**
+ * Experiment to test the clipping behaviour of the microphone.
+ *
+ * The stimulus is sinusoidal, and calculated to cause clipping for
+ * part of the waveform. The experiment looks for strange clipping behaviour
+ * by checking if the signal has any discontinuities (which might indicate
+ * wraparound, for example).
+ */
+public class OverflowExperiment extends LoopbackExperiment {
+    private static final float FREQ = 250.0f;
+    private static final float AMPL = 32768.0f * 1.1f * CalibrateVolumeActivity.OUTPUT_AMPL
+            / CalibrateVolumeActivity.TARGET_AMPL;
+    private static final float DURATION = 3.0f; // Duration of tone in seconds
+    private static final float MIN_DURATION = DURATION * 0.9f;
+    private static final float RAMP = 0.01f;
+
+    public OverflowExperiment() {
+        super(true);
+    }
+
+    @Override
+    protected String lookupName(Context context) {
+        return context.getString(R.string.aq_overflow_exp);
+    }
+
+    @Override
+    protected byte[] getStim(Context context) {
+        short[] sinusoid = mNative.generateSinusoid(FREQ, DURATION,
+                AudioQualityVerifierActivity.SAMPLE_RATE, AMPL, RAMP);
+        return Utils.shortToByteArray(sinusoid);
+    }
+
+    @Override
+    protected void compare(byte[] stim, byte[] record) {
+        short[] pcm = Utils.byteToShortArray(record);
+        float[] ret = mNative.overflowCheck(pcm, AudioQualityVerifierActivity.SAMPLE_RATE);
+        int numDeltas = Math.round(ret[0]);
+        float error = ret[Native.OVERFLOW_ERROR];
+        float duration = ret[Native.OVERFLOW_DURATION];
+        float minPeak = ret[Native.OVERFLOW_MIN];
+        float maxPeak = ret[Native.OVERFLOW_MAX];
+
+        if (error < 0.0f) {
+            setScore(getString(R.string.aq_fail));
+            setReport(getString(R.string.aq_overflow_report_error));
+        } else if (duration < MIN_DURATION) {
+            setScore(getString(R.string.aq_fail));
+            setReport(String.format(getString(R.string.aq_overflow_report_short),
+                    DURATION, duration));
+        } else if (numDeltas > 0) {
+            setScore(getString(R.string.aq_fail));
+            setReport(String.format(getString(R.string.aq_overflow_report_fail),
+                    numDeltas, duration, minPeak, maxPeak));
+        } else {
+            setScore(getString(R.string.aq_pass));
+            setReport(String.format(getString(R.string.aq_overflow_report_pass),
+                    numDeltas, duration, minPeak, maxPeak));
+        }
+   }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SequenceExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SequenceExperiment.java
new file mode 100644
index 0000000..0a3846c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SequenceExperiment.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 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.audioquality.experiments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.audioquality.Utils;
+
+import android.content.Context;
+
+/**
+ * An extension to LoopbackExperiment, in which the "playback and record"
+ * cycle is repeated several times. A family of stimuli is defined, and
+ * the experiment outcome may depend on the whole sequence of recordings.
+ */
+public class SequenceExperiment extends LoopbackExperiment {
+    public SequenceExperiment(boolean enable) {
+        super(enable);
+    }
+
+    protected int getTrials() {
+        return 1;
+    }
+
+    protected byte[] getStim(Context context, int trial) {
+        int stimNum = 2;
+        byte[] data = Utils.getStim(context, stimNum);
+        return data;
+    }
+
+    protected void compare(byte[][] stim, byte[][] record) {
+        setScore(getString(R.string.aq_complete));
+        setReport(getString(R.string.aq_loopback_report));
+    }
+
+    @Override
+    public void run() {
+        int n = getTrials();
+        byte[][] playbackData = new byte[n][];
+        byte[][] recordedData = new byte[n][];
+        for (int trial = 0; trial < n; trial++) {
+            playbackData[trial] = getStim(mContext, trial);
+            recordedData[trial] = loopback(playbackData[trial]);
+            setRecording(recordedData[trial], trial);
+        }
+        compare(playbackData, recordedData);
+        mTerminator.terminate(false);
+    }
+
+    @Override
+    public int getTimeout() {
+        return TIMEOUT * getTrials();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SoundLevelExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SoundLevelExperiment.java
new file mode 100644
index 0000000..4435f31
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SoundLevelExperiment.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 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.audioquality.experiments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
+import com.android.cts.verifier.audioquality.CalibrateVolumeActivity;
+import com.android.cts.verifier.audioquality.Native;
+import com.android.cts.verifier.audioquality.Utils;
+
+import android.content.Context;
+
+/**
+ * Experiment to verify that the sound level has been correctly set.
+ *
+ * This experiment should be run first; if it fails the others may
+ * fail also.
+ */
+public class SoundLevelExperiment extends LoopbackExperiment {
+    private static final float ONSET_THRESH = 10.0f;
+    private static final int DURATION = 2;
+    private static final float TOLERANCE = 1.05f;
+    private static final float FREQ = 625.0f;
+    private static final float RAMP = 0.0f;
+
+    public SoundLevelExperiment() {
+        super(true);
+    }
+
+    @Override
+    protected String lookupName(Context context) {
+        return context.getString(R.string.aq_sound_level_exp);
+    }
+
+    @Override
+    protected byte[] getStim(Context context) {
+        if (CalibrateVolumeActivity.USE_PINK) {
+            return Utils.getPinkNoise(context, CalibrateVolumeActivity.OUTPUT_AMPL, DURATION);
+        } else {
+            short[] sinusoid = mNative.generateSinusoid(FREQ, DURATION,
+                    AudioQualityVerifierActivity.SAMPLE_RATE, CalibrateVolumeActivity.OUTPUT_AMPL, RAMP);
+            return Utils.shortToByteArray(sinusoid);
+        }
+    }
+
+    @Override
+    protected void compare(byte[] stim, byte[] record) {
+        short[] pcm = Utils.byteToShortArray(record);
+        float[] results = mNative.measureRms(pcm, AudioQualityVerifierActivity.SAMPLE_RATE, ONSET_THRESH);
+        float rms = results[Native.MEASURE_RMS_RMS];
+        float duration = results[Native.MEASURE_RMS_DURATION];
+        String delta;
+        if (rms * TOLERANCE < CalibrateVolumeActivity.TARGET_RMS) {
+            setScore(getString(R.string.aq_fail));
+            delta = getString(R.string.aq_status_low);
+        } else if (rms > CalibrateVolumeActivity.TARGET_RMS * TOLERANCE) {
+            setScore(getString(R.string.aq_fail));
+            delta = getString(R.string.aq_status_high);
+        } else {
+            setScore(getString(R.string.aq_pass));
+            delta = getString(R.string.aq_status_ok);
+        }
+        setReport(delta + ".\n" + String.format(getString(R.string.aq_level_report),
+                rms, CalibrateVolumeActivity.TARGET_RMS,
+                100.0f * (TOLERANCE - 1.0f), duration));
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SpectrumShapeExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SpectrumShapeExperiment.java
new file mode 100644
index 0000000..148fb47
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SpectrumShapeExperiment.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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.audioquality.experiments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
+import com.android.cts.verifier.audioquality.Native;
+import com.android.cts.verifier.audioquality.Utils;
+
+import android.content.Context;
+
+/**
+ * Experiment to check that the frequency profile of the recorded signal
+ * does not differ too much from the stimulus.
+ */
+public class SpectrumShapeExperiment extends LoopbackExperiment {
+    private static final float MAX_RMS_DEVIATION = 7.0f;
+    private static final int AMPL = 10000;
+    private static final int DURATION = 3;
+
+    public SpectrumShapeExperiment() {
+        super(true);
+    }
+
+    @Override
+    protected String lookupName(Context context) {
+        return context.getString(R.string.aq_spectrum_shape_exp);
+    }
+
+   @Override
+    protected byte[] getStim(Context context) {
+        return Utils.getPinkNoise(context, AMPL, DURATION);
+    }
+
+    @Override
+    protected void compare(byte[] stim, byte[] record) {
+        short[] pcm = Utils.byteToShortArray(record);
+        short[] refPcm = Utils.byteToShortArray(stim);
+        float[] ret = mNative.compareSpectra(pcm, refPcm, AudioQualityVerifierActivity.SAMPLE_RATE);
+        float maxDeviation = ret[Native.SPECTRUM_MAX_DEVIATION];
+        float error = ret[Native.SPECTRUM_ERROR];
+        float rmsDeviation = ret[Native.SPECTRUM_RMS_DEVIATION];
+        if (error < 0.0f) {
+            setScore(getString(R.string.aq_fail));
+            setReport(getString(R.string.aq_spectrum_report_error));
+        } else if (rmsDeviation > MAX_RMS_DEVIATION) {
+            setScore(getString(R.string.aq_fail));
+            setReport(String.format(getString(R.string.aq_spectrum_report_normal),
+                    rmsDeviation, MAX_RMS_DEVIATION));
+        } else {
+            setScore(getString(R.string.aq_pass));
+            setReport(String.format(getString(R.string.aq_spectrum_report_normal),
+                    rmsDeviation, MAX_RMS_DEVIATION));
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/WarmLatencyExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/WarmLatencyExperiment.java
new file mode 100644
index 0000000..88aaf8c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/WarmLatencyExperiment.java
@@ -0,0 +1,337 @@
+/*
+ * 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.audioquality.experiments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
+import com.android.cts.verifier.audioquality.Experiment;
+import com.android.cts.verifier.audioquality.Native;
+import com.android.cts.verifier.audioquality.Utils;
+
+import android.content.Context;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.AudioTrack;
+import android.media.MediaRecorder.AudioSource;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * {@link Experiment} that measures how long it takes for a stimulus emitted
+ * by a warmed up {@link AudioTrack} to be recorded by a warmed up
+ * {@link AudioRecord} instance.
+ */
+public class WarmLatencyExperiment extends Experiment {
+
+    /** Milliseconds to wait before playing the sound. */
+    private static final int DELAY_TIME = 2000;
+
+    /** Target RMS value to detect before quitting the experiment. */
+    private static final float TARGET_RMS = 4000;
+
+    /** Target latency to react to the sound. */
+    private static final long TARGET_LATENCY_MS = 200;
+
+    private static final int CHANNEL_IN_CONFIG = AudioFormat.CHANNEL_IN_MONO;
+    private static final int CHANNEL_OUT_CONFIG = AudioFormat.CHANNEL_OUT_MONO;
+    private static final float FREQ = 625.0f;
+    private static final int DURATION = 1;
+    private static final int OUTPUT_AMPL = 5000;
+    private static final float RAMP = 0.0f;
+    private static final int BUFFER_TIME_MS = 100;
+    private static final int READ_TIME = 25;
+
+    public WarmLatencyExperiment() {
+        super(true);
+    }
+
+    @Override
+    protected String lookupName(Context context) {
+        return context.getString(R.string.aq_warm_latency);
+    }
+
+    @Override
+    public void run() {
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        CyclicBarrier barrier = new CyclicBarrier(2);
+        PlaybackTask playbackTask = new PlaybackTask(barrier);
+        RecordingTask recordingTask = new RecordingTask(barrier);
+
+        Future<Long> playbackTimeFuture = executor.submit(playbackTask);
+        Future<Long> recordTimeFuture = executor.submit(recordingTask);
+
+        try {
+            // Get the time when the sound is detected or throw an exception...
+            long recordTime = recordTimeFuture.get(DELAY_TIME * 2, TimeUnit.MILLISECONDS);
+
+            // Stop the playback now since the sound was detected. Get the time playback started.
+            playbackTask.stopPlaying();
+            long playbackTime = playbackTimeFuture.get();
+
+            if (recordTime == -1 || playbackTime == -1) {
+                setScore(getString(R.string.aq_fail));
+            } else {
+                long latency = recordTime - playbackTime;
+                setScore(latency < TARGET_LATENCY_MS
+                        ? getString(R.string.aq_pass)
+                        : getString(R.string.aq_fail));
+                setReport(String.format(getString(R.string.aq_warm_latency_report_normal),
+                        latency));
+            }
+        } catch (InterruptedException e) {
+            setExceptionReport(e);
+        } catch (ExecutionException e) {
+            setExceptionReport(e);
+        } catch (TimeoutException e) {
+            setScore(getString(R.string.aq_fail));
+            setReport(String.format(getString(R.string.aq_warm_latency_report_error),
+                    recordingTask.getLastRms(), TARGET_RMS));
+        } finally {
+            playbackTask.stopPlaying();
+            recordingTask.stopRecording();
+            mTerminator.terminate(false);
+        }
+    }
+
+    private void setExceptionReport(Exception e) {
+        setScore(getString(R.string.aq_fail));
+        setReport(String.format(getString(R.string.aq_exception_error), e.getClass().getName()));
+    }
+
+    @Override
+    public int getTimeout() {
+        return 10; // seconds
+    }
+
+    /**
+     * Task that plays a sinusoid after playing silence for a couple of seconds.
+     * Returns the playback start time.
+     */
+    private class PlaybackTask implements Callable<Long> {
+
+        private final byte[] mData;
+
+        private final int mBufferSize;
+
+        private final CyclicBarrier mReadyBarrier;
+
+        private int mPosition;
+
+        private boolean mKeepPlaying = true;
+
+        public PlaybackTask(CyclicBarrier barrier) {
+            this.mData = getAudioData();
+            this.mBufferSize = getBufferSize();
+            this.mReadyBarrier = barrier;
+        }
+
+        private byte[] getAudioData() {
+            short[] sinusoid = mNative.generateSinusoid(FREQ, DURATION,
+                    AudioQualityVerifierActivity.SAMPLE_RATE, OUTPUT_AMPL, RAMP);
+            return Utils.shortToByteArray(sinusoid);
+        }
+
+        private int getBufferSize() {
+            int minBufferSize = (BUFFER_TIME_MS * AudioQualityVerifierActivity.SAMPLE_RATE
+                    * AudioQualityVerifierActivity.BYTES_PER_SAMPLE) / 1000;
+            return Utils.getAudioTrackBufferSize(minBufferSize);
+        }
+
+        public Long call() throws Exception {
+            if (mBufferSize == -1) {
+                setReport(getString(R.string.aq_audiotrack_buffer_size_error));
+                return -1l;
+            }
+
+            AudioTrack track = null;
+            try {
+                track = new AudioTrack(AudioManager.STREAM_MUSIC,
+                        AudioQualityVerifierActivity.SAMPLE_RATE, CHANNEL_OUT_CONFIG,
+                        AudioQualityVerifierActivity.AUDIO_FORMAT, mBufferSize,
+                        AudioTrack.MODE_STREAM);
+
+                if (track.getPlayState() != AudioTrack.STATE_INITIALIZED) {
+                    setReport(getString(R.string.aq_init_audiotrack_error));
+                    return -1l;
+                }
+
+                track.play();
+                while (track.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
+                    // Wait until we've started playing...
+                }
+
+                // Wait until the recording thread has started and is recording...
+                mReadyBarrier.await(1, TimeUnit.SECONDS);
+
+                long time = System.currentTimeMillis();
+                while (System.currentTimeMillis() - time < DELAY_TIME) {
+                    synchronized (this) {
+                        if (!mKeepPlaying) {
+                            break;
+                        }
+                    }
+                    // Play nothing...
+                }
+
+                long playTime = System.currentTimeMillis();
+                writeAudio(track);
+                while (true) {
+                    synchronized (this) {
+                        if (!mKeepPlaying) {
+                            break;
+                        }
+                    }
+                    writeAudio(track);
+                }
+
+                return playTime;
+            } finally {
+                if (track != null) {
+                    if (track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
+                        track.stop();
+                    }
+                    track.release();
+                    track = null;
+                }
+            }
+        }
+
+        private void writeAudio(AudioTrack track) {
+            int length = mData.length;
+            int writeBytes = Math.min(mBufferSize, length - mPosition);
+            int numBytesWritten = track.write(mData, mPosition, writeBytes);
+            if (numBytesWritten < 0) {
+                throw new IllegalStateException("Couldn't write any data to the track!");
+            } else {
+                mPosition += numBytesWritten;
+                if (mPosition == length) {
+                    mPosition = 0;
+                }
+            }
+        }
+
+        public void stopPlaying() {
+            synchronized (this) {
+                mKeepPlaying = false;
+            }
+        }
+    }
+
+    /** Task that records until detecting a sound of the target RMS. Returns the detection time. */
+    private class RecordingTask implements Callable<Long> {
+
+        private final int mSamplesToRead;
+
+        private final byte[] mBuffer;
+
+        private final CyclicBarrier mBarrier;
+
+        private boolean mKeepRecording = true;
+
+        private float mLastRms = 0.0f;
+
+        public RecordingTask(CyclicBarrier barrier) {
+            this.mSamplesToRead = (READ_TIME * AudioQualityVerifierActivity.SAMPLE_RATE) / 1000;
+            this.mBuffer = new byte[mSamplesToRead * AudioQualityVerifierActivity.BYTES_PER_SAMPLE];
+            this.mBarrier = barrier;
+        }
+
+        public Long call() throws Exception {
+            int minBufferSize = BUFFER_TIME_MS / 1000
+                    * AudioQualityVerifierActivity.SAMPLE_RATE
+                    * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
+            int bufferSize = Utils.getAudioRecordBufferSize(minBufferSize);
+            if (bufferSize < 0) {
+                setReport(getString(R.string.aq_audiorecord_buffer_size_error));
+                return -1l;
+            }
+
+            long recordTime = -1;
+            AudioRecord record = null;
+            try {
+                record = new AudioRecord(AudioSource.VOICE_RECOGNITION,
+                        AudioQualityVerifierActivity.SAMPLE_RATE, CHANNEL_IN_CONFIG,
+                        AudioQualityVerifierActivity.AUDIO_FORMAT, bufferSize);
+
+                if (record.getRecordingState() != AudioRecord.STATE_INITIALIZED) {
+                    setReport(getString(R.string.aq_init_audiorecord_error));
+                    return -1l;
+                }
+
+                record.startRecording();
+                while (record.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
+                    // Wait until we can start recording...
+                }
+
+                // Wait until the playback thread has started and is playing...
+                mBarrier.await(1, TimeUnit.SECONDS);
+
+                int maxBytes = mSamplesToRead * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
+                while (true) {
+                    synchronized (this) {
+                        if (!mKeepRecording) {
+                            break;
+                        }
+                    }
+                    int numBytesRead = record.read(mBuffer, 0, maxBytes);
+                    if (numBytesRead < 0) {
+                        setReport(getString(R.string.aq_recording_error));
+                        return -1l;
+                    } else if (numBytesRead > 2) {
+                        // TODO: Could be improved to use a sliding window?
+                        short[] samples = Utils.byteToShortArray(mBuffer, 0, numBytesRead);
+                        float[] results = mNative.measureRms(samples,
+                                AudioQualityVerifierActivity.SAMPLE_RATE, -1.0f);
+                        mLastRms = results[Native.MEASURE_RMS_RMS];
+                        if (mLastRms >= TARGET_RMS) {
+                            recordTime = System.currentTimeMillis();
+                            break;
+                        }
+                    }
+                }
+
+                return recordTime;
+            } finally {
+                if (record != null) {
+                    if (record.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
+                        record.stop();
+                    }
+                    record.release();
+                    record = null;
+                }
+            }
+        }
+
+        public float getLastRms() {
+            return mLastRms;
+        }
+
+        public void stopRecording() {
+            synchronized (this) {
+                mKeepRecording = false;
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothChatService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothChatService.java
new file mode 100644
index 0000000..05c50e3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothChatService.java
@@ -0,0 +1,500 @@
+/*
+ * 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;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.UUID;
+
+/**
+ * This class does all the work for setting up and managing Bluetooth
+ * connections with other devices. It has a thread that listens for
+ * incoming connections, a thread for connecting with a device, and a
+ * thread for performing data transmissions when connected.
+ */
+public class BluetoothChatService {
+    // Message types sent from the BluetoothChatService Handler
+    public static final int MESSAGE_STATE_CHANGE = 1;
+    public static final int MESSAGE_READ = 2;
+    public static final int MESSAGE_WRITE = 3;
+    public static final int MESSAGE_DEVICE_NAME = 4;
+    public static final int MESSAGE_TOAST = 5;
+
+    // Key names received from the BluetoothChatService Handler
+    public static final String DEVICE_NAME = "device_name";
+    public static final String TOAST = "toast";
+
+    // Debugging
+    private static final String TAG = "CtsBluetoothChatService";
+    private static final boolean D = true;
+
+    // Name for the SDP record when creating server socket
+    private static final String NAME_SECURE = "CtsBluetoothChatSecure";
+    private static final String NAME_INSECURE = "CtsBluetoothChatInsecure";
+
+    // Unique UUID for this application
+    private static final UUID MY_UUID_SECURE =
+        UUID.fromString("8591d757-18ee-45e1-9b12-92875d06ba23");
+    private static final UUID MY_UUID_INSECURE =
+        UUID.fromString("301c214f-91a2-43bf-a795-09d1198a81a7");
+
+    // Member fields
+    private final BluetoothAdapter mAdapter;
+    private final Handler mHandler;
+    private AcceptThread mSecureAcceptThread;
+    private AcceptThread mInsecureAcceptThread;
+    private ConnectThread mConnectThread;
+    private ConnectedThread mConnectedThread;
+    private int mState;
+
+    // Constants that indicate the current connection state
+    public static final int STATE_NONE = 0;       // we're doing nothing
+    public static final int STATE_LISTEN = 1;     // now listening for incoming connections
+    public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
+    public static final int STATE_CONNECTED = 3;  // now connected to a remote device
+
+    /**
+     * Constructor. Prepares a new BluetoothChat session.
+     * @param context  The UI Activity Context
+     * @param handler  A Handler to send messages back to the UI Activity
+     */
+    public BluetoothChatService(Context context, Handler handler) {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mState = STATE_NONE;
+        mHandler = handler;
+    }
+
+    /**
+     * Set the current state of the chat connection
+     * @param state  An integer defining the current connection state
+     */
+    private synchronized void setState(int state) {
+        if (D) Log.d(TAG, "setState() " + mState + " -> " + state);
+        mState = state;
+
+        // Give the new state to the Handler so the UI Activity can update
+        mHandler.obtainMessage(MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
+    }
+
+    /**
+     * Return the current connection state. */
+    public synchronized int getState() {
+        return mState;
+    }
+
+    /**
+     * Start the chat service. Specifically start AcceptThread to begin a
+     * session in listening (server) mode. Called by the Activity onResume() */
+    public synchronized void start(boolean secure) {
+        if (D) Log.d(TAG, "start secure: " + secure + UUID.randomUUID() + " - " + UUID.randomUUID());
+
+        // Cancel any thread attempting to make a connection
+        if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
+
+        // Cancel any thread currently running a connection
+        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
+
+        setState(STATE_LISTEN);
+
+        // Start the thread to listen on a BluetoothServerSocket
+        if (secure && mSecureAcceptThread == null) {
+            mSecureAcceptThread = new AcceptThread(true);
+            mSecureAcceptThread.start();
+        }
+        if (!secure && mInsecureAcceptThread == null) {
+            mInsecureAcceptThread = new AcceptThread(false);
+            mInsecureAcceptThread.start();
+        }
+    }
+
+    /**
+     * Start the ConnectThread to initiate a connection to a remote device.
+     * @param device  The BluetoothDevice to connect
+     * @param secure Socket Security type - Secure (true) , Insecure (false)
+     */
+    public synchronized void connect(BluetoothDevice device, boolean secure) {
+        if (D) Log.d(TAG, "connect to: " + device);
+
+        // Cancel any thread attempting to make a connection
+        if (mState == STATE_CONNECTING) {
+            if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
+        }
+
+        // Cancel any thread currently running a connection
+        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
+
+        // Start the thread to connect with the given device
+        mConnectThread = new ConnectThread(device, secure);
+        mConnectThread.start();
+        setState(STATE_CONNECTING);
+    }
+
+    /**
+     * Start the ConnectedThread to begin managing a Bluetooth connection
+     * @param socket  The BluetoothSocket on which the connection was made
+     * @param device  The BluetoothDevice that has been connected
+     */
+    public synchronized void connected(BluetoothSocket socket, BluetoothDevice
+            device, final String socketType) {
+        if (D) Log.d(TAG, "connected, Socket Type: " + socketType);
+
+        // Cancel the thread that completed the connection
+        if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
+
+        // Cancel any thread currently running a connection
+        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
+
+        // Cancel the accept thread because we only want to connect to one device
+        if (mSecureAcceptThread != null) {
+            mSecureAcceptThread.cancel();
+            mSecureAcceptThread = null;
+        }
+        if (mInsecureAcceptThread != null) {
+            mInsecureAcceptThread.cancel();
+            mInsecureAcceptThread = null;
+        }
+
+        // Start the thread to manage the connection and perform transmissions
+        mConnectedThread = new ConnectedThread(socket, socketType);
+        mConnectedThread.start();
+
+        // Send the name of the connected device back to the UI Activity
+        Message msg = mHandler.obtainMessage(MESSAGE_DEVICE_NAME);
+        Bundle bundle = new Bundle();
+        bundle.putString(DEVICE_NAME, device.getName());
+        msg.setData(bundle);
+        mHandler.sendMessage(msg);
+
+        setState(STATE_CONNECTED);
+    }
+
+    /**
+     * Stop all threads
+     */
+    public synchronized void stop() {
+        if (D) Log.d(TAG, "stop");
+
+        if (mConnectThread != null) {
+            mConnectThread.cancel();
+            mConnectThread = null;
+        }
+
+        if (mConnectedThread != null) {
+            mConnectedThread.cancel();
+            mConnectedThread = null;
+        }
+
+        if (mSecureAcceptThread != null) {
+            mSecureAcceptThread.cancel();
+            mSecureAcceptThread = null;
+        }
+
+        if (mInsecureAcceptThread != null) {
+            mInsecureAcceptThread.cancel();
+            mInsecureAcceptThread = null;
+        }
+        setState(STATE_NONE);
+    }
+
+    /**
+     * Write to the ConnectedThread in an unsynchronized manner
+     * @param out The bytes to write
+     * @see ConnectedThread#write(byte[])
+     */
+    public void write(byte[] out) {
+        // Create temporary object
+        ConnectedThread r;
+        // Synchronize a copy of the ConnectedThread
+        synchronized (this) {
+            if (mState != STATE_CONNECTED) return;
+            r = mConnectedThread;
+        }
+        // Perform the write unsynchronized
+        r.write(out);
+    }
+
+    /**
+     * Indicate that the connection attempt failed and notify the UI Activity.
+     */
+    private void connectionFailed() {
+        // Send a failure message back to the Activity
+        Message msg = mHandler.obtainMessage(MESSAGE_TOAST);
+        Bundle bundle = new Bundle();
+        bundle.putString(TOAST, "Unable to connect device");
+        msg.setData(bundle);
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Indicate that the connection was lost and notify the UI Activity.
+     */
+    private void connectionLost() {
+        // Send a failure message back to the Activity
+        Message msg = mHandler.obtainMessage(MESSAGE_TOAST);
+        Bundle bundle = new Bundle();
+        bundle.putString(TOAST, "Device connection was lost");
+        msg.setData(bundle);
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * This thread runs while listening for incoming connections. It behaves
+     * like a server-side client. It runs until a connection is accepted
+     * (or until cancelled).
+     */
+    private class AcceptThread extends Thread {
+        // The local server socket
+        private final BluetoothServerSocket mmServerSocket;
+        private String mSocketType;
+
+        public AcceptThread(boolean secure) {
+            BluetoothServerSocket tmp = null;
+            mSocketType = secure ? "Secure" : "Insecure";
+
+            // Create a new listening server socket
+            try {
+                if (secure) {
+                    tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
+                        MY_UUID_SECURE);
+                } else {
+                    tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
+                            NAME_INSECURE, MY_UUID_INSECURE);
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Socket Type: " + mSocketType + " listen() failed", e);
+            }
+            mmServerSocket = tmp;
+        }
+
+        public void run() {
+            if (D) Log.d(TAG, "Socket Type: " + mSocketType +
+                    " BEGIN mAcceptThread" + this);
+            setName("AcceptThread" + mSocketType);
+
+            BluetoothSocket socket = null;
+
+            // Listen to the server socket if we're not connected
+            while (mState != STATE_CONNECTED) {
+                try {
+                    // This is a blocking call and will only return on a
+                    // successful connection or an exception
+                    socket = mmServerSocket.accept();
+                } catch (IOException e) {
+                    Log.e(TAG, "Socket Type: " + mSocketType + " accept() failed", e);
+                    break;
+                }
+
+                // If a connection was accepted
+                if (socket != null) {
+                    synchronized (BluetoothChatService.this) {
+                        switch (mState) {
+                        case STATE_LISTEN:
+                        case STATE_CONNECTING:
+                            // Situation normal. Start the connected thread.
+                            connected(socket, socket.getRemoteDevice(),
+                                    mSocketType);
+                            break;
+                        case STATE_NONE:
+                        case STATE_CONNECTED:
+                            // Either not ready or already connected. Terminate new socket.
+                            try {
+                                socket.close();
+                            } catch (IOException e) {
+                                Log.e(TAG, "Could not close unwanted socket", e);
+                            }
+                            break;
+                        }
+                    }
+                } else {
+                    Log.i(TAG, "Got null socket");
+                }
+            }
+            if (D) Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
+
+        }
+
+        public void cancel() {
+            if (D) Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
+            try {
+                mmServerSocket.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
+            }
+        }
+    }
+
+
+    /**
+     * This thread runs while attempting to make an outgoing connection
+     * with a device. It runs straight through; the connection either
+     * succeeds or fails.
+     */
+    private class ConnectThread extends Thread {
+        private final BluetoothSocket mmSocket;
+        private final BluetoothDevice mmDevice;
+        private String mSocketType;
+
+        public ConnectThread(BluetoothDevice device, boolean secure) {
+            mmDevice = device;
+            BluetoothSocket tmp = null;
+            mSocketType = secure ? "Secure" : "Insecure";
+
+            // Get a BluetoothSocket for a connection with the
+            // given BluetoothDevice
+            try {
+                if (secure) {
+                    tmp = device.createRfcommSocketToServiceRecord(
+                            MY_UUID_SECURE);
+                } else {
+                    tmp = device.createInsecureRfcommSocketToServiceRecord(
+                            MY_UUID_INSECURE);
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
+            }
+            mmSocket = tmp;
+        }
+
+        public void run() {
+            Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
+            setName("ConnectThread" + mSocketType);
+
+            // Always cancel discovery because it will slow down a connection
+            mAdapter.cancelDiscovery();
+
+            // Make a connection to the BluetoothSocket
+            try {
+                // This is a blocking call and will only return on a
+                // successful connection or an exception
+                mmSocket.connect();
+            } catch (IOException e) {
+                Log.e(TAG, "connect() failed ", e);
+                // Close the socket
+                try {
+                    mmSocket.close();
+                } catch (IOException e2) {
+                    Log.e(TAG, "unable to close() " + mSocketType +
+                            " socket during connection failure", e2);
+                }
+                connectionFailed();
+                return;
+            }
+
+            // Reset the ConnectThread because we're done
+            synchronized (BluetoothChatService.this) {
+                mConnectThread = null;
+            }
+
+            // Start the connected thread
+            connected(mmSocket, mmDevice, mSocketType);
+        }
+
+        public void cancel() {
+            try {
+                mmSocket.close();
+            } catch (IOException e) {
+                Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
+            }
+        }
+    }
+
+    /**
+     * This thread runs during a connection with a remote device.
+     * It handles all incoming and outgoing transmissions.
+     */
+    private class ConnectedThread extends Thread {
+        private final BluetoothSocket mmSocket;
+        private final InputStream mmInStream;
+        private final OutputStream mmOutStream;
+
+        public ConnectedThread(BluetoothSocket socket, String socketType) {
+            Log.d(TAG, "create ConnectedThread: " + socketType);
+            mmSocket = socket;
+            InputStream tmpIn = null;
+            OutputStream tmpOut = null;
+
+            // Get the BluetoothSocket input and output streams
+            try {
+                tmpIn = socket.getInputStream();
+                tmpOut = socket.getOutputStream();
+            } catch (IOException e) {
+                Log.e(TAG, "temp sockets not created", e);
+            }
+
+            mmInStream = tmpIn;
+            mmOutStream = tmpOut;
+        }
+
+        public void run() {
+            Log.i(TAG, "BEGIN mConnectedThread");
+            byte[] buffer = new byte[1024];
+            int bytes;
+
+            // Keep listening to the InputStream while connected
+            while (true) {
+                try {
+                    // Read from the InputStream
+                    bytes = mmInStream.read(buffer);
+
+                    // Send the obtained bytes to the UI Activity
+                    mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
+                            .sendToTarget();
+                } catch (IOException e) {
+                    Log.e(TAG, "disconnected", e);
+                    connectionLost();
+                    break;
+                }
+            }
+        }
+
+        /**
+         * Write to the connected OutStream.
+         * @param buffer  The bytes to write
+         */
+        public void write(byte[] buffer) {
+            try {
+                mmOutStream.write(buffer);
+                mmOutStream.flush();
+
+                // Share the sent message back to the UI Activity
+                mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer)
+                        .sendToTarget();
+            } catch (IOException e) {
+                Log.e(TAG, "Exception during write", e);
+            }
+        }
+
+        public void cancel() {
+            try {
+                mmSocket.close();
+            } catch (IOException e) {
+                Log.e(TAG, "close() of connect socket failed", e);
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
new file mode 100644
index 0000000..2beff93
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothTestActivity.java
@@ -0,0 +1,94 @@
+/*
+ * 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;
+
+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.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ListView;
+
+public class BluetoothTestActivity extends PassFailButtons.ListActivity {
+
+    private static final int LAUNCH_TEST_REQUEST_CODE = 1;
+
+    private TestListAdapter mAdapter;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.bt_main);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.bluetooth_test, R.string.bluetooth_test_info, -1);
+
+        mAdapter = new TestListAdapter(this, getClass().getName());
+        setListAdapter(mAdapter);
+        mAdapter.loadTestResults();
+
+        if (BluetoothAdapter.getDefaultAdapter() == null) {
+            showNoBluetoothDialog();
+        }
+    }
+
+    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();
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        super.onListItemClick(l, v, position, id);
+        TestListItem testItem = (TestListItem) l.getItemAtPosition(position);
+        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 LAUNCH_TEST_REQUEST_CODE:
+                handleLaunchTestResult(resultCode, data);
+                break;
+        }
+    }
+
+    private void handleLaunchTestResult(int resultCode, Intent data) {
+        if (resultCode == RESULT_OK) {
+            TestResult testResult = TestResult.fromActivityResult(resultCode, data);
+            mAdapter.setTestResult(testResult);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java
new file mode 100644
index 0000000..7106e7b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java
@@ -0,0 +1,156 @@
+/*
+ * 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;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ToggleButton;
+
+/**
+ * Activity for testing that Bluetooth can be disabled and enabled properly. The activity shows
+ * a button that toggles Bluetooth by disabling it via {@link BluetoothAdapter#disable()} and
+ * enabling it via the Intent action {@link BluetoothAdapter#ACTION_REQUEST_ENABLE}.
+ */
+public class BluetoothToggleActivity extends PassFailButtons.Activity {
+
+    private static final String TAG = BluetoothToggleActivity.class.getName();
+
+    private static final int START_ENABLE_BLUETOOTH_REQUEST = 1;
+
+    private BluetoothAdapter mBluetoothAdapter;
+
+    private BluetoothBroadcastReceiver mReceiver;
+
+    private ProgressDialog mDisablingDialog;
+
+    private ToggleButton mToggleButton;
+
+    private int mNumDisabledTimes = 0;
+
+    private int mNumEnabledTimes = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.bt_toggle);
+        setPassFailButtonClickListeners();
+
+        mReceiver = new BluetoothBroadcastReceiver();
+        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+        registerReceiver(mReceiver, filter);
+
+        mDisablingDialog = new ProgressDialog(this);
+        mDisablingDialog.setMessage(getString(R.string.bt_disabling));
+
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        getPassButton().setEnabled(false);
+
+        mToggleButton = (ToggleButton) findViewById(R.id.bt_toggle_button);
+        mToggleButton.setChecked(mBluetoothAdapter.isEnabled());
+        mToggleButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mToggleButton.isChecked()) {
+                    enableBluetooth();
+                } else {
+                    disableBluetooth();
+                }
+            }
+        });
+    }
+
+    private void enableBluetooth() {
+        mDisablingDialog.hide();
+        mToggleButton.setEnabled(false);
+        Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+        startActivityForResult(intent, START_ENABLE_BLUETOOTH_REQUEST);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case START_ENABLE_BLUETOOTH_REQUEST:
+                boolean enabledBluetooth = RESULT_OK == resultCode;
+                mToggleButton.setChecked(enabledBluetooth);
+                mToggleButton.setEnabled(true);
+                break;
+        }
+    }
+
+    private void disableBluetooth() {
+        mDisablingDialog.show();
+        mToggleButton.setEnabled(false);
+        if (!mBluetoothAdapter.disable()) {
+            mDisablingDialog.hide();
+            mToggleButton.setEnabled(true);
+            new AlertDialog.Builder(this)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setMessage(R.string.bt_disabling_error)
+                .setPositiveButton(android.R.string.ok, null)
+                .show();
+        }
+    }
+
+    class BluetoothBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
+            int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+            Log.i(TAG, "Previous state: " + previousState + " New state: " + newState);
+
+            if (BluetoothAdapter.STATE_OFF == newState
+                    && (BluetoothAdapter.STATE_ON == previousState
+                            || BluetoothAdapter.STATE_TURNING_OFF == previousState)) {
+                mNumDisabledTimes++;
+            }
+
+            if (BluetoothAdapter.STATE_ON == newState
+                    && (BluetoothAdapter.STATE_OFF == previousState
+                            || BluetoothAdapter.STATE_TURNING_ON == previousState)) {
+                mNumEnabledTimes++;
+            }
+
+            if (BluetoothAdapter.STATE_OFF == newState) {
+                mDisablingDialog.hide();
+                mToggleButton.setEnabled(true);
+            }
+
+            mToggleButton.setChecked(mBluetoothAdapter.isEnabled());
+            getPassButton().setEnabled(mNumDisabledTimes > 0 &&  mNumEnabledTimes > 0);
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/DevicePickerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/DevicePickerActivity.java
new file mode 100644
index 0000000..be71f66
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/DevicePickerActivity.java
@@ -0,0 +1,219 @@
+/*
+ * 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;
+
+import com.android.cts.verifier.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+
+import java.util.Set;
+
+/**
+ * {@link Activity} that shows a list of paired and new devices and returns the device selected
+ * by the user. When the user selects a paired device, it forwards them to the Bluetooth settings
+ * page, so that they can unpair it for the test.
+ */
+public class DevicePickerActivity extends Activity {
+
+    public static final String EXTRA_DEVICE_ADDRESS = "deviceAddress";
+
+    private static final int ENABLE_BLUETOOTH_REQUEST = 1;
+
+    private BluetoothAdapter mBluetoothAdapter;
+
+    private DiscoveryReceiver mReceiver;
+
+    private ArrayAdapter<Device> mNewDevicesAdapter;
+
+    private ArrayAdapter<Device> mPairedDevicesAdapter;
+
+    private TextView mEmptyNewView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+        setContentView(R.layout.bt_device_picker);
+
+        mPairedDevicesAdapter = new ArrayAdapter<Device>(this, R.layout.bt_device_name);
+        ListView pairedDevicesListView = (ListView) findViewById(R.id.bt_paired_devices);
+        pairedDevicesListView.setAdapter(mPairedDevicesAdapter);
+        pairedDevicesListView.setOnItemClickListener(new PairedDeviceClickListener());
+
+        View emptyPairedView = findViewById(R.id.bt_empty_paired_devices);
+        pairedDevicesListView.setEmptyView(emptyPairedView);
+
+        mNewDevicesAdapter = new ArrayAdapter<Device>(this, R.layout.bt_device_name);
+        ListView newDevicesListView = (ListView) findViewById(R.id.bt_new_devices);
+        newDevicesListView.setAdapter(mNewDevicesAdapter);
+        newDevicesListView.setOnItemClickListener(new NewDeviceClickListener());
+
+        mEmptyNewView = (TextView) findViewById(R.id.bt_empty_new_devices);
+        newDevicesListView.setEmptyView(mEmptyNewView);
+
+        mReceiver = new DiscoveryReceiver();
+        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
+        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+        filter.addAction(BluetoothDevice.ACTION_FOUND);
+        registerReceiver(mReceiver, filter);
+
+        Button scanButton = (Button) findViewById(R.id.bt_scan_button);
+        scanButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                scan();
+            }
+        });
+
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mBluetoothAdapter.isEnabled()) {
+            scan();
+        } 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) {
+                scan();
+            } else {
+                setResult(RESULT_CANCELED);
+                finish();
+            }
+        }
+    }
+
+    private void scan() {
+        populatePairedDevices();
+        mNewDevicesAdapter.clear();
+        if (mBluetoothAdapter.isDiscovering()) {
+            mBluetoothAdapter.cancelDiscovery();
+        }
+        mBluetoothAdapter.startDiscovery();
+    }
+
+    private void populatePairedDevices() {
+        mPairedDevicesAdapter.clear();
+        Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
+        for (BluetoothDevice device : pairedDevices) {
+            mPairedDevicesAdapter.add(Device.fromBluetoothDevice(device));
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (mBluetoothAdapter != null) {
+            mBluetoothAdapter.cancelDiscovery();
+        }
+        unregisterReceiver(mReceiver);
+    }
+
+    class NewDeviceClickListener implements OnItemClickListener {
+        @Override
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            Intent data = new Intent();
+            Device device = (Device) parent.getItemAtPosition(position);
+            data.putExtra(EXTRA_DEVICE_ADDRESS, device.mAddress);
+            setResult(RESULT_OK, data);
+            finish();
+        }
+    }
+
+    class PairedDeviceClickListener implements OnItemClickListener {
+        @Override
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            new AlertDialog.Builder(DevicePickerActivity.this)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setMessage(R.string.bt_unpair)
+                .setPositiveButton(R.string.bt_settings, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        if (mBluetoothAdapter != null) {
+                            mBluetoothAdapter.cancelDiscovery();
+                        }
+                        Intent intent = new Intent();
+                        intent.setAction(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS);
+                        startActivity(intent);
+                    }
+                })
+                .show();
+        }
+    }
+
+    class DiscoveryReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
+                mEmptyNewView.setText(R.string.bt_scanning);
+                setProgressBarIndeterminateVisibility(true);
+            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
+                mEmptyNewView.setText(R.string.bt_no_devices);
+                setProgressBarIndeterminateVisibility(false);
+            } else if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) {
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
+                    mNewDevicesAdapter.add(Device.fromBluetoothDevice(device));
+                }
+            }
+        }
+    }
+
+    static class Device {
+
+        String mName;
+
+        String mAddress;
+
+        Device(String name, String address) {
+            mName = name;
+            mAddress = address;
+        }
+
+        @Override
+        public String toString() {
+            return mName + "\n" + mAddress;
+        }
+
+        static Device fromBluetoothDevice(BluetoothDevice device) {
+            return new Device(device.getName() != null ? device.getName() : "",
+                    device.getAddress());
+        }
+    }
+}
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
new file mode 100644
index 0000000..9405d71
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/MessageTestActivity.java
@@ -0,0 +1,357 @@
+/*
+ * 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;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestResult;
+
+import android.app.AlertDialog;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+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 .*");
+
+    private BluetoothAdapter mBluetoothAdapter;
+    private PairingActionReceiver mPairingActionReceiver;
+    private BluetoothChatService mChatService;
+
+    private ArrayAdapter<String> mReceivedMessagesAdapter;
+    private ArrayAdapter<String> mSentMessagesAdapter;
+
+    private ListView mReceivedMessages;
+    private ListView mSentMessages;
+
+    private TextView mEmptyReceivedView;
+    private TextView mEmptySentView;
+
+    private AlertDialog mInstructionsDialog;
+
+    private String mDeviceAddress;
+    private boolean mSecure;
+    private boolean mServer;
+
+    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);
+        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+        setContentView(R.layout.bt_messages);
+        setPassFailButtonClickListeners();
+
+        if (mServer) {
+            setTitle(mSecure ? R.string.bt_secure_server : R.string.bt_insecure_server);
+        } else {
+            setTitle(mSecure ? R.string.bt_secure_client : R.string.bt_insecure_client);
+        }
+
+        mReceivedMessages = (ListView) findViewById(R.id.bt_received_messages);
+        mReceivedMessagesAdapter = new ArrayAdapter<String>(this, R.layout.bt_message_row);
+        mReceivedMessages.setAdapter(mReceivedMessagesAdapter);
+
+        mSentMessages = (ListView) findViewById(R.id.bt_sent_messages);
+        mSentMessagesAdapter = new ArrayAdapter<String>(this, R.layout.bt_message_row);
+        mSentMessages.setAdapter(mSentMessagesAdapter);
+
+        mEmptyReceivedView = (TextView) findViewById(R.id.bt_empty_received_messages);
+        mReceivedMessages.setEmptyView(mEmptyReceivedView);
+
+        mEmptySentView = (TextView) findViewById(R.id.bt_empty_sent_messages);
+        mSentMessages.setEmptyView(mEmptySentView);
+
+        setEmptyViewText(R.string.bt_no_messages);
+
+        Button makeDiscoverableButton = (Button) findViewById(R.id.bt_make_discoverable_button);
+        makeDiscoverableButton.setVisibility(mServer ? View.VISIBLE : View.GONE);
+        makeDiscoverableButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                makeDiscoverable();
+            }
+        });
+
+        getPassButton().setEnabled(false);
+
+        mPairingActionReceiver = new PairingActionReceiver();
+        IntentFilter intentFilter = new IntentFilter(ACTION_PAIRING_REQUEST);
+        registerReceiver(mPairingActionReceiver, intentFilter);
+
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (!mServer) {
+            Intent intent = new Intent(this, DevicePickerActivity.class);
+            startActivityForResult(intent, PICK_SERVER_DEVICE_REQUEST);
+        } else {
+            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);
+        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;
+        }
+    }
+
+    private void startChatService() {
+        mChatService = new BluetoothChatService(this, new ChatHandler());
+        if (mServer) {
+            mChatService.start(mSecure);
+        } else {
+            BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mDeviceAddress);
+            mChatService.connect(device, mSecure);
+        }
+    }
+
+    private void makeDiscoverable() {
+        if (mBluetoothAdapter.getScanMode() !=
+                BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
+            intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 30);
+            startActivity(intent);
+        }
+    }
+
+    private class ChatHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            super.handleMessage(msg);
+            switch (msg.what) {
+                case BluetoothChatService.MESSAGE_STATE_CHANGE:
+                    handleStateChange(msg);
+                    break;
+                case BluetoothChatService.MESSAGE_READ:
+                    handleMessageRead(msg);
+                    break;
+                case BluetoothChatService.MESSAGE_WRITE:
+                    handleMessageWrite(msg);
+                    break;
+                case BluetoothChatService.MESSAGE_DEVICE_NAME:
+                    handleDeviceName(msg);
+                    break;
+                case BluetoothChatService.MESSAGE_TOAST:
+                    handleToast(msg);
+                    break;
+            }
+        }
+    }
+
+    private void handleStateChange(Message msg) {
+        int state = msg.arg1;
+        switch (state) {
+            case BluetoothChatService.STATE_LISTEN:
+                setEmptyViewText(R.string.bt_waiting);
+                setProgressBarIndeterminateVisibility(true);
+                showInstructionsDialog();
+                break;
+
+            case BluetoothChatService.STATE_CONNECTING:
+                setEmptyViewText(R.string.bt_connecting);
+                setProgressBarIndeterminateVisibility(true);
+                break;
+
+            case BluetoothChatService.STATE_CONNECTED:
+                setEmptyViewText(R.string.bt_no_messages);
+                setProgressBarIndeterminateVisibility(false);
+
+                hideInstructionsDialog();
+                sendInitialMessageFromClient();
+                break;
+
+            case BluetoothChatService.STATE_NONE:
+                setEmptyViewText(R.string.bt_no_messages);
+                setProgressBarIndeterminateVisibility(false);
+                break;
+        }
+    }
+
+    private void setEmptyViewText(int textId) {
+        mEmptyReceivedView.setText(textId);
+        mEmptySentView.setText(textId);
+    }
+
+    private void showInstructionsDialog() {
+        if (mInstructionsDialog == null) {
+            mInstructionsDialog = new AlertDialog.Builder(this)
+                    .setIcon(android.R.drawable.ic_dialog_info)
+                    .setTitle(getString(R.string.bt_waiting))
+                    .setMessage(getString(mSecure
+                            ? R.string.bt_secure_server_instructions
+                            : R.string.bt_insecure_server_instructions))
+                    .setPositiveButton(android.R.string.ok, null)
+                    .create();
+        }
+        mInstructionsDialog.show();
+    }
+
+    private void hideInstructionsDialog() {
+        if (mInstructionsDialog != null) {
+            mInstructionsDialog.hide();
+        }
+    }
+
+    private void sendInitialMessageFromClient() {
+        if (!mServer) {
+            sendMessage(0);
+        }
+    }
+
+    private void sendMessage(int number) {
+        String message = "Message " + number + " to "
+                + (mRemoteDeviceName != null ? mRemoteDeviceName : "")
+                + MESSAGE_DELIMITER;
+        mChatService.write(message.getBytes());
+    }
+
+    private void handleMessageRead(Message msg) {
+        String chunk = new String((byte[]) msg.obj, 0, msg.arg1);
+        mMessageBuffer.append(chunk);
+
+        int delimiterIndex = mMessageBuffer.indexOf(MESSAGE_DELIMITER);
+        if (delimiterIndex != -1) {
+            String message = mMessageBuffer.substring(0, delimiterIndex); // Chop off delimiter
+            mMessageBuffer.delete(0, delimiterIndex + 1);
+            addNewMessage(message);
+        }
+    }
+
+    private void addNewMessage(String msg) {
+        mReceivedMessagesAdapter.add(msg);
+        Matcher matcher = MESSAGE_PATTERN.matcher(msg);
+        if (matcher.matches()) {
+            int number = Integer.valueOf(matcher.group(1));
+            if (mServer && number == 10 || !mServer && number == 11) {
+                getPassButton().setEnabled(true);
+            }
+            if (number <= 10) {
+                sendMessage(number + 1);
+            }
+        }
+    }
+
+    private void handleMessageWrite(Message msg) {
+        String sentMessage = new String((byte[]) msg.obj).trim(); // Chop off delimiter
+        mSentMessagesAdapter.add(sentMessage);
+    }
+
+    private void handleDeviceName(Message msg) {
+        mRemoteDeviceName = msg.getData().getString(BluetoothChatService.DEVICE_NAME);
+    }
+
+    private void handleToast(Message msg) {
+        String toast = msg.getData().getString(BluetoothChatService.TOAST);
+        Toast.makeText(this, toast, Toast.LENGTH_LONG).show();
+    }
+
+    class PairingActionReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!mSecure && ACTION_PAIRING_REQUEST.equals(intent.getAction())) {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        showPairingErrorDialog();
+                    }
+                });
+            }
+        }
+    }
+
+    private void showPairingErrorDialog() {
+        new AlertDialog.Builder(MessageTestActivity.this)
+            .setIcon(android.R.drawable.ic_dialog_alert)
+            .setTitle(R.string.bt_insecure_pairing_error_title)
+            .setMessage(R.string.bt_insecure_pairing_error_message)
+            .setPositiveButton(android.R.string.ok,
+                    new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    TestResult.setFailedResult(MessageTestActivity.this);
+                    finish();
+                }
+            })
+            .setCancelable(false)
+            .show();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        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/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index 4f6e0a3..99ed723 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -40,6 +40,8 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Set;
 
 public class FeatureSummaryActivity extends PassFailButtons.ListActivity {
     /**
@@ -65,7 +67,7 @@
         /**
          * Constructor does not include 'present' because that's a detected
          * value, and not set during creation.
-         * 
+         *
          * @param name value for this.name
          * @param required value for this.required
          */
@@ -74,6 +76,23 @@
             this.required = required;
             this.present = false;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            } else if (o == null || !(o instanceof Feature)) {
+                return false;
+            } else {
+                Feature feature = (Feature) o;
+                return name.equals(feature.name);
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return name.hashCode();
+        }
     }
 
     /**
@@ -92,8 +111,9 @@
     };
 
     /**
-     * A list of all features added in FroYo (API=8). Because we want to run on
-     * Eclair devices, we can't use static references to constants added later
+     * A list of all features added in FroYo (API=8) and Gingerbread (API=9).
+     * Because we want to run on Eclair devices,
+     * we can't use static references to constants added later
      * than Eclair. We could use Reflection, but we'd still need a list of
      * string literals (for constant names) anyway, and there's little point in
      * using Reflection to to look up a constant String value for a constant
@@ -113,12 +133,36 @@
             new Feature("android.hardware.wifi", false),
     };
 
+    public static final Feature[] ALL_GINGERBREAD_FEATURES = {
+            // Required features in prior releases that became optional in GB
+            new Feature("android.hardware.bluetooth", false),
+            new Feature("android.hardware.camera", false),
+            new Feature("android.hardware.location.gps", false),
+            new Feature("android.hardware.microphone", false),
+            new Feature("android.hardware.sensor.accelerometer", false),
+            new Feature("android.hardware.sensor.compass", false),
+
+            // New features in GB
+            new Feature("android.hardware.audio.low_latency", false),
+            new Feature("android.hardware.camera.front", false),
+            new Feature("android.hardware.nfc", false),
+            new Feature("android.hardware.sensor.barometer", false),
+            new Feature("android.hardware.sensor.gyroscope", false),
+            new Feature("android.hardware.touchscreen.multitouch.jazzhand", false),
+            new Feature("android.software.sip", false),
+            new Feature("android.software.sip.voip", false),
+    };
+
+    public static final Feature[] ALL_GINGERBREAD_MR1_FEATURES = {
+            new Feature("android.hardware.usb.accessory", false),
+    };
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.fs_main);
+        setPassFailButtonClickListeners();
         setInfoResources(R.string.feature_summary, R.string.feature_summary_info, R.layout.fs_info);
-        setResult(RESULT_CANCELED);
 
         // some values used to detect warn-able conditions involving multiple
         // features
@@ -139,14 +183,22 @@
         // roll over all known features & check whether device reports them
         boolean present = false;
         int statusIcon;
-        ArrayList<Feature> features = new ArrayList<Feature>();
+        Set<Feature> features = new LinkedHashSet<Feature>();
+
+        // add features from latest to last so that the latest requirements are put in the set first
         int apiVersion = Build.VERSION.SDK_INT;
-        if (apiVersion >= Build.VERSION_CODES.ECLAIR_MR1) {
-            Collections.addAll(features, ALL_ECLAIR_FEATURES);
+        if (apiVersion >= Build.VERSION_CODES.GINGERBREAD_MR1) {
+            Collections.addAll(features, ALL_GINGERBREAD_MR1_FEATURES);
+        }
+        if (apiVersion >= Build.VERSION_CODES.GINGERBREAD) {
+            Collections.addAll(features, ALL_GINGERBREAD_FEATURES);
         }
         if (apiVersion >= Build.VERSION_CODES.FROYO) {
             Collections.addAll(features, ALL_FROYO_FEATURES);
         }
+        if (apiVersion >= Build.VERSION_CODES.ECLAIR_MR1) {
+            Collections.addAll(features, ALL_ECLAIR_FEATURES);
+        }
         for (Feature f : features) {
             HashMap<String, Object> row = new HashMap<String, Object>();
             listViewData.add(row);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerTestActivity.java
index 2100c0f..193f37e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerTestActivity.java
@@ -41,7 +41,6 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setResult(RESULT_CANCELED);
 
         mSensorManager = (SensorManager) getApplicationContext().getSystemService(
                 Context.SENSOR_SERVICE);
@@ -49,6 +48,7 @@
         mListener = renderer;
 
         setContentView(R.layout.pass_fail_gl);
+        setPassFailButtonClickListeners();
         setInfoResources(R.string.snsr_accel_test, R.string.snsr_accel_test_info, -1);
         mGLSurfaceView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
         mGLSurfaceView.setRenderer(renderer);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeTestActivity.java
new file mode 100644
index 0000000..ea6ca4c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeTestActivity.java
@@ -0,0 +1,412 @@
+/*
+ * 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.sensors;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLU;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * Manual test for testing the gyroscope sensor. This test consists of 6 steps for all the
+ * different ways to rotate the device along the x, y, and z axis. It also raises a warning
+ * if the values seem to high and may be degrees.
+ */
+public class GyroscopeTestActivity extends PassFailButtons.Activity {
+
+    private static final int NUM_STAGES = 6;
+    private static final String STAGE_INDEX_EXTRA = "stageIndex";
+
+    private static final int BACKGROUND_BLACK = 0;
+    private static final int BACKGROUND_RED = 1;
+    private static final int BACKGROUND_GREEN = 2;
+
+    private AtomicInteger mBackgroundColor = new AtomicInteger(BACKGROUND_BLACK);
+
+    private SensorManager mSensorManager;
+    private Sensor mSensor;
+    private SensorListener mSensorListener;
+    private GLSurfaceView mGLSurfaceView;
+    private TextView mProgressText;
+    private TextView mSensorText;
+
+    private AlertDialog mNoGyroscopeWarningDialog;
+    private AlertDialog mDegreesWarningDialog;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.snsr_gyro);
+        setInfoResources(R.string.snsr_gyro_test, R.string.snsr_gyro_test_info, 0);
+        setPassFailButtonClickListeners();
+
+        // This activity is reused 6 times with different settings to test each rotation direction
+        final int stageIndex = getIntent().getIntExtra(STAGE_INDEX_EXTRA, 0);
+        Settings settings = getSettings(stageIndex);
+
+        // Hitting the pass button goes to the next test activity. Only the last one ends the test.
+        if (stageIndex + 1 < NUM_STAGES) {
+            setPassButtonGoesToNextStage(stageIndex);
+        }
+
+        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
+        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
+        mSensorListener = new SensorListener(settings.mSensorEventIndex,
+                settings.mExpectPositiveValue);
+
+        mGLSurfaceView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
+        mGLSurfaceView.setRenderer(new RotationGuideRenderer(settings.mRotateX, settings.mRotateY,
+                settings.mRotateZ));
+
+        mProgressText = (TextView) findViewById(R.id.progress);
+        mProgressText.setText(String.format(getString(R.string.snsr_gyro_test_progress),
+                settings.mStageIndex + 1, settings.mTotalStages));
+
+        mSensorText = (TextView) findViewById(R.id.sensor_value);
+    }
+
+    private Settings getSettings(int stageIndex) {
+        switch (stageIndex) {
+            case 0:
+                return new Settings(stageIndex, NUM_STAGES, 0, 0, 1, 2, true);
+            case 1:
+                return new Settings(stageIndex, NUM_STAGES, 0, 0, -1, 2, false);
+            case 2:
+                return new Settings(stageIndex, NUM_STAGES, 0, 1, 0, 1, true);
+            case 3:
+                return new Settings(stageIndex, NUM_STAGES, 0, -1, 0, 1, false);
+            case 4:
+                return new Settings(stageIndex, NUM_STAGES, 1, 0, 0, 0, true);
+            case 5:
+                return new Settings(stageIndex, NUM_STAGES, -1, 0, 0, 0, false);
+            default:
+                throw new IllegalArgumentException("Bad stage index: " + stageIndex);
+        }
+    }
+
+    /** Bundle of settings for testing a certain rotation direction. */
+    class Settings {
+        int mStageIndex;
+        int mTotalStages;
+        float mRotateX;
+        float mRotateY;
+        float mRotateZ;
+        int mSensorEventIndex;
+        boolean mExpectPositiveValue;
+
+        Settings(int stageIndex, int totalStages, float rotateX, float rotateY, float rotateZ,
+                int sensorEventIndex, boolean expectPositiveValue) {
+            mStageIndex = stageIndex;
+            mTotalStages = totalStages;
+            mRotateX = rotateX;
+            mRotateY = rotateY;
+            mRotateZ = rotateZ;
+            mSensorEventIndex = sensorEventIndex;
+            mExpectPositiveValue = expectPositiveValue;
+        }
+    }
+
+    private void setPassButtonGoesToNextStage(final int stageIndex) {
+        findViewById(R.id.pass_button).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent(GyroscopeTestActivity.this,
+                        GyroscopeTestActivity.class);
+                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+                        | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+                intent.putExtra(STAGE_INDEX_EXTRA, stageIndex + 1);
+                startActivity(intent);
+            }
+        });
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (!mSensorManager.registerListener(mSensorListener, mSensor,
+                SensorManager.SENSOR_DELAY_UI)) {
+            showNoGyroscopeWarningDialog();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mSensorManager.unregisterListener(mSensorListener, mSensor);
+    }
+
+    /** Renders a spinning block to indicate how the device should be rotated in the test. */
+    class RotationGuideRenderer implements GLSurfaceView.Renderer {
+
+        private static final double ANGLE_INCREMENT = 1.0;
+
+        private final Monolith mMonolith = new Monolith();
+
+        private float mAngle = 0.0f;
+
+        private float mRotateX;
+
+        private float mRotateY;
+
+        private float mRotateZ;
+
+        public RotationGuideRenderer(float rotateX, float rotateY, float rotateZ) {
+            mRotateX = rotateX;
+            mRotateY = rotateY;
+            mRotateZ = rotateZ;
+        }
+
+        @Override
+        public void onDrawFrame(GL10 gl) {
+            clearBackground(gl);
+            gl.glMatrixMode(GL10.GL_MODELVIEW);
+            gl.glLoadIdentity();
+            gl.glRotatef(mAngle, mRotateX, mRotateY, mRotateZ);
+            mMonolith.draw(gl);
+            mAngle += ANGLE_INCREMENT;
+        }
+
+        private void clearBackground(GL10 gl) {
+            switch (mBackgroundColor.get()) {
+                case BACKGROUND_GREEN:
+                    gl.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+                    break;
+
+                case BACKGROUND_RED:
+                    gl.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
+                    break;
+
+                default:
+                    gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+                    break;
+            }
+            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+        }
+
+        @Override
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            gl.glViewport(0, 0, width, height);
+            gl.glMatrixMode(GL10.GL_PROJECTION);
+            gl.glLoadIdentity();
+            float ratio = (float) width / height;
+            gl.glFrustumf(-ratio, ratio, -1, 1, 3, 15);
+            GLU.gluLookAt(gl, 0, 0, 10, 0, 0, 0, 0, 1, 0);
+        }
+
+        @Override
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+            gl.glEnable(GL10.GL_LIGHTING);
+            gl.glEnable(GL10.GL_LIGHT0);
+            gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, new float[] {0.75f, 0.75f, 0.75f, 1f}, 0);
+        }
+    }
+
+    /** Rectangular block that is rotated by {@link RotationGuideRenderer}. */
+    class Monolith {
+
+        private static final int NUM_VERTICES = 8;
+
+        private static final int NUM_INDICES = 36;
+
+        private FloatBuffer mVertexBuffer;
+
+        private ShortBuffer mIndexBuffer;
+
+        public Monolith() {
+            mVertexBuffer = ByteBuffer.allocateDirect(NUM_VERTICES * 3 * 4)
+                    .order(ByteOrder.nativeOrder())
+                    .asFloatBuffer();
+
+            float[] coordinates = {
+                    -0.65f, -1, 0.2f,
+                    -0.65f, 1, 0.2f,
+                    0.65f, 1, 0.2f,
+                    0.65f, -1, 0.2f,
+
+                    -0.65f, -1, -0.2f,
+                    -0.65f, 1, -0.2f,
+                    0.65f, 1, -0.2f,
+                    0.65f, -1, -0.2f,
+            };
+
+            for (int i = 0; i < coordinates.length; i++) {
+                mVertexBuffer.put(coordinates[i]);
+            }
+
+            mIndexBuffer = ByteBuffer.allocateDirect(NUM_INDICES * 2)
+                    .order(ByteOrder.nativeOrder())
+                    .asShortBuffer();
+
+            // Front
+            mIndexBuffer.put((short) 0);
+            mIndexBuffer.put((short) 1);
+            mIndexBuffer.put((short) 2);
+            mIndexBuffer.put((short) 0);
+            mIndexBuffer.put((short) 2);
+            mIndexBuffer.put((short) 3);
+
+            // Back
+            mIndexBuffer.put((short) 7);
+            mIndexBuffer.put((short) 6);
+            mIndexBuffer.put((short) 5);
+            mIndexBuffer.put((short) 7);
+            mIndexBuffer.put((short) 5);
+            mIndexBuffer.put((short) 4);
+
+            // Right
+            mIndexBuffer.put((short) 3);
+            mIndexBuffer.put((short) 2);
+            mIndexBuffer.put((short) 6);
+            mIndexBuffer.put((short) 3);
+            mIndexBuffer.put((short) 6);
+            mIndexBuffer.put((short) 7);
+
+            // Left
+            mIndexBuffer.put((short) 4);
+            mIndexBuffer.put((short) 5);
+            mIndexBuffer.put((short) 1);
+            mIndexBuffer.put((short) 4);
+            mIndexBuffer.put((short) 1);
+            mIndexBuffer.put((short) 0);
+
+            // Top
+            mIndexBuffer.put((short) 1);
+            mIndexBuffer.put((short) 5);
+            mIndexBuffer.put((short) 6);
+            mIndexBuffer.put((short) 1);
+            mIndexBuffer.put((short) 6);
+            mIndexBuffer.put((short) 2);
+
+            // Bottom
+            mIndexBuffer.put((short) 3);
+            mIndexBuffer.put((short) 7);
+            mIndexBuffer.put((short) 4);
+            mIndexBuffer.put((short) 3);
+            mIndexBuffer.put((short) 4);
+            mIndexBuffer.put((short) 0);
+
+            mVertexBuffer.position(0);
+            mIndexBuffer.position(0);
+        }
+
+        public void draw(GL10 gl) {
+            gl.glColor4f(0.5f, 0.5f, 0.5f, 1f);
+            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
+            gl.glDrawElements(GL10.GL_TRIANGLES, NUM_INDICES, GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
+        }
+    }
+
+    class SensorListener implements SensorEventListener {
+
+        /** Throw away other events that are smaller than this. */
+        private static final double MOVING_AMOUNT = 0.1;
+
+        private final int mEventIndex;
+
+        private final boolean mExpectPositive;
+
+        SensorListener(int eventIndex, boolean expectPositive) {
+            mEventIndex = eventIndex;
+            mExpectPositive = expectPositive;
+        }
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            float value = event.values[mEventIndex];
+            if (value > MOVING_AMOUNT) {
+                if (mExpectPositive) {
+                    updateWidgets(value, BACKGROUND_GREEN, R.drawable.fs_good);
+                } else {
+                    updateWidgets(value, BACKGROUND_RED, R.drawable.fs_error);
+                }
+            } else if (value < -MOVING_AMOUNT) {
+                if (mExpectPositive) {
+                    updateWidgets(value, BACKGROUND_RED, R.drawable.fs_error);
+                } else {
+                    updateWidgets(value, BACKGROUND_GREEN, R.drawable.fs_good);
+                }
+            } else {
+                updateWidgets(value, BACKGROUND_BLACK, R.drawable.fs_indeterminate);
+            }
+
+            if (value > 10) {
+                showDegreesWarningDialog();
+            }
+        }
+
+        void updateWidgets(float sensorValue, int backgroundColor, int icon) {
+            synchronized (GyroscopeTestActivity.this) {
+                mBackgroundColor.set(backgroundColor);
+            }
+            mSensorText.setText(String.format("%+.2f", sensorValue));
+            mSensorText.setCompoundDrawablesWithIntrinsicBounds(0, 0, icon, 0);
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        }
+    }
+
+    private void showNoGyroscopeWarningDialog() {
+        if (mNoGyroscopeWarningDialog == null) {
+            mNoGyroscopeWarningDialog = new AlertDialog.Builder(GyroscopeTestActivity.this)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setTitle(R.string.snsr_gyro_test_no_gyro_title)
+                .setMessage(R.string.snsr_gyro_test_no_gyro_message)
+                .setPositiveButton(android.R.string.ok, null)
+                .create();
+        }
+        if (!mNoGyroscopeWarningDialog.isShowing()) {
+            mNoGyroscopeWarningDialog.show();
+        }
+    }
+
+    private void showDegreesWarningDialog() {
+        if (mDegreesWarningDialog == null) {
+            mDegreesWarningDialog = new AlertDialog.Builder(GyroscopeTestActivity.this)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setTitle(R.string.snsr_gyro_test_degrees_title)
+                    .setMessage(R.string.snsr_gyro_test_degrees_message)
+                    .setPositiveButton(android.R.string.ok, null)
+                    .create();
+        }
+        if (!mDegreesWarningDialog.isShowing()) {
+            mDegreesWarningDialog.show();
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java
deleted file mode 100644
index 479a2fb..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2010 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.sensors;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.opengl.GLSurfaceView;
-import android.os.Bundle;
-
-/**
- * CTS Verifier case for verifying correct integration of accelerometer.
- * Displays a wedge using OpenGL that, on a correctly-integrated device, always
- * points down.
- */
-public class MagnetometerTestActivity extends PassFailButtons.Activity {
-    private GLSurfaceView mGLSurfaceView;
-
-    private AccelerometerTestRenderer mListener;
-
-    private SensorManager mSensorManager;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setResult(RESULT_CANCELED);
-
-        mSensorManager = (SensorManager) getApplicationContext().getSystemService(
-                Context.SENSOR_SERVICE);
-        AccelerometerTestRenderer renderer = new MagnetometerTestRenderer(this);
-        mListener = renderer;
-
-        setContentView(R.layout.pass_fail_gl);
-        setInfoResources(R.string.snsr_mag_test, R.string.snsr_mag_test_info, -1);
-        mGLSurfaceView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
-        mGLSurfaceView.setRenderer(renderer);
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mSensorManager.unregisterListener(mListener);
-        mGLSurfaceView.onPause();
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mSensorManager.registerListener(mListener, mSensorManager.getSensorList(
-                Sensor.TYPE_MAGNETIC_FIELD).get(0), SensorManager.SENSOR_DELAY_UI);
-        mGLSurfaceView.onResume();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestRenderer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestRenderer.java
deleted file mode 100644
index b5d4587..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestRenderer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2010 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.sensors;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-
-public class MagnetometerTestRenderer extends AccelerometerTestRenderer {
-    public MagnetometerTestRenderer(Context context) {
-        super(context);
-    }
-
-    @Override
-    public void onSensorChanged(SensorEvent event) {
-        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
-            /*
-             * The base class is written for accelerometer, where the vector
-             * points *away* from the thing being measured (i.e. gravity). But
-             * our vector points *toward* the thing being measured (i.e.
-             * magnetic north pole). Accordingly, the base class has an
-             * inversion to handle that that doesn't apply to us, so the
-             * simplest method is just to flip our vector to point in the exact
-             * opposite direction and then everything works out in the base
-             * class.
-             */
-            event.values[0] *= -1;
-            event.values[1] *= -1;
-            event.values[2] *= -1;
-
-            // rest of method is the same as in base class
-            normalize(event.values);
-            event.values[1] *= -1;
-            crossProduct(event.values, Z_AXIS, mCrossProd);
-            mAngle = (float) Math.acos(dotProduct(event.values, Z_AXIS));
-        }
-    }
-}
diff --git a/apps/CtsVerifier/tests/src/com/android/cts/verifier/CtsVerifierActivityTest.java b/apps/CtsVerifier/tests/src/com/android/cts/verifier/CtsVerifierActivityTest.java
deleted file mode 100644
index c3e01b2..0000000
--- a/apps/CtsVerifier/tests/src/com/android/cts/verifier/CtsVerifierActivityTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2010 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.app.Activity;
-import android.app.Instrumentation;
-import android.app.Instrumentation.ActivityMonitor;
-import android.test.ActivityInstrumentationTestCase2;
-import android.widget.Button;
-import android.widget.TextView;
-
-import java.util.concurrent.TimeUnit;
-
-public class CtsVerifierActivityTest
-        extends ActivityInstrumentationTestCase2<CtsVerifierActivity> {
-
-    private Activity mActivity;
-    private Instrumentation mInstrumentation;
-    private TextView mWelcomeTextView;
-    private Button mContinueButton;
-    private String mWelcomeText;
-    private String mContinueText;
-
-    public CtsVerifierActivityTest() {
-        super(CtsVerifierActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mActivity = getActivity();
-        mInstrumentation = getInstrumentation();
-        mWelcomeTextView = (TextView) mActivity.findViewById(R.id.welcome);
-        mWelcomeText = mActivity.getString(R.string.welcome_text);
-        mContinueButton = (Button) mActivity.findViewById(R.id.continue_button);
-        mContinueText = mActivity.getString(R.string.continue_button_text);
-    }
-
-    public void testPreconditions() {
-        assertNotNull(mWelcomeTextView);
-        assertNotNull(mWelcomeText);
-        assertNotNull(mContinueButton);
-    }
-
-    public void testWelcome() {
-        assertEquals(mWelcomeText, mWelcomeTextView.getText().toString());
-        assertEquals(mContinueText, mContinueButton.getText().toString());
-    }
-
-    /** Check that the continue button leads to the test list successfully. */
-    public void testContinueButton() throws Throwable {
-        ActivityMonitor monitor =
-                new ActivityMonitor(TestListActivity.class.getName(), null, false);
-        mInstrumentation.addMonitor(monitor);
-
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-               assertTrue(mContinueButton.performClick());
-            }
-        });
-
-        Activity activity = mInstrumentation.waitForMonitorWithTimeout(monitor,
-                TimeUnit.SECONDS.toMillis(10));
-        assertNotNull(activity);
-        activity.finish();
-    }
-}
diff --git a/apps/CtsVerifier/tests/src/com/android/cts/verifier/TestListActivityTest.java b/apps/CtsVerifier/tests/src/com/android/cts/verifier/TestListActivityTest.java
deleted file mode 100644
index 8961c16..0000000
--- a/apps/CtsVerifier/tests/src/com/android/cts/verifier/TestListActivityTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2010 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.app.Activity;
-import android.app.Instrumentation;
-import android.app.Instrumentation.ActivityMonitor;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.Cursor;
-import android.test.ActivityInstrumentationTestCase2;
-import android.view.KeyEvent;
-
-import java.util.concurrent.TimeUnit;
-
-public class TestListActivityTest
-        extends ActivityInstrumentationTestCase2<TestListActivity> {
-
-    private TestListActivity mActivity;
-    private Instrumentation mInstrumentation;
-
-    public TestListActivityTest() {
-        super(TestListActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mActivity = getActivity();
-        mInstrumentation = getInstrumentation();
-    }
-
-    /** Check that querying the for manual tests somewhat works. */
-    public void testListAdapter() {
-        assertNotNull(mActivity.getListAdapter());
-        assertTrue(mActivity.getListAdapter().getCount() > 0);
-    }
-
-    /** Test that clicking on an item launches a test. */
-    public void testLaunchAndFinishTestActivity() throws Throwable {
-        clearAllTestResults();
-        Activity testActivity = launchTestActivity();
-        finishTestActivity(testActivity);
-    }
-
-    private void clearAllTestResults() throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                ContentResolver resolver = mActivity.getContentResolver();
-                resolver.delete(TestResultsProvider.RESULTS_CONTENT_URI, "1", null);
-
-                Cursor cursor = resolver.query(TestResultsProvider.RESULTS_CONTENT_URI,
-                        TestResultsProvider.ALL_COLUMNS, null, null, null);
-                assertEquals(0, cursor.getCount());
-                cursor.close();
-            }
-        });
-    }
-
-    private Activity launchTestActivity() {
-        IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
-        filter.addCategory(TestListAdapter.CATEGORY_MANUAL_TEST);
-
-        ActivityMonitor monitor = new ActivityMonitor(filter, null, false);
-        mInstrumentation.addMonitor(monitor);
-
-        sendKeys(KeyEvent.KEYCODE_ENTER);
-
-        Activity activity = mInstrumentation.waitForMonitorWithTimeout(monitor,
-                TimeUnit.SECONDS.toMillis(1));
-        assertNotNull(activity);
-        return activity;
-    }
-
-    private void finishTestActivity(Activity activity) throws Throwable {
-        TestResult.setPassedResult(activity);
-        activity.finish();
-        mInstrumentation.waitForIdleSync();
-
-        ContentResolver resolver = mActivity.getContentResolver();
-        Cursor cursor = resolver.query(TestResultsProvider.RESULTS_CONTENT_URI,
-                TestResultsProvider.ALL_COLUMNS, null, null, null);
-        assertEquals(1, cursor.getCount());
-        cursor.close();
-    }
-}
diff --git a/apps/CtsVerifier/tests/src/com/android/cts/verifier/TestResultsProviderTest.java b/apps/CtsVerifier/tests/src/com/android/cts/verifier/TestResultsProviderTest.java
deleted file mode 100644
index 9999125..0000000
--- a/apps/CtsVerifier/tests/src/com/android/cts/verifier/TestResultsProviderTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.android.cts.verifier;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.test.ProviderTestCase2;
-
-public class TestResultsProviderTest extends ProviderTestCase2<TestResultsProvider> {
-
-    private static final String FOO_TEST_NAME = "com.android.cts.verifier.foo.FooActivity";
-
-    private static final String BAR_TEST_NAME = "com.android.cts.verifier.foo.BarActivity";
-
-    private TestResultsProvider mProvider;
-
-    public TestResultsProviderTest() {
-        super(TestResultsProvider.class, TestResultsProvider.AUTHORITY);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mProvider = getProvider();
-    }
-
-    public void testInsertUpdateDeleteByTestName() {
-        Cursor cursor = mProvider.query(TestResultsProvider.RESULTS_CONTENT_URI,
-                TestResultsProvider.ALL_COLUMNS, null, null, null);
-        assertEquals(0, cursor.getCount());
-
-        ContentValues values = new ContentValues(2);
-        values.put(TestResultsProvider.COLUMN_TEST_NAME, FOO_TEST_NAME);
-        values.put(TestResultsProvider.COLUMN_TEST_RESULT, TestResult.TEST_RESULT_FAILED);
-        assertNotNull(mProvider.insert(TestResultsProvider.RESULTS_CONTENT_URI, values));
-
-        cursor = mProvider.query(TestResultsProvider.RESULTS_CONTENT_URI, TestResultsProvider.ALL_COLUMNS,
-                null, null, null);
-        assertEquals(1, cursor.getCount());
-        assertTrue(cursor.moveToFirst());
-        assertEquals(FOO_TEST_NAME, cursor.getString(1));
-        assertEquals(TestResult.TEST_RESULT_FAILED, cursor.getInt(2));
-        cursor.close();
-
-        values = new ContentValues();
-        values.put(TestResultsProvider.COLUMN_TEST_NAME, BAR_TEST_NAME);
-        values.put(TestResultsProvider.COLUMN_TEST_RESULT, TestResult.TEST_RESULT_PASSED);
-        int numUpdated = mProvider.update(TestResultsProvider.RESULTS_CONTENT_URI, values,
-                TestResultsProvider.COLUMN_TEST_NAME + " = ?", new String[] {BAR_TEST_NAME});
-        assertEquals(0, numUpdated);
-
-        cursor = mProvider.query(TestResultsProvider.RESULTS_CONTENT_URI, TestResultsProvider.ALL_COLUMNS,
-                null, null, null);
-        assertEquals(1, cursor.getCount());
-        assertTrue(cursor.moveToFirst());
-        assertEquals(FOO_TEST_NAME, cursor.getString(1));
-        assertEquals(TestResult.TEST_RESULT_FAILED, cursor.getInt(2));
-        cursor.close();
-
-        values = new ContentValues(1);
-        values.put(TestResultsProvider.COLUMN_TEST_RESULT, TestResult.TEST_RESULT_PASSED);
-        numUpdated = mProvider.update(TestResultsProvider.RESULTS_CONTENT_URI, values,
-                TestResultsProvider.COLUMN_TEST_NAME + " = ?", new String[] {FOO_TEST_NAME});
-        assertEquals(1, numUpdated);
-
-        cursor = mProvider.query(TestResultsProvider.RESULTS_CONTENT_URI, TestResultsProvider.ALL_COLUMNS,
-                null, null, null);
-        assertEquals(1, cursor.getCount());
-        assertTrue(cursor.moveToFirst());
-        assertEquals(FOO_TEST_NAME, cursor.getString(1));
-        assertEquals(TestResult.TEST_RESULT_PASSED, cursor.getInt(2));
-        cursor.close();
-
-        int numDeleted = mProvider.delete(TestResultsProvider.RESULTS_CONTENT_URI, "1", null);
-        assertEquals(1, numDeleted);
-
-        cursor = mProvider.query(TestResultsProvider.RESULTS_CONTENT_URI, TestResultsProvider.ALL_COLUMNS,
-                null, null, null);
-        assertEquals(0, cursor.getCount());
-        cursor.close();
-    }
-}
diff --git a/apps/CtsVerifier/tests/src/com/android/cts/verifier/features/FeatureSummaryActivityTest.java b/apps/CtsVerifier/tests/src/com/android/cts/verifier/features/FeatureSummaryActivityTest.java
deleted file mode 100644
index ca7ced4..0000000
--- a/apps/CtsVerifier/tests/src/com/android/cts/verifier/features/FeatureSummaryActivityTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2010 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.features;
-
-import com.android.cts.verifier.features.FeatureSummaryActivity.Feature;
-
-import android.content.pm.PackageManager;
-import android.os.Build;
-
-import java.lang.reflect.Field;
-import java.util.HashSet;
-import java.util.Set;
-
-import junit.framework.TestCase;
-
-public class FeatureSummaryActivityTest extends TestCase {
-
-    public void testAllFeatures() throws Exception {
-        int version = Build.VERSION.SDK_INT;
-
-        Set<String> expectedFeatures = getFeatureConstants();
-
-        Set<String> actualFeatures = new HashSet<String>();
-        for (Feature feature : FeatureSummaryActivity.ALL_ECLAIR_FEATURES) {
-            actualFeatures.add(feature.name);
-        }
-        if (version >= Build.VERSION_CODES.FROYO) {
-            for (Feature feature : FeatureSummaryActivity.ALL_FROYO_FEATURES) {
-                actualFeatures.add(feature.name);
-            }
-        }
-
-        assertEquals("Feature list needs to be updated.",
-                expectedFeatures.size(), actualFeatures.size());
-    }
-
-    private static Set<String> getFeatureConstants()
-            throws IllegalArgumentException, IllegalAccessException {
-        Set<String> features = new HashSet<String>();
-        Field[] fields = PackageManager.class.getFields();
-        for (Field field : fields) {
-            if (field.getName().startsWith("FEATURE_")) {
-                String feature = (String) field.get(null);
-                features.add(feature);
-            }
-        }
-        return features;
-    }
-}
diff --git a/development/ide/eclipse/.classpath b/development/ide/eclipse/.classpath
index ef2759e..d700593 100644
--- a/development/ide/eclipse/.classpath
+++ b/development/ide/eclipse/.classpath
@@ -2,7 +2,7 @@
 <classpath>
     <classpathentry kind="src" path="cts/apps/CtsVerifier/src"/>
     <classpathentry kind="src" path="cts/apps/CtsVerifier/tests/src"/>
-    <classpathentry kind="src" path="cts/libs/annotation/src"/>
+    <classpathentry kind="src" path="cts/libs/vogar-expect/src"/>
     <classpathentry kind="src" path="cts/tests/ApiDemosReferenceTest/src"/>
     <classpathentry kind="src" path="cts/tests/ProcessTest/src"/>
     <classpathentry kind="src" path="cts/tests/ProcessTest/NoShareUidApp/src"/>
@@ -13,9 +13,11 @@
     <classpathentry kind="src" path="cts/tests/appsecurity-tests/src"/>
     <classpathentry kind="src" path="cts/tests/appsecurity-tests/test-apps/AppWithData/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"/>
@@ -47,7 +49,6 @@
     <classpathentry kind="src" path="cts/tests/tests/view/src"/>
     <classpathentry kind="src" path="cts/tests/tests/webkit/src"/>
     <classpathentry kind="src" path="cts/tests/tests/widget/src"/>
-    <classpathentry kind="src" path="cts/tools/annotation-helper/src"/>
     <classpathentry kind="src" path="cts/tools/cts-api-coverage/src"/>
     <classpathentry kind="src" path="cts/tools/cts-reference-app-lib/src"/>
     <classpathentry kind="src" path="cts/tools/dasm/src"/>
@@ -58,8 +59,6 @@
     <classpathentry kind="src" path="cts/tools/host/test"/>
     <classpathentry kind="src" path="cts/tools/signature-tools/src"/>
     <classpathentry kind="src" path="cts/tools/signature-tools/test"/>
-    <classpathentry kind="src" path="cts/tools/spec-progress/src"/>
-    <classpathentry kind="src" path="cts/tools/test-progress-new/src"/>
     <classpathentry kind="src" path="cts/tools/utils"/>
     <classpathentry kind="src" path="cts/tools/vm-tests/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsAccessibilityServiceTestCases_intermediates/src/src"/>
diff --git a/libs/annotation/Android.mk b/libs/annotation/Android.mk
deleted file mode 100644
index 3a4dd82..0000000
--- a/libs/annotation/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2010 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.
-#
-
-# Build static Java library for APKs like the CTS tests. 
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_MODULE := CtsTestAnnotationsLib
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Build the annotations for the host to use.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_MODULE := CtsTestAnnotationsHostLib
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/libs/annotation/src/android/annotation/cts/RequiredFeatures.java b/libs/annotation/src/android/annotation/cts/RequiredFeatures.java
deleted file mode 100644
index 8675b34..0000000
--- a/libs/annotation/src/android/annotation/cts/RequiredFeatures.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2010 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.annotation.cts;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation for specifying what features are needed to run a test. The device must have all the
- * features specified by the annotation in order to be executed.
- * <p>
- * Examples:
- * <pre>
- * @RequiredFeatures(PackageManager.FEATURE_WIFI)
- * @RequiredFeatures({PackageManager.FEATURE_TELEPHONY, PackageManager.FEATURE_TELEPHONY_CDMA})
- * </pre>
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE, ElementType.METHOD})
-public @interface RequiredFeatures {
-    String[] value();
-}
diff --git a/apps/CtsVerifier/tests/Android.mk b/libs/json/Android.mk
similarity index 76%
copy from apps/CtsVerifier/tests/Android.mk
copy to libs/json/Android.mk
index b9572fb..7ec9e79 100644
--- a/apps/CtsVerifier/tests/Android.mk
+++ b/libs/json/Android.mk
@@ -16,19 +16,10 @@
 
 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 := CtsVerifierTests
+LOCAL_MODULE := jsonlib
+LOCAL_MODULE_TAGS := optional
 
-LOCAL_INSTRUMENTATION_FOR := CtsVerifier
+include $(BUILD_HOST_JAVA_LIBRARY)
 
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
diff --git a/libs/json/src/com/android/json/stream/JsonReader.java b/libs/json/src/com/android/json/stream/JsonReader.java
new file mode 100644
index 0000000..d912d62
--- /dev/null
+++ b/libs/json/src/com/android/json/stream/JsonReader.java
@@ -0,0 +1,1106 @@
+/*
+ * Copyright (C) 2010 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.json.stream;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
+ * encoded value as a stream of tokens. This stream includes both literal
+ * values (strings, numbers, booleans, and nulls) as well as the begin and
+ * end delimiters of objects and arrays. The tokens are traversed in
+ * depth-first order, the same order that they appear in the JSON document.
+ * Within JSON objects, name/value pairs are represented by a single token.
+ *
+ * <h3>Parsing JSON</h3>
+ * To create a recursive descent parser for your own JSON streams, first create
+ * an entry point method that creates a {@code JsonReader}.
+ *
+ * <p>Next, create handler methods for each structure in your JSON text. You'll
+ * need a method for each object type and for each array type.
+ * <ul>
+ *   <li>Within <strong>array handling</strong> methods, first call {@link
+ *       #beginArray} to consume the array's opening bracket. Then create a
+ *       while loop that accumulates values, terminating when {@link #hasNext}
+ *       is false. Finally, read the array's closing bracket by calling {@link
+ *       #endArray}.
+ *   <li>Within <strong>object handling</strong> methods, first call {@link
+ *       #beginObject} to consume the object's opening brace. Then create a
+ *       while loop that assigns values to local variables based on their name.
+ *       This loop should terminate when {@link #hasNext} is false. Finally,
+ *       read the object's closing brace by calling {@link #endObject}.
+ * </ul>
+ * <p>When a nested object or array is encountered, delegate to the
+ * corresponding handler method.
+ *
+ * <p>When an unknown name is encountered, strict parsers should fail with an
+ * exception. Lenient parsers should call {@link #skipValue()} to recursively
+ * skip the value's nested tokens, which may otherwise conflict.
+ *
+ * <p>If a value may be null, you should first check using {@link #peek()}.
+ * Null literals can be consumed using either {@link #nextNull()} or {@link
+ * #skipValue()}.
+ *
+ * <h3>Example</h3>
+ * Suppose we'd like to parse a stream of messages such as the following: <pre> {@code
+ * [
+ *   {
+ *     "id": 912345678901,
+ *     "text": "How do I read JSON on Android?",
+ *     "geo": null,
+ *     "user": {
+ *       "name": "android_newb",
+ *       "followers_count": 41
+ *      }
+ *   },
+ *   {
+ *     "id": 912345678902,
+ *     "text": "@android_newb just use android.util.JsonReader!",
+ *     "geo": [50.454722, -104.606667],
+ *     "user": {
+ *       "name": "jesse",
+ *       "followers_count": 2
+ *     }
+ *   }
+ * ]}</pre>
+ * This code implements the parser for the above structure: <pre>   {@code
+ *
+ *   public List<Message> readJsonStream(InputStream in) throws IOException {
+ *     JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
+ *     return readMessagesArray(reader);
+ *   }
+ *
+ *   public List<Message> readMessagesArray(JsonReader reader) throws IOException {
+ *     List<Message> messages = new ArrayList<Message>();
+ *
+ *     reader.beginArray();
+ *     while (reader.hasNext()) {
+ *       messages.add(readMessage(reader));
+ *     }
+ *     reader.endArray();
+ *     return messages;
+ *   }
+ *
+ *   public Message readMessage(JsonReader reader) throws IOException {
+ *     long id = -1;
+ *     String text = null;
+ *     User user = null;
+ *     List<Double> geo = null;
+ *
+ *     reader.beginObject();
+ *     while (reader.hasNext()) {
+ *       String name = reader.nextName();
+ *       if (name.equals("id")) {
+ *         id = reader.nextLong();
+ *       } else if (name.equals("text")) {
+ *         text = reader.nextString();
+ *       } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
+ *         geo = readDoublesArray(reader);
+ *       } else if (name.equals("user")) {
+ *         user = readUser(reader);
+ *       } else {
+ *         reader.skipValue();
+ *       }
+ *     }
+ *     reader.endObject();
+ *     return new Message(id, text, user, geo);
+ *   }
+ *
+ *   public List<Double> readDoublesArray(JsonReader reader) throws IOException {
+ *     List<Double> doubles = new ArrayList<Double>();
+ *
+ *     reader.beginArray();
+ *     while (reader.hasNext()) {
+ *       doubles.add(reader.nextDouble());
+ *     }
+ *     reader.endArray();
+ *     return doubles;
+ *   }
+ *
+ *   public User readUser(JsonReader reader) throws IOException {
+ *     String username = null;
+ *     int followersCount = -1;
+ *
+ *     reader.beginObject();
+ *     while (reader.hasNext()) {
+ *       String name = reader.nextName();
+ *       if (name.equals("name")) {
+ *         username = reader.nextString();
+ *       } else if (name.equals("followers_count")) {
+ *         followersCount = reader.nextInt();
+ *       } else {
+ *         reader.skipValue();
+ *       }
+ *     }
+ *     reader.endObject();
+ *     return new User(username, followersCount);
+ *   }}</pre>
+ *
+ * <h3>Number Handling</h3>
+ * This reader permits numeric values to be read as strings and string values to
+ * be read as numbers. For example, both elements of the JSON array {@code
+ * [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}.
+ * This behavior is intended to prevent lossy numeric conversions: double is
+ * JavaScript's only numeric type and very large values like {@code
+ * 9007199254740993} cannot be represented exactly on that platform. To minimize
+ * precision loss, extremely large values should be written and read as strings
+ * in JSON.
+ *
+ * <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances
+ * of this class are not thread safe.
+ */
+public final class JsonReader implements Closeable {
+
+    private static final String TRUE = "true";
+    private static final String FALSE = "false";
+
+    /** The input JSON. */
+    private final Reader in;
+
+    /** True to accept non-spec compliant JSON */
+    private boolean lenient = false;
+
+    /**
+     * Use a manual buffer to easily read and unread upcoming characters, and
+     * also so we can create strings without an intermediate StringBuilder.
+     * We decode literals directly out of this buffer, so it must be at least as
+     * long as the longest token that can be reported as a number.
+     */
+    private final char[] buffer = new char[1024];
+    private int pos = 0;
+    private int limit = 0;
+
+    private final List<JsonScope> stack = new ArrayList<JsonScope>();
+    {
+        push(JsonScope.EMPTY_DOCUMENT);
+    }
+
+    /**
+     * The type of the next token to be returned by {@link #peek} and {@link
+     * #advance}. If null, peek() will assign a value.
+     */
+    private JsonToken token;
+
+    /** The text of the next name. */
+    private String name;
+
+    /*
+     * For the next literal value, we may have the text value, or the position
+     * and length in the buffer.
+     */
+    private String value;
+    private int valuePos;
+    private int valueLength;
+
+    /** True if we're currently handling a skipValue() call. */
+    private boolean skipping = false;
+
+    /**
+     * Creates a new instance that reads a JSON-encoded stream from {@code in}.
+     */
+    public JsonReader(Reader in) {
+        if (in == null) {
+            throw new NullPointerException("in == null");
+        }
+        this.in = in;
+    }
+
+    /**
+     * Configure this parser to be  be liberal in what it accepts. By default,
+     * this parser is strict and only accepts JSON as specified by <a
+     * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the
+     * parser to lenient causes it to ignore the following syntax errors:
+     *
+     * <ul>
+     *   <li>End of line comments starting with {@code //} or {@code #} and
+     *       ending with a newline character.
+     *   <li>C-style comments starting with {@code /*} and ending with
+     *       {@code *}{@code /}. Such comments may not be nested.
+     *   <li>Names that are unquoted or {@code 'single quoted'}.
+     *   <li>Strings that are unquoted or {@code 'single quoted'}.
+     *   <li>Array elements separated by {@code ;} instead of {@code ,}.
+     *   <li>Unnecessary array separators. These are interpreted as if null
+     *       was the omitted value.
+     *   <li>Names and values separated by {@code =} or {@code =>} instead of
+     *       {@code :}.
+     *   <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
+     * </ul>
+     */
+    public void setLenient(boolean lenient) {
+        this.lenient = lenient;
+    }
+
+    /**
+     * Consumes the next token from the JSON stream and asserts that it is the
+     * beginning of a new array.
+     */
+    public void beginArray() throws IOException {
+        expect(JsonToken.BEGIN_ARRAY);
+    }
+
+    /**
+     * Consumes the next token from the JSON stream and asserts that it is the
+     * end of the current array.
+     */
+    public void endArray() throws IOException {
+        expect(JsonToken.END_ARRAY);
+    }
+
+    /**
+     * Consumes the next token from the JSON stream and asserts that it is the
+     * beginning of a new object.
+     */
+    public void beginObject() throws IOException {
+        expect(JsonToken.BEGIN_OBJECT);
+    }
+
+    /**
+     * Consumes the next token from the JSON stream and asserts that it is the
+     * end of the current array.
+     */
+    public void endObject() throws IOException {
+        expect(JsonToken.END_OBJECT);
+    }
+
+    /**
+     * Consumes {@code expected}.
+     */
+    private void expect(JsonToken expected) throws IOException {
+        peek();
+        if (token != expected) {
+            throw new IllegalStateException("Expected " + expected + " but was " + peek());
+        }
+        advance();
+    }
+
+    /**
+     * Returns true if the current array or object has another element.
+     */
+    public boolean hasNext() throws IOException {
+        peek();
+        return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY;
+    }
+
+    /**
+     * Returns the type of the next token without consuming it.
+     */
+    public JsonToken peek() throws IOException {
+        if (token != null) {
+          return token;
+        }
+
+        switch (peekStack()) {
+            case EMPTY_DOCUMENT:
+                replaceTop(JsonScope.NONEMPTY_DOCUMENT);
+                JsonToken firstToken = nextValue();
+                if (token != JsonToken.BEGIN_ARRAY && token != JsonToken.BEGIN_OBJECT) {
+                    throw new IOException(
+                            "Expected JSON document to start with '[' or '{' but was " + token);
+                }
+                return firstToken;
+            case EMPTY_ARRAY:
+                return nextInArray(true);
+            case NONEMPTY_ARRAY:
+                return nextInArray(false);
+            case EMPTY_OBJECT:
+                return nextInObject(true);
+            case DANGLING_NAME:
+                return objectValue();
+            case NONEMPTY_OBJECT:
+                return nextInObject(false);
+            case NONEMPTY_DOCUMENT:
+                return token = JsonToken.END_DOCUMENT;
+            case CLOSED:
+                throw new IllegalStateException("JsonReader is closed");
+            default:
+                throw new AssertionError();
+        }
+    }
+
+    /**
+     * Advances the cursor in the JSON stream to the next token.
+     */
+    private JsonToken advance() throws IOException {
+        peek();
+
+        JsonToken result = token;
+        token = null;
+        value = null;
+        name = null;
+        return result;
+    }
+
+    /**
+     * Returns the next token, a {@link JsonToken#NAME property name}, and
+     * consumes it.
+     *
+     * @throws IOException if the next token in the stream is not a property
+     *     name.
+     */
+    public String nextName() throws IOException {
+        peek();
+        if (token != JsonToken.NAME) {
+            throw new IllegalStateException("Expected a name but was " + peek());
+        }
+        String result = name;
+        advance();
+        return result;
+    }
+
+    /**
+     * Returns the {@link JsonToken#STRING string} value of the next token,
+     * consuming it. If the next token is a number, this method will return its
+     * string form.
+     *
+     * @throws IllegalStateException if the next token is not a string or if
+     *     this reader is closed.
+     */
+    public String nextString() throws IOException {
+        peek();
+        if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
+            throw new IllegalStateException("Expected a string but was " + peek());
+        }
+
+        String result = value;
+        advance();
+        return result;
+    }
+
+    /**
+     * Returns the {@link JsonToken#BOOLEAN boolean} value of the next token,
+     * consuming it.
+     *
+     * @throws IllegalStateException if the next token is not a boolean or if
+     *     this reader is closed.
+     */
+    public boolean nextBoolean() throws IOException {
+        peek();
+        if (token != JsonToken.BOOLEAN) {
+            throw new IllegalStateException("Expected a boolean but was " + token);
+        }
+
+        boolean result = (value == TRUE);
+        advance();
+        return result;
+    }
+
+    /**
+     * Consumes the next token from the JSON stream and asserts that it is a
+     * literal null.
+     *
+     * @throws IllegalStateException if the next token is not null or if this
+     *     reader is closed.
+     */
+    public void nextNull() throws IOException {
+        peek();
+        if (token != JsonToken.NULL) {
+            throw new IllegalStateException("Expected null but was " + token);
+        }
+
+        advance();
+    }
+
+    /**
+     * Returns the {@link JsonToken#NUMBER double} value of the next token,
+     * consuming it. If the next token is a string, this method will attempt to
+     * parse it as a double using {@link Double#parseDouble(String)}.
+     *
+     * @throws IllegalStateException if the next token is not a literal value.
+     */
+    public double nextDouble() throws IOException {
+        peek();
+        if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
+            throw new IllegalStateException("Expected a double but was " + token);
+        }
+
+        double result = Double.parseDouble(value);
+        advance();
+        return result;
+    }
+
+    /**
+     * Returns the {@link JsonToken#NUMBER long} value of the next token,
+     * consuming it. If the next token is a string, this method will attempt to
+     * parse it as a long. If the next token's numeric value cannot be exactly
+     * represented by a Java {@code long}, this method throws.
+     *
+     * @throws IllegalStateException if the next token is not a literal value.
+     * @throws NumberFormatException if the next literal value cannot be parsed
+     *     as a number, or exactly represented as a long.
+     */
+    public long nextLong() throws IOException {
+        peek();
+        if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
+            throw new IllegalStateException("Expected a long but was " + token);
+        }
+
+        long result;
+        try {
+            result = Long.parseLong(value);
+        } catch (NumberFormatException ignored) {
+            double asDouble = Double.parseDouble(value); // don't catch this NumberFormatException
+            result = (long) asDouble;
+            if ((double) result != asDouble) {
+                throw new NumberFormatException(value);
+            }
+        }
+
+        advance();
+        return result;
+    }
+
+    /**
+     * Returns the {@link JsonToken#NUMBER int} value of the next token,
+     * consuming it. If the next token is a string, this method will attempt to
+     * parse it as an int. If the next token's numeric value cannot be exactly
+     * represented by a Java {@code int}, this method throws.
+     *
+     * @throws IllegalStateException if the next token is not a literal value.
+     * @throws NumberFormatException if the next literal value cannot be parsed
+     *     as a number, or exactly represented as an int.
+     */
+    public int nextInt() throws IOException {
+        peek();
+        if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
+            throw new IllegalStateException("Expected an int but was " + token);
+        }
+
+        int result;
+        try {
+            result = Integer.parseInt(value);
+        } catch (NumberFormatException ignored) {
+            double asDouble = Double.parseDouble(value); // don't catch this NumberFormatException
+            result = (int) asDouble;
+            if ((double) result != asDouble) {
+                throw new NumberFormatException(value);
+            }
+        }
+
+        advance();
+        return result;
+    }
+
+    /**
+     * Closes this JSON reader and the underlying {@link Reader}.
+     */
+    public void close() throws IOException {
+        value = null;
+        token = null;
+        stack.clear();
+        stack.add(JsonScope.CLOSED);
+        in.close();
+    }
+
+    /**
+     * Skips the next value recursively. If it is an object or array, all nested
+     * elements are skipped. This method is intended for use when the JSON token
+     * stream contains unrecognized or unhandled values.
+     */
+    public void skipValue() throws IOException {
+        skipping = true;
+        try {
+            int count = 0;
+            do {
+                JsonToken token = advance();
+                if (token == JsonToken.BEGIN_ARRAY || token == JsonToken.BEGIN_OBJECT) {
+                    count++;
+                } else if (token == JsonToken.END_ARRAY || token == JsonToken.END_OBJECT) {
+                    count--;
+                }
+            } while (count != 0);
+        } finally {
+            skipping = false;
+        }
+    }
+
+    private JsonScope peekStack() {
+        return stack.get(stack.size() - 1);
+    }
+
+    private JsonScope pop() {
+        return stack.remove(stack.size() - 1);
+    }
+
+    private void push(JsonScope newTop) {
+        stack.add(newTop);
+    }
+
+    /**
+     * Replace the value on the top of the stack with the given value.
+     */
+    private void replaceTop(JsonScope newTop) {
+        stack.set(stack.size() - 1, newTop);
+    }
+
+    private JsonToken nextInArray(boolean firstElement) throws IOException {
+        if (firstElement) {
+            replaceTop(JsonScope.NONEMPTY_ARRAY);
+        } else {
+            /* Look for a comma before each element after the first element. */
+            switch (nextNonWhitespace()) {
+                case ']':
+                    pop();
+                    return token = JsonToken.END_ARRAY;
+                case ';':
+                    checkLenient(); // fall-through
+                case ',':
+                    break;
+                default:
+                    throw syntaxError("Unterminated array");
+            }
+        }
+
+        switch (nextNonWhitespace()) {
+            case ']':
+                if (firstElement) {
+                    pop();
+                    return token = JsonToken.END_ARRAY;
+                }
+                // fall-through to handle ",]"
+            case ';':
+            case ',':
+                /* In lenient mode, a 0-length literal means 'null' */
+                checkLenient();
+                pos--;
+                value = "null";
+                return token = JsonToken.NULL;
+            default:
+                pos--;
+                return nextValue();
+        }
+    }
+
+    private JsonToken nextInObject(boolean firstElement) throws IOException {
+        /*
+         * Read delimiters. Either a comma/semicolon separating this and the
+         * previous name-value pair, or a close brace to denote the end of the
+         * object.
+         */
+        if (firstElement) {
+            /* Peek to see if this is the empty object. */
+            switch (nextNonWhitespace()) {
+                case '}':
+                    pop();
+                    return token = JsonToken.END_OBJECT;
+                default:
+                    pos--;
+            }
+        } else {
+            switch (nextNonWhitespace()) {
+                case '}':
+                    pop();
+                    return token = JsonToken.END_OBJECT;
+                case ';':
+                case ',':
+                    break;
+                default:
+                    throw syntaxError("Unterminated object");
+            }
+        }
+
+        /* Read the name. */
+        int quote = nextNonWhitespace();
+        switch (quote) {
+            case '\'':
+                checkLenient(); // fall-through
+            case '"':
+                name = nextString((char) quote);
+                break;
+            default:
+                checkLenient();
+                pos--;
+                name = nextLiteral(false);
+                if (name.isEmpty()) {
+                    throw syntaxError("Expected name");
+                }
+        }
+
+        replaceTop(JsonScope.DANGLING_NAME);
+        return token = JsonToken.NAME;
+    }
+
+    private JsonToken objectValue() throws IOException {
+        /*
+         * Read the name/value separator. Usually a colon ':'. In lenient mode
+         * we also accept an equals sign '=', or an arrow "=>".
+         */
+        switch (nextNonWhitespace()) {
+            case ':':
+                break;
+            case '=':
+                checkLenient();
+                if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
+                    pos++;
+                }
+                break;
+            default:
+                throw syntaxError("Expected ':'");
+        }
+
+        replaceTop(JsonScope.NONEMPTY_OBJECT);
+        return nextValue();
+    }
+
+    private JsonToken nextValue() throws IOException {
+        int c = nextNonWhitespace();
+        switch (c) {
+            case '{':
+                push(JsonScope.EMPTY_OBJECT);
+                return token = JsonToken.BEGIN_OBJECT;
+
+            case '[':
+                push(JsonScope.EMPTY_ARRAY);
+                return token = JsonToken.BEGIN_ARRAY;
+
+            case '\'':
+                checkLenient(); // fall-through
+            case '"':
+                value = nextString((char) c);
+                return token = JsonToken.STRING;
+
+            default:
+                pos--;
+                return readLiteral();
+        }
+    }
+
+    /**
+     * Returns true once {@code limit - pos >= minimum}. If the data is
+     * exhausted before that many characters are available, this returns
+     * false.
+     */
+    private boolean fillBuffer(int minimum) throws IOException {
+        if (limit != pos) {
+            limit -= pos;
+            System.arraycopy(buffer, pos, buffer, 0, limit);
+        } else {
+            limit = 0;
+        }
+
+        pos = 0;
+        int total;
+        while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) {
+            limit += total;
+            if (limit >= minimum) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private int nextNonWhitespace() throws IOException {
+        while (pos < limit || fillBuffer(1)) {
+            int c = buffer[pos++];
+            switch (c) {
+                case '\t':
+                case ' ':
+                case '\n':
+                case '\r':
+                    continue;
+
+                case '/':
+                    if (pos == limit && !fillBuffer(1)) {
+                        return c;
+                    }
+
+                    checkLenient();
+                    char peek = buffer[pos];
+                    switch (peek) {
+                        case '*':
+                            // skip a /* c-style comment */
+                            pos++;
+                            if (!skipTo("*/")) {
+                                throw syntaxError("Unterminated comment");
+                            }
+                            pos += 2;
+                            continue;
+
+                        case '/':
+                            // skip a // end-of-line comment
+                            pos++;
+                            skipToEndOfLine();
+                            continue;
+
+                        default:
+                            return c;
+                    }
+
+                case '#':
+                    /*
+                     * Skip a # hash end-of-line comment. The JSON RFC doesn't
+                     * specify this behaviour, but it's required to parse
+                     * existing documents. See http://b/2571423.
+                     */
+                    checkLenient();
+                    skipToEndOfLine();
+                    continue;
+
+                default:
+                    return c;
+            }
+        }
+
+        throw syntaxError("End of input");
+    }
+
+    private void checkLenient() throws IOException {
+        if (!lenient) {
+            throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON");
+        }
+    }
+
+    /**
+     * Advances the position until after the next newline character. If the line
+     * is terminated by "\r\n", the '\n' must be consumed as whitespace by the
+     * caller.
+     */
+    private void skipToEndOfLine() throws IOException {
+        while (pos < limit || fillBuffer(1)) {
+            char c = buffer[pos++];
+            if (c == '\r' || c == '\n') {
+                break;
+            }
+        }
+    }
+
+    private boolean skipTo(String toFind) throws IOException {
+        outer:
+        for (; pos + toFind.length() < limit || fillBuffer(toFind.length()); pos++) {
+            for (int c = 0; c < toFind.length(); c++) {
+                if (buffer[pos + c] != toFind.charAt(c)) {
+                    continue outer;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the string up to but not including {@code quote}, unescaping any
+     * character escape sequences encountered along the way. The opening quote
+     * should have already been read. This consumes the closing quote, but does
+     * not include it in the returned string.
+     *
+     * @param quote either ' or ".
+     * @throws NumberFormatException if any unicode escape sequences are
+     *     malformed.
+     */
+    private String nextString(char quote) throws IOException {
+        StringBuilder builder = null;
+        do {
+            /* the index of the first character not yet appended to the builder. */
+            int start = pos;
+            while (pos < limit) {
+                int c = buffer[pos++];
+
+                if (c == quote) {
+                    if (skipping) {
+                        return "skipped!";
+                    } else if (builder == null) {
+                        return new String(buffer, start, pos - start - 1);
+                    } else {
+                        builder.append(buffer, start, pos - start - 1);
+                        return builder.toString();
+                    }
+
+                } else if (c == '\\') {
+                    if (builder == null) {
+                        builder = new StringBuilder();
+                    }
+                    builder.append(buffer, start, pos - start - 1);
+                    builder.append(readEscapeCharacter());
+                    start = pos;
+                }
+            }
+
+            if (builder == null) {
+                builder = new StringBuilder();
+            }
+            builder.append(buffer, start, pos - start);
+        } while (fillBuffer(1));
+
+        throw syntaxError("Unterminated string");
+    }
+
+    /**
+     * Reads the value up to but not including any delimiter characters. This
+     * does not consume the delimiter character.
+     *
+     * @param assignOffsetsOnly true for this method to only set the valuePos
+     *     and valueLength fields and return a null result. This only works if
+     *     the literal is short; a string is returned otherwise.
+     */
+    private String nextLiteral(boolean assignOffsetsOnly) throws IOException {
+        StringBuilder builder = null;
+        valuePos = -1;
+        valueLength = 0;
+        int i = 0;
+
+        findNonLiteralCharacter:
+        while (true) {
+            for (; pos + i < limit; i++) {
+                switch (buffer[pos + i]) {
+                case '/':
+                case '\\':
+                case ';':
+                case '#':
+                case '=':
+                    checkLenient(); // fall-through
+                case '{':
+                case '}':
+                case '[':
+                case ']':
+                case ':':
+                case ',':
+                case ' ':
+                case '\t':
+                case '\f':
+                case '\r':
+                case '\n':
+                    break findNonLiteralCharacter;
+                }
+            }
+
+            /*
+             * Attempt to load the entire literal into the buffer at once. If
+             * we run out of input, add a non-literal character at the end so
+             * that decoding doesn't need to do bounds checks.
+             */
+            if (i < buffer.length) {
+                if (fillBuffer(i + 1)) {
+                    continue;
+                } else {
+                    buffer[limit] = '\0';
+                    break;
+                }
+            }
+
+            // use a StringBuilder when the value is too long. It must be an unquoted string.
+            if (builder == null) {
+                builder = new StringBuilder();
+            }
+            builder.append(buffer, pos, i);
+            valueLength += i;
+            pos += i;
+            i = 0;
+            if (!fillBuffer(1)) {
+                break;
+            }
+        }
+
+        String result;
+        if (assignOffsetsOnly && builder == null) {
+            valuePos = pos;
+            result = null;
+        } else if (skipping) {
+            result = "skipped!";
+        } else if (builder == null) {
+            result = new String(buffer, pos, i);
+        } else {
+            builder.append(buffer, pos, i);
+            result = builder.toString();
+        }
+        valueLength += i;
+        pos += i;
+        return result;
+    }
+
+    @Override public String toString() {
+        return getClass().getSimpleName() + " near " + getSnippet();
+    }
+
+    /**
+     * Unescapes the character identified by the character or characters that
+     * immediately follow a backslash. The backslash '\' should have already
+     * been read. This supports both unicode escapes "u000A" and two-character
+     * escapes "\n".
+     *
+     * @throws NumberFormatException if any unicode escape sequences are
+     *     malformed.
+     */
+    private char readEscapeCharacter() throws IOException {
+        if (pos == limit && !fillBuffer(1)) {
+            throw syntaxError("Unterminated escape sequence");
+        }
+
+        char escaped = buffer[pos++];
+        switch (escaped) {
+            case 'u':
+                if (pos + 4 > limit && !fillBuffer(4)) {
+                    throw syntaxError("Unterminated escape sequence");
+                }
+                String hex = new String(buffer, pos, 4);
+                pos += 4;
+                return (char) Integer.parseInt(hex, 16);
+
+            case 't':
+                return '\t';
+
+            case 'b':
+                return '\b';
+
+            case 'n':
+                return '\n';
+
+            case 'r':
+                return '\r';
+
+            case 'f':
+                return '\f';
+
+            case '\'':
+            case '"':
+            case '\\':
+            default:
+                return escaped;
+        }
+    }
+
+    /**
+     * Reads a null, boolean, numeric or unquoted string literal value.
+     */
+    private JsonToken readLiteral() throws IOException {
+        value = nextLiteral(true);
+        if (valueLength == 0) {
+            throw syntaxError("Expected literal value");
+        }
+        token = decodeLiteral();
+        if (token == JsonToken.STRING) {
+          checkLenient();
+        }
+        return token;
+    }
+
+    /**
+     * Assigns {@code nextToken} based on the value of {@code nextValue}.
+     */
+    private JsonToken decodeLiteral() throws IOException {
+        if (valuePos == -1) {
+            // it was too long to fit in the buffer so it can only be a string
+            return JsonToken.STRING;
+        } else if (valueLength == 4
+                && ('n' == buffer[valuePos    ] || 'N' == buffer[valuePos    ])
+                && ('u' == buffer[valuePos + 1] || 'U' == buffer[valuePos + 1])
+                && ('l' == buffer[valuePos + 2] || 'L' == buffer[valuePos + 2])
+                && ('l' == buffer[valuePos + 3] || 'L' == buffer[valuePos + 3])) {
+            value = "null";
+            return JsonToken.NULL;
+        } else if (valueLength == 4
+                && ('t' == buffer[valuePos    ] || 'T' == buffer[valuePos    ])
+                && ('r' == buffer[valuePos + 1] || 'R' == buffer[valuePos + 1])
+                && ('u' == buffer[valuePos + 2] || 'U' == buffer[valuePos + 2])
+                && ('e' == buffer[valuePos + 3] || 'E' == buffer[valuePos + 3])) {
+            value = TRUE;
+            return JsonToken.BOOLEAN;
+        } else if (valueLength == 5
+                && ('f' == buffer[valuePos    ] || 'F' == buffer[valuePos    ])
+                && ('a' == buffer[valuePos + 1] || 'A' == buffer[valuePos + 1])
+                && ('l' == buffer[valuePos + 2] || 'L' == buffer[valuePos + 2])
+                && ('s' == buffer[valuePos + 3] || 'S' == buffer[valuePos + 3])
+                && ('e' == buffer[valuePos + 4] || 'E' == buffer[valuePos + 4])) {
+            value = FALSE;
+            return JsonToken.BOOLEAN;
+        } else {
+            value = new String(buffer, valuePos, valueLength);
+            return decodeNumber(buffer, valuePos, valueLength);
+        }
+    }
+
+    /**
+     * Determine whether the characters is a JSON number. Numbers are of the
+     * form -12.34e+56. Fractional and exponential parts are optional. Leading
+     * zeroes are not allowed in the value or exponential part, but are allowed
+     * in the fraction.
+     *
+     * <p>This has a side effect of setting isInteger.
+     */
+    private JsonToken decodeNumber(char[] chars, int offset, int length) {
+        int i = offset;
+        int c = chars[i];
+
+        if (c == '-') {
+            c = chars[++i];
+        }
+
+        if (c == '0') {
+            c = chars[++i];
+        } else if (c >= '1' && c <= '9') {
+            c = chars[++i];
+            while (c >= '0' && c <= '9') {
+                c = chars[++i];
+            }
+        } else {
+            return JsonToken.STRING;
+        }
+
+        if (c == '.') {
+            c = chars[++i];
+            while (c >= '0' && c <= '9') {
+                c = chars[++i];
+            }
+        }
+
+        if (c == 'e' || c == 'E') {
+            c = chars[++i];
+            if (c == '+' || c == '-') {
+                c = chars[++i];
+            }
+            if (c >= '0' && c <= '9') {
+                c = chars[++i];
+                while (c >= '0' && c <= '9') {
+                    c = chars[++i];
+                }
+            } else {
+                return JsonToken.STRING;
+            }
+        }
+
+        if (i == offset + length) {
+            return JsonToken.NUMBER;
+        } else {
+            return JsonToken.STRING;
+        }
+    }
+
+    /**
+     * Throws a new IO exception with the given message and a context snippet
+     * with this reader's content.
+     */
+    public IOException syntaxError(String message) throws IOException {
+        throw new JsonSyntaxException(message + " near " + getSnippet());
+    }
+
+    private CharSequence getSnippet() {
+        StringBuilder snippet = new StringBuilder();
+        int beforePos = Math.min(pos, 20);
+        snippet.append(buffer, pos - beforePos, beforePos);
+        int afterPos = Math.min(limit - pos, 20);
+        snippet.append(buffer, pos, afterPos);
+        return snippet;
+    }
+
+    private static class JsonSyntaxException extends IOException {
+        private JsonSyntaxException(String s) {
+            super(s);
+        }
+    }
+}
diff --git a/libs/json/src/com/android/json/stream/JsonScope.java b/libs/json/src/com/android/json/stream/JsonScope.java
new file mode 100644
index 0000000..12d10e5
--- /dev/null
+++ b/libs/json/src/com/android/json/stream/JsonScope.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 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.json.stream;
+
+/**
+ * Lexical scoping elements within a JSON reader or writer.
+ */
+enum JsonScope {
+
+    /**
+     * An array with no elements requires no separators or newlines before
+     * it is closed.
+     */
+    EMPTY_ARRAY,
+
+    /**
+     * A array with at least one value requires a comma and newline before
+     * the next element.
+     */
+    NONEMPTY_ARRAY,
+
+    /**
+     * An object with no name/value pairs requires no separators or newlines
+     * before it is closed.
+     */
+    EMPTY_OBJECT,
+
+    /**
+     * An object whose most recent element is a key. The next element must
+     * be a value.
+     */
+    DANGLING_NAME,
+
+    /**
+     * An object with at least one name/value pair requires a comma and
+     * newline before the next element.
+     */
+    NONEMPTY_OBJECT,
+
+    /**
+     * No object or array has been started.
+     */
+    EMPTY_DOCUMENT,
+
+    /**
+     * A document with at an array or object.
+     */
+    NONEMPTY_DOCUMENT,
+
+    /**
+     * A document that's been closed and cannot be accessed.
+     */
+    CLOSED,
+}
diff --git a/libs/json/src/com/android/json/stream/JsonToken.java b/libs/json/src/com/android/json/stream/JsonToken.java
new file mode 100644
index 0000000..a5233a4
--- /dev/null
+++ b/libs/json/src/com/android/json/stream/JsonToken.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 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.json.stream;
+
+/**
+ * A structure, name or value type in a JSON-encoded string.
+ */
+public enum JsonToken {
+
+    /**
+     * The opening of a JSON array. Written using {@link JsonWriter#beginObject}
+     * and read using {@link JsonReader#beginObject}.
+     */
+    BEGIN_ARRAY,
+
+    /**
+     * The closing of a JSON array. Written using {@link JsonWriter#endArray}
+     * and read using {@link JsonReader#endArray}.
+     */
+    END_ARRAY,
+
+    /**
+     * The opening of a JSON object. Written using {@link JsonWriter#beginObject}
+     * and read using {@link JsonReader#beginObject}.
+     */
+    BEGIN_OBJECT,
+
+    /**
+     * The closing of a JSON object. Written using {@link JsonWriter#endObject}
+     * and read using {@link JsonReader#endObject}.
+     */
+    END_OBJECT,
+
+    /**
+     * A JSON property name. Within objects, tokens alternate between names and
+     * their values. Written using {@link JsonWriter#name} and read using {@link
+     * JsonReader#nextName}
+     */
+    NAME,
+
+    /**
+     * A JSON string.
+     */
+    STRING,
+
+    /**
+     * A JSON number represented in this API by a Java {@code double}, {@code
+     * long}, or {@code int}.
+     */
+    NUMBER,
+
+    /**
+     * A JSON {@code true} or {@code false}.
+     */
+    BOOLEAN,
+
+    /**
+     * A JSON {@code null}.
+     */
+    NULL,
+
+    /**
+     * The end of the JSON stream. This sentinel value is returned by {@link
+     * JsonReader#peek()} to signal that the JSON-encoded value has no more
+     * tokens.
+     */
+    END_DOCUMENT
+}
diff --git a/libs/json/src/com/android/json/stream/JsonWriter.java b/libs/json/src/com/android/json/stream/JsonWriter.java
new file mode 100644
index 0000000..66b21f0
--- /dev/null
+++ b/libs/json/src/com/android/json/stream/JsonWriter.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2010 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.json.stream;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Writes a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
+ * encoded value to a stream, one token at a time. The stream includes both
+ * literal values (strings, numbers, booleans and nulls) as well as the begin
+ * and end delimiters of objects and arrays.
+ *
+ * <h3>Encoding JSON</h3>
+ * To encode your data as JSON, create a new {@code JsonWriter}. Each JSON
+ * document must contain one top-level array or object. Call methods on the
+ * writer as you walk the structure's contents, nesting arrays and objects as
+ * necessary:
+ * <ul>
+ *   <li>To write <strong>arrays</strong>, first call {@link #beginArray()}.
+ *       Write each of the array's elements with the appropriate {@link #value}
+ *       methods or by nesting other arrays and objects. Finally close the array
+ *       using {@link #endArray()}.
+ *   <li>To write <strong>objects</strong>, first call {@link #beginObject()}.
+ *       Write each of the object's properties by alternating calls to
+ *       {@link #name} with the property's value. Write property values with the
+ *       appropriate {@link #value} method or by nesting other objects or arrays.
+ *       Finally close the object using {@link #endObject()}.
+ * </ul>
+ *
+ * <h3>Example</h3>
+ * Suppose we'd like to encode a stream of messages such as the following: <pre> {@code
+ * [
+ *   {
+ *     "id": 912345678901,
+ *     "text": "How do I write JSON on Android?",
+ *     "geo": null,
+ *     "user": {
+ *       "name": "android_newb",
+ *       "followers_count": 41
+ *      }
+ *   },
+ *   {
+ *     "id": 912345678902,
+ *     "text": "@android_newb just use android.util.JsonWriter!",
+ *     "geo": [50.454722, -104.606667],
+ *     "user": {
+ *       "name": "jesse",
+ *       "followers_count": 2
+ *     }
+ *   }
+ * ]}</pre>
+ * This code encodes the above structure: <pre>   {@code
+ *   public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
+ *     JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
+ *     writer.setIndent("  ");
+ *     writeMessagesArray(writer, messages);
+ *     writer.close();
+ *   }
+ *
+ *   public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
+ *     writer.beginArray();
+ *     for (Message message : messages) {
+ *       writeMessage(writer, message);
+ *     }
+ *     writer.endArray();
+ *   }
+ *
+ *   public void writeMessage(JsonWriter writer, Message message) throws IOException {
+ *     writer.beginObject();
+ *     writer.name("id").value(message.getId());
+ *     writer.name("text").value(message.getText());
+ *     if (message.getGeo() != null) {
+ *       writer.name("geo");
+ *       writeDoublesArray(writer, message.getGeo());
+ *     } else {
+ *       writer.name("geo").nullValue();
+ *     }
+ *     writer.name("user");
+ *     writeUser(writer, message.getUser());
+ *     writer.endObject();
+ *   }
+ *
+ *   public void writeUser(JsonWriter writer, User user) throws IOException {
+ *     writer.beginObject();
+ *     writer.name("name").value(user.getName());
+ *     writer.name("followers_count").value(user.getFollowersCount());
+ *     writer.endObject();
+ *   }
+ *
+ *   public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
+ *     writer.beginArray();
+ *     for (Double value : doubles) {
+ *       writer.value(value);
+ *     }
+ *     writer.endArray();
+ *   }}</pre>
+ *
+ * <p>Each {@code JsonWriter} may be used to write a single JSON stream.
+ * Instances of this class are not thread safe. Calls that would result in a
+ * malformed JSON string will fail with an {@link IllegalStateException}.
+ */
+public final class JsonWriter implements Closeable {
+
+    /** The output data, containing at most one top-level array or object. */
+    private final Writer out;
+
+    private final List<JsonScope> stack = new ArrayList<JsonScope>();
+    {
+        stack.add(JsonScope.EMPTY_DOCUMENT);
+    }
+
+    /**
+     * A string containing a full set of spaces for a single level of
+     * indentation, or null for no pretty printing.
+     */
+    private String indent;
+
+    /**
+     * The name/value separator; either ":" or ": ".
+     */
+    private String separator = ":";
+
+    /**
+     * Creates a new instance that writes a JSON-encoded stream to {@code out}.
+     * For best performance, ensure {@link Writer} is buffered; wrapping in
+     * {@link java.io.BufferedWriter BufferedWriter} if necessary.
+     */
+    public JsonWriter(Writer out) {
+        if (out == null) {
+            throw new NullPointerException("out == null");
+        }
+        this.out = out;
+    }
+
+    /**
+     * Sets the indentation string to be repeated for each level of indentation
+     * in the encoded document. If {@code indent.isEmpty()} the encoded document
+     * will be compact. Otherwise the encoded document will be more
+     * human-readable.
+     *
+     * @param indent a string containing only whitespace.
+     */
+    public void setIndent(String indent) {
+        if (indent.isEmpty()) {
+            this.indent = null;
+            this.separator = ":";
+        } else {
+            this.indent = indent;
+            this.separator = ": ";
+        }
+    }
+
+    /**
+     * Begins encoding a new array. Each call to this method must be paired with
+     * a call to {@link #endArray}.
+     *
+     * @return this writer.
+     */
+    public JsonWriter beginArray() throws IOException {
+        return open(JsonScope.EMPTY_ARRAY, "[");
+    }
+
+    /**
+     * Ends encoding the current array.
+     *
+     * @return this writer.
+     */
+    public JsonWriter endArray() throws IOException {
+        return close(JsonScope.EMPTY_ARRAY, JsonScope.NONEMPTY_ARRAY, "]");
+    }
+
+    /**
+     * Begins encoding a new object. Each call to this method must be paired
+     * with a call to {@link #endObject}.
+     *
+     * @return this writer.
+     */
+    public JsonWriter beginObject() throws IOException {
+        return open(JsonScope.EMPTY_OBJECT, "{");
+    }
+
+    /**
+     * Ends encoding the current object.
+     *
+     * @return this writer.
+     */
+    public JsonWriter endObject() throws IOException {
+        return close(JsonScope.EMPTY_OBJECT, JsonScope.NONEMPTY_OBJECT, "}");
+    }
+
+    /**
+     * Enters a new scope by appending any necessary whitespace and the given
+     * bracket.
+     */
+    private JsonWriter open(JsonScope empty, String openBracket) throws IOException {
+        beforeValue(true);
+        stack.add(empty);
+        out.write(openBracket);
+        return this;
+    }
+
+    /**
+     * Closes the current scope by appending any necessary whitespace and the
+     * given bracket.
+     */
+    private JsonWriter close(JsonScope empty, JsonScope nonempty, String closeBracket)
+            throws IOException {
+        JsonScope context = peek();
+        if (context != nonempty && context != empty) {
+            throw new IllegalStateException("Nesting problem: " + stack);
+        }
+
+        stack.remove(stack.size() - 1);
+        if (context == nonempty) {
+            newline();
+        }
+        out.write(closeBracket);
+        return this;
+    }
+
+    /**
+     * Returns the value on the top of the stack.
+     */
+    private JsonScope peek() {
+        return stack.get(stack.size() - 1);
+    }
+
+    /**
+     * Replace the value on the top of the stack with the given value.
+     */
+    private void replaceTop(JsonScope topOfStack) {
+        stack.set(stack.size() - 1, topOfStack);
+    }
+
+    /**
+     * Encodes the property name.
+     *
+     * @param name the name of the forthcoming value. May not be null.
+     * @return this writer.
+     */
+    public JsonWriter name(String name) throws IOException {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+        beforeName();
+        string(name);
+        return this;
+    }
+
+    /**
+     * Encodes {@code value}.
+     *
+     * @param value the literal string value, or null to encode a null literal.
+     * @return this writer.
+     */
+    public JsonWriter value(String value) throws IOException {
+        if (value == null) {
+            return nullValue();
+        }
+        beforeValue(false);
+        string(value);
+        return this;
+    }
+
+    /**
+     * Encodes {@code null}.
+     *
+     * @return this writer.
+     */
+    public JsonWriter nullValue() throws IOException {
+        beforeValue(false);
+        out.write("null");
+        return this;
+    }
+
+    /**
+     * Encodes {@code value}.
+     *
+     * @return this writer.
+     */
+    public JsonWriter value(boolean value) throws IOException {
+        beforeValue(false);
+        out.write(value ? "true" : "false");
+        return this;
+    }
+
+    /**
+     * Encodes {@code value}.
+     *
+     * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
+     *     {@link Double#isInfinite() infinities}.
+     * @return this writer.
+     */
+    public JsonWriter value(double value) throws IOException {
+        if (Double.isNaN(value) || Double.isInfinite(value)) {
+            throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
+        }
+        beforeValue(false);
+        out.append(Double.toString(value));
+        return this;
+    }
+
+    /**
+     * Encodes {@code value}.
+     *
+     * @return this writer.
+     */
+    public JsonWriter value(long value) throws IOException {
+        beforeValue(false);
+        out.write(Long.toString(value));
+        return this;
+    }
+
+    /**
+     * Ensures all buffered data is written to the underlying {@link Writer}
+     * and flushes that writer.
+     */
+    public void flush() throws IOException {
+        out.flush();
+    }
+
+    /**
+     * Flushes and closes this writer and the underlying {@link Writer}.
+     *
+     * @throws IOException if the JSON document is incomplete.
+     */
+    public void close() throws IOException {
+        out.close();
+
+        if (peek() != JsonScope.NONEMPTY_DOCUMENT) {
+            throw new IOException("Incomplete document");
+        }
+    }
+
+    private void string(String value) throws IOException {
+        out.write("\"");
+        for (int i = 0, length = value.length(); i < length; i++) {
+            char c = value.charAt(i);
+
+            /*
+             * From RFC 4627, "All Unicode characters may be placed within the
+             * quotation marks except for the characters that must be escaped:
+             * quotation mark, reverse solidus, and the control characters
+             * (U+0000 through U+001F)."
+             */
+            switch (c) {
+                case '"':
+                case '\\':
+                case '/':
+                    out.write('\\');
+                    out.write(c);
+                    break;
+
+                case '\t':
+                    out.write("\\t");
+                    break;
+
+                case '\b':
+                    out.write("\\b");
+                    break;
+
+                case '\n':
+                    out.write("\\n");
+                    break;
+
+                case '\r':
+                    out.write("\\r");
+                    break;
+
+                case '\f':
+                    out.write("\\f");
+                    break;
+
+                default:
+                    if (c <= 0x1F) {
+                        out.write(String.format("\\u%04x", (int) c));
+                    } else {
+                        out.write(c);
+                    }
+                    break;
+            }
+
+        }
+        out.write("\"");
+    }
+
+    private void newline() throws IOException {
+        if (indent == null) {
+            return;
+        }
+
+        out.write("\n");
+        for (int i = 1; i < stack.size(); i++) {
+            out.write(indent);
+        }
+    }
+
+    /**
+     * Inserts any necessary separators and whitespace before a name. Also
+     * adjusts the stack to expect the name's value.
+     */
+    private void beforeName() throws IOException {
+        JsonScope context = peek();
+        if (context == JsonScope.NONEMPTY_OBJECT) { // first in object
+            out.write(',');
+        } else if (context != JsonScope.EMPTY_OBJECT) { // not in an object!
+            throw new IllegalStateException("Nesting problem: " + stack);
+        }
+        newline();
+        replaceTop(JsonScope.DANGLING_NAME);
+    }
+
+    /**
+     * Inserts any necessary separators and whitespace before a literal value,
+     * inline array, or inline object. Also adjusts the stack to expect either a
+     * closing bracket or another element.
+     *
+     * @param root true if the value is a new array or object, the two values
+     *     permitted as top-level elements.
+     */
+    private void beforeValue(boolean root) throws IOException {
+        switch (peek()) {
+            case EMPTY_DOCUMENT: // first in document
+                if (!root) {
+                    throw new IllegalStateException(
+                            "JSON must start with an array or an object.");
+                }
+                replaceTop(JsonScope.NONEMPTY_DOCUMENT);
+                break;
+
+            case EMPTY_ARRAY: // first in array
+                replaceTop(JsonScope.NONEMPTY_ARRAY);
+                newline();
+                break;
+
+            case NONEMPTY_ARRAY: // another in array
+                out.append(',');
+                newline();
+                break;
+
+            case DANGLING_NAME: // value for name
+                out.append(separator);
+                replaceTop(JsonScope.NONEMPTY_OBJECT);
+                break;
+
+            case NONEMPTY_DOCUMENT:
+                throw new IllegalStateException(
+                        "JSON must have only one top-level value.");
+
+            default:
+                throw new IllegalStateException("Nesting problem: " + stack);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/tests/Android.mk b/libs/vogar-expect/Android.mk
similarity index 76%
copy from apps/CtsVerifier/tests/Android.mk
copy to libs/vogar-expect/Android.mk
index b9572fb..075bb43 100644
--- a/apps/CtsVerifier/tests/Android.mk
+++ b/libs/vogar-expect/Android.mk
@@ -16,19 +16,10 @@
 
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
-
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE := vogarexpectlib
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_STATIC_JAVA_LIBRARIES := guavalib jsonlib
+include $(BUILD_HOST_JAVA_LIBRARY)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsVerifierTests
-
-LOCAL_INSTRUMENTATION_FOR := CtsVerifier
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
diff --git a/libs/vogar-expect/README b/libs/vogar-expect/README
new file mode 100644
index 0000000..eee6f83
--- /dev/null
+++ b/libs/vogar-expect/README
@@ -0,0 +1 @@
+Selected classes taken from http://code.google.com/p/vogar/
diff --git a/libs/vogar-expect/src/vogar/AnnotatedOutcome.java b/libs/vogar-expect/src/vogar/AnnotatedOutcome.java
new file mode 100644
index 0000000..a27ab9e
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/AnnotatedOutcome.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 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 vogar;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Ordering;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedMap;
+
+/**
+ * Contains an outcome for a test, along with some metadata pertaining to the history of this test,
+ * including a list of previous outcomes, an outcome corresponding to the tag Vogar is being run
+ * with, if applicable, and the expectation for this test, so that result value information is
+ * available.
+ */
+public final class AnnotatedOutcome {
+    public static Ordering<AnnotatedOutcome> ORDER_BY_NAME = new Ordering<AnnotatedOutcome>() {
+        @Override public int compare(AnnotatedOutcome a, AnnotatedOutcome b) {
+            return a.getName().compareTo(b.getName());
+       }
+    };
+
+    private final Expectation expectation;
+    private final Outcome outcome;
+    /** a list of previous outcomes for the same action, sorted in chronological order */
+    private final SortedMap<Long, Outcome> previousOutcomes;
+    /** will be null if not comparing to a tag */
+    private final String tagName;
+    private final Outcome tagOutcome;
+    private final boolean hasMetadata;
+
+    AnnotatedOutcome(Outcome outcome, Expectation expectation,
+            SortedMap<Long, Outcome> previousOutcomes, String tagName, Outcome tagOutcome,
+            boolean hasMetadata) {
+        if (previousOutcomes == null) {
+            throw new NullPointerException();
+        }
+        this.expectation = expectation;
+        this.outcome = outcome;
+        this.previousOutcomes = previousOutcomes;
+        this.tagName = tagName;
+        this.tagOutcome = tagOutcome;
+        this.hasMetadata = hasMetadata;
+    }
+
+    public Outcome getOutcome() {
+        return outcome;
+    }
+
+    public String getName() {
+        return outcome.getName();
+    }
+
+    public ResultValue getResultValue() {
+        return outcome.getResultValue(expectation);
+    }
+
+    public List<ResultValue> getPreviousResultValues() {
+        List<ResultValue> previousResultValues = new ArrayList<ResultValue>();
+        for (Outcome previousOutcome : previousOutcomes.values()) {
+            previousResultValues.add(previousOutcome.getResultValue(expectation));
+        }
+        return previousResultValues;
+    }
+
+    /**
+     * Returns the most recent result value of a run of this test (before the current run).
+     */
+    public ResultValue getMostRecentResultValue(ResultValue defaultValue) {
+        List<ResultValue> previousResultValues = getPreviousResultValues();
+        return previousResultValues.isEmpty() ?
+                defaultValue :
+                previousResultValues.get(previousResultValues.size() - 1);
+    }
+
+    public boolean hasTag() {
+        return tagOutcome != null;
+    }
+
+    public String getTagName() {
+        return tagName;
+    }
+
+    public ResultValue getTagResultValue() {
+        return tagOutcome == null ? null : tagOutcome.getResultValue(expectation);
+    }
+
+    /**
+     * Returns true if the outcome is noteworthy given the result value and previous history.
+     */
+    public boolean isNoteworthy() {
+        return getResultValue() != ResultValue.OK || recentlyChanged() || changedSinceTag();
+    }
+
+    public boolean outcomeChanged() {
+        List<Outcome> previousOutcomesList = getOutcomeList();
+        return previousOutcomesList.isEmpty()
+                || !outcome.equals(previousOutcomesList.get(previousOutcomesList.size() - 1));
+    }
+
+    private ArrayList<Outcome> getOutcomeList() {
+        return new ArrayList<Outcome>(previousOutcomes.values());
+    }
+
+    /**
+     * Returns true if the outcome recently changed in result value.
+     */
+    private boolean recentlyChanged() {
+        List<ResultValue> previousResultValues = getPreviousResultValues();
+        if (previousResultValues.isEmpty()) {
+            return false;
+        }
+        return previousResultValues.get(previousResultValues.size() - 1) != getResultValue();
+    }
+
+    private boolean changedSinceTag() {
+        ResultValue tagResultValue = getTagResultValue();
+        return tagResultValue != null && tagResultValue != getResultValue();
+    }
+
+    /**
+     * Returns a Long representing the time the outcome was last run. Returns {@code defaultValue}
+     * if the outcome is not known to have run before.
+     */
+    public Long lastRun(Long defaultValue) {
+        if (!hasMetadata) {
+            return defaultValue;
+        }
+        List<Long> runTimes = Lists.newArrayList(previousOutcomes.keySet());
+        return runTimes.isEmpty() ? defaultValue : runTimes.get(runTimes.size() - 1);
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/Expectation.java b/libs/vogar-expect/src/vogar/Expectation.java
new file mode 100644
index 0000000..f065f42
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/Expectation.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 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 vogar;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * The expected result of an action execution. This is typically encoded in the
+ * expectations text file, which has the following format:
+ * <pre>
+ * test java.io.StreamTokenizer.Reset
+ * result UNSUPPORTED
+ * pattern .*should get token \[, but get -1.*
+ *
+ * # should we fix this?
+ * test java.util.Arrays.CopyMethods
+ * result COMPILE_FAILED
+ * pattern .*cannot find symbol.*
+ * </pre>
+ */
+public final class Expectation {
+
+    /** The pattern to use when no expected output is specified */
+    public static final Pattern MATCH_ALL_PATTERN
+            = Pattern.compile(".*", Pattern.MULTILINE | Pattern.DOTALL);
+
+    /** The expectation of a general successful run. */
+    public static final Expectation SUCCESS = new Expectation(Result.SUCCESS, MATCH_ALL_PATTERN,
+            Collections.<String>emptySet(), "", -1);
+
+    /** Justification for this expectation */
+    private final String description;
+
+    /** The action's expected result, such as {@code EXEC_FAILED}. */
+    private final Result result;
+
+    /** The pattern the expected output will match. */
+    private final Pattern pattern;
+
+    /** Attributes of this test. */
+    private final Set<String> tags;
+
+    /** The tracking bug ID */
+    private final long bug;
+
+    /** True if the identified bug still active. */
+    private boolean bugIsOpen = false;
+
+    public Expectation(Result result, Pattern pattern, Set<String> tags, String description, long bug) {
+        if (result == null || description == null || pattern == null) {
+            throw new IllegalArgumentException(
+                    "result=" + result + " description=" + description + " pattern=" + pattern);
+        }
+
+        this.description = description;
+        this.result = result;
+        this.pattern = pattern;
+        this.tags = new LinkedHashSet<String>(tags);
+        this.bug = bug;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public long getBug() {
+        return bug;
+    }
+
+    public Result getResult() {
+        return result;
+    }
+
+    public Set<String> getTags() {
+        return tags;
+    }
+
+    /**
+     * Set the current status of this expectation's bug. When a bug is open,
+     * any result (success or failure) is permitted.
+     */
+    public void setBugIsOpen(boolean bugIsOpen) {
+        this.bugIsOpen = bugIsOpen;
+    }
+
+    /**
+     * Returns true if {@code outcome} matches this expectation.
+     */
+    public boolean matches(Outcome outcome) {
+        return patternMatches(outcome) && (bugIsOpen || result == outcome.getResult());
+    }
+
+    private boolean patternMatches(Outcome outcome) {
+        return pattern.matcher(outcome.getOutput()).matches();
+    }
+
+    @Override public String toString() {
+        return "Expectation[description=" + description + " pattern=" + pattern.pattern() + "]";
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/ExpectationStore.java b/libs/vogar-expect/src/vogar/ExpectationStore.java
new file mode 100644
index 0000000..cfa20e9
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/ExpectationStore.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2010 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 vogar;
+
+//import com.google.caliper.internal.gson.stream.JsonReader;
+
+import com.android.json.stream.JsonReader;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import vogar.commands.Command;
+import vogar.util.Log;
+
+/**
+ * A database of expected outcomes. Entries in this database come in two forms.
+ * <ul>
+ *   <li>Outcome expectations name an outcome (or its prefix, such as
+ *       "java.util"), its expected result, and an optional pattern to match
+ *       the expected output.
+ *   <li>Failure expectations include a pattern that may match the output of any
+ *       outcome. These expectations are useful for hiding failures caused by
+ *       cross-cutting features that aren't supported.
+ * </ul>
+ *
+ * <p>If an outcome matches both an outcome expectation and a failure
+ * expectation, the outcome expectation will be returned.
+ */
+public final class ExpectationStore {
+    private static final int PATTERN_FLAGS = Pattern.MULTILINE | Pattern.DOTALL;
+    private final Map<String, Expectation> outcomes = new LinkedHashMap<String, Expectation>();
+    private final Map<String, Expectation> failures = new LinkedHashMap<String, Expectation>();
+
+    private ExpectationStore() {}
+
+    /**
+     * Finds the expected result for the specified action or outcome name. This
+     * returns a value for all names, even if no explicit expectation was set.
+     */
+    public Expectation get(String name) {
+        Expectation byName = getByNameOrPackage(name);
+        return byName != null ? byName : Expectation.SUCCESS;
+    }
+
+    /**
+     * Finds the expected result for the specified outcome after it has
+     * completed. Unlike {@code get()}, this also takes into account the
+     * outcome's output.
+     *
+     * <p>For outcomes that have both a name match and an output match,
+     * exact name matches are preferred, then output matches, then inexact
+     * name matches.
+     */
+    public Expectation get(Outcome outcome) {
+        Expectation exactNameMatch = outcomes.get(outcome.getName());
+        if (exactNameMatch != null) {
+            return exactNameMatch;
+        }
+
+        for (Map.Entry<String, Expectation> entry : failures.entrySet()) {
+            if (entry.getValue().matches(outcome)) {
+                return entry.getValue();
+            }
+        }
+
+        Expectation byName = getByNameOrPackage(outcome.getName());
+        return byName != null ? byName : Expectation.SUCCESS;
+    }
+
+    private Expectation getByNameOrPackage(String name) {
+        while (true) {
+            Expectation expectation = outcomes.get(name);
+            if (expectation != null) {
+                return expectation;
+            }
+
+            int dotOrHash = Math.max(name.lastIndexOf('.'), name.lastIndexOf('#'));
+            if (dotOrHash == -1) {
+                return null;
+            }
+
+            name = name.substring(0, dotOrHash);
+        }
+    }
+
+    public static ExpectationStore parse(Set<File> expectationFiles, ModeId mode) throws IOException {
+        ExpectationStore result = new ExpectationStore();
+        for (File f : expectationFiles) {
+            if (f.exists()) {
+                result.parse(f, mode);
+            }
+        }
+        return result;
+    }
+
+    public void parse(File expectationsFile, ModeId mode) throws IOException {
+        Log.verbose("loading expectations file " + expectationsFile);
+
+        int count = 0;
+        JsonReader reader = null;
+        try {
+            reader = new JsonReader(new FileReader(expectationsFile));
+            reader.setLenient(true);
+            reader.beginArray();
+            while (reader.hasNext()) {
+                readExpectation(reader, mode);
+                count++;
+            }
+            reader.endArray();
+
+            Log.verbose("loaded " + count + " expectations from " + expectationsFile);
+        } finally {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+    }
+
+    private void readExpectation(JsonReader reader, ModeId mode) throws IOException {
+        boolean isFailure = false;
+        Result result = Result.SUCCESS;
+        Pattern pattern = Expectation.MATCH_ALL_PATTERN;
+        Set<String> names = new LinkedHashSet<String>();
+        Set<String> tags = new LinkedHashSet<String>();
+        Set<ModeId> modes = null;
+        String description = "";
+        long buganizerBug = -1;
+
+        reader.beginObject();
+        while (reader.hasNext()) {
+            String name = reader.nextName();
+            if (name.equals("result")) {
+                result = Result.valueOf(reader.nextString());
+            } else if (name.equals("name")) {
+                names.add(reader.nextString());
+            } else if (name.equals("names")) {
+                readStrings(reader, names);
+            } else if (name.equals("failure")) {
+                isFailure = true;
+                names.add(reader.nextString());
+            } else if (name.equals("pattern")) {
+                pattern = Pattern.compile(reader.nextString(), PATTERN_FLAGS);
+            } else if (name.equals("substring")) {
+                pattern = Pattern.compile(".*" + Pattern.quote(reader.nextString()) + ".*", PATTERN_FLAGS);
+            } else if (name.equals("tags")) {
+                readStrings(reader, tags);
+            } else if (name.equals("description")) {
+                Iterable<String> split = Splitter.on("\n").omitEmptyStrings().trimResults().split(reader.nextString());
+                description = Joiner.on("\n").join(split);
+            } else if (name.equals("bug")) {
+                buganizerBug = reader.nextLong();
+            } else if (name.equals("modes")) {
+                modes = readModes(reader);
+            } else {
+                Log.warn("Unhandled name in expectations file: " + name);
+                reader.skipValue();
+            }
+        }
+        reader.endObject();
+
+        if (names.isEmpty()) {
+            throw new IllegalArgumentException("Missing 'name' or 'failure' key in " + reader);
+        }
+        if (modes != null && !modes.contains(mode)) {
+            return;
+        }
+
+        Expectation expectation = new Expectation(result, pattern, tags, description, buganizerBug);
+        Map<String, Expectation> map = isFailure ? failures : outcomes;
+        for (String name : names) {
+            if (map.put(name, expectation) != null) {
+                throw new IllegalArgumentException("Duplicate expectations for " + name);
+            }
+        }
+    }
+
+    private void readStrings(JsonReader reader, Set<String> output) throws IOException {
+        reader.beginArray();
+        while (reader.hasNext()) {
+            output.add(reader.nextString());
+        }
+        reader.endArray();
+    }
+
+    private Set<ModeId> readModes(JsonReader reader) throws IOException {
+        Set<ModeId> result = new LinkedHashSet<ModeId>();
+        reader.beginArray();
+        while (reader.hasNext()) {
+            result.add(ModeId.valueOf(reader.nextString().toUpperCase()));
+        }
+        reader.endArray();
+        return result;
+    }
+
+    /**
+     * Sets the bugIsOpen status on all expectations by querying an external bug
+     * tracker.
+     */
+    public void loadBugStatuses(String openBugsCommand) {
+        Iterable<Expectation> allExpectations = Iterables.concat(outcomes.values(), failures.values());
+
+        // figure out what bug IDs we're interested in
+        Set<String> bugs = new LinkedHashSet<String>();
+        for (Expectation expectation : allExpectations) {
+            if (expectation.getBug() != -1) {
+                bugs.add(Long.toString(expectation.getBug()));
+            }
+        }
+        if (bugs.isEmpty()) {
+            return;
+        }
+
+        // query the external app for open bugs
+        List<String> openBugs = new Command.Builder()
+                .args(openBugsCommand)
+                .args(bugs)
+                .execute();
+        Set<Long> openBugsSet = new LinkedHashSet<Long>();
+        for (String bug : openBugs) {
+            openBugsSet.add(Long.parseLong(bug));
+        }
+
+        Log.verbose("tracking " + openBugsSet.size() + " open bugs: " + openBugs);
+
+        // update our expectations with that set
+        for (Expectation expectation : allExpectations) {
+            if (openBugsSet.contains(expectation.getBug())) {
+                expectation.setBugIsOpen(true);
+            }
+        }
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/ModeId.java b/libs/vogar-expect/src/vogar/ModeId.java
new file mode 100644
index 0000000..3b24cc1
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/ModeId.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2009 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 vogar;
+
+public enum ModeId {
+    DEVICE, JVM, ACTIVITY, SIM, HOST;
+
+    public boolean acceptsVmArgs() {
+        return this != ACTIVITY;
+    }
+
+    public boolean isHost() {
+        return this == JVM || this == SIM || this == HOST;
+    }
+
+    public boolean requiresAndroidSdk() {
+        return this == DEVICE || this == ACTIVITY || this == SIM || this == HOST;
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/Outcome.java b/libs/vogar-expect/src/vogar/Outcome.java
new file mode 100644
index 0000000..3d7c68f
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/Outcome.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2010 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 vogar;
+
+import com.google.common.collect.Lists;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import vogar.util.Strings;
+
+/**
+ * An outcome of an action. Some actions may have multiple outcomes. For
+ * example, JUnit tests have one outcome for each test method.
+ */
+public final class Outcome {
+
+    private final String outcomeName;
+    private final Result result;
+    private final String output;
+    private final Date date;
+
+    public Outcome(String outcomeName, Result result, List<String> outputLines) {
+        this.outcomeName = outcomeName;
+        this.result = result;
+        this.output = sanitizeOutputLines(outputLines);
+        this.date = new Date();
+    }
+
+    public Outcome(String outcomeName, Result result, String outputLine, Date date) {
+        this.outcomeName = outcomeName;
+        this.result = result;
+        this.output = sanitizeOutputLine(outputLine);
+        this.date = date;
+    }
+
+    public Outcome(String outcomeName, Result result, String outputLine) {
+        this.outcomeName = outcomeName;
+        this.result = result;
+        this.output = sanitizeOutputLine(outputLine);
+        this.date = new Date();
+    }
+
+    public Outcome(String outcomeName, Result result, Throwable throwable) {
+        this.outcomeName = outcomeName;
+        this.result = result;
+        this.output = sanitizeOutputLines(throwableToLines(throwable));
+        this.date = new Date();
+    }
+
+    private String sanitizeOutputLines(List<String> outputLines) {
+        List<String> sanitizedStrings = Lists.newArrayList();
+        for (String line : outputLines) {
+            sanitizedStrings.add(sanitizeOutputLine(line));
+        }
+        return Strings.join(sanitizedStrings, "\n");
+    }
+
+    private String sanitizeOutputLine(String outputLine) {
+        return Strings.xmlSanitize(outputLine.replaceAll("\r\n?", "\n"));
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public String getName() {
+        return outcomeName;
+    }
+
+    public Result getResult() {
+        return result;
+    }
+
+    public String getOutput() {
+        return output;
+    }
+
+    public List<String> getOutputLines() {
+        return Arrays.asList(output.split("\n"));
+    }
+
+    private static List<String> throwableToLines(Throwable t) {
+        StringWriter writer = new StringWriter();
+        PrintWriter out = new PrintWriter(writer);
+        t.printStackTrace(out);
+        return Arrays.asList(writer.toString().split("\\n"));
+    }
+
+    /**
+     * Returns the action's suite name, such as java.lang.Integer or
+     * java.lang.IntegerTest.
+     */
+    public String getSuiteName() {
+        int split = split(outcomeName);
+        return split == -1 ? "defaultpackage" : outcomeName.substring(0, split);
+    }
+
+    /**
+     * Returns the specific action name, such as BitTwiddle or testBitTwiddle.
+     */
+    public String getTestName() {
+        int split = split(outcomeName);
+        return split == -1 ? outcomeName : outcomeName.substring(split + 1);
+    }
+
+    private static int split(String name) {
+        int lastHash = name.indexOf('#');
+        return lastHash == -1 ? name.lastIndexOf('.') : lastHash;
+    }
+
+    /**
+     * Returns whether the result indicates that the contents of the Outcome are important.
+     *
+     * For example, for a test skipped because it is unsupported, we don't care about the result.
+     */
+    private boolean matters() {
+        return result != Result.UNSUPPORTED;
+    }
+
+    public ResultValue getResultValue(Expectation expectation) {
+        if (matters()) {
+            return expectation.matches(this) ? ResultValue.OK : ResultValue.FAIL;
+        }
+        return ResultValue.IGNORE;
+    }
+
+    /**
+     * Returns a filesystem db path for this outcome. For example, a path for an outcome with name
+     * "foo.bar.baz#testName" would be "foo/bar/baz/testName".
+     */
+    public String getPath() {
+        return outcomeName.replaceAll("[\\.#]", "/");
+    }
+
+    @Override public boolean equals(Object o) {
+        if (o instanceof Outcome) {
+            Outcome outcome = (Outcome) o;
+            return outcomeName.equals(outcome.outcomeName)
+                    && result == outcome.result
+                    && output.equals(outcome.output);
+        }
+        return false;
+    }
+
+    @Override public int hashCode() {
+        int hashCode = 17;
+        hashCode = 37 * hashCode + outcomeName.hashCode();
+        hashCode  = 37 * hashCode + result.hashCode();
+        hashCode = 37 * hashCode + output.hashCode();
+        return hashCode;
+    }
+
+    @Override public String toString() {
+        return "Outcome[name=" + outcomeName + " output=" + output + "]";
+    }
+
+}
diff --git a/libs/vogar-expect/src/vogar/Result.java b/libs/vogar-expect/src/vogar/Result.java
new file mode 100644
index 0000000..45c88ce
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/Result.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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 vogar;
+
+/**
+ * The result of a test or benchmark execution.
+ */
+public enum Result {
+
+    /**
+     * An action that cannot be run by this harness, such as a shell script.
+     */
+    UNSUPPORTED,
+
+    COMPILE_FAILED,
+    EXEC_FAILED,
+    EXEC_TIMEOUT,
+    ERROR,
+    SUCCESS
+}
diff --git a/libs/vogar-expect/src/vogar/ResultValue.java b/libs/vogar-expect/src/vogar/ResultValue.java
new file mode 100644
index 0000000..2e450f4
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/ResultValue.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 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 vogar;
+
+/**
+ * Represents an evaluation of the goodness of a result.
+ */
+public enum ResultValue {
+    OK,
+    IGNORE,
+    FAIL
+}
diff --git a/libs/vogar-expect/src/vogar/commands/Command.java b/libs/vogar-expect/src/vogar/commands/Command.java
new file mode 100644
index 0000000..d60d77e
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/commands/Command.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2009 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 vogar.commands;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import vogar.util.Log;
+import vogar.util.Strings;
+import vogar.util.Threads;
+
+/**
+ * An out of process executable.
+ */
+public final class Command {
+    private final List<String> args;
+    private final Map<String, String> env;
+    private final File workingDirectory;
+    private final boolean permitNonZeroExitStatus;
+    private final PrintStream tee;
+    private final boolean nativeOutput;
+    private volatile Process process;
+
+    public Command(String... args) {
+        this(Arrays.asList(args));
+    }
+
+    public Command(List<String> args) {
+        this.args = new ArrayList<String>(args);
+        this.env = Collections.emptyMap();
+        this.workingDirectory = null;
+        this.permitNonZeroExitStatus = false;
+        this.tee = null;
+        this.nativeOutput = false;
+    }
+
+    private Command(Builder builder) {
+        this.args = new ArrayList<String>(builder.args);
+        this.env = builder.env;
+        this.workingDirectory = builder.workingDirectory;
+        this.permitNonZeroExitStatus = builder.permitNonZeroExitStatus;
+        this.tee = builder.tee;
+        if (builder.maxLength != -1) {
+            String string = toString();
+            if (string.length() > builder.maxLength) {
+                throw new IllegalStateException("Maximum command length " + builder.maxLength
+                                                + " exceeded by: " + string);
+            }
+        }
+        this.nativeOutput = builder.nativeOutput;
+    }
+
+    public void start() throws IOException {
+        if (isStarted()) {
+            throw new IllegalStateException("Already started!");
+        }
+
+        Log.verbose("executing " + this);
+
+        ProcessBuilder processBuilder = new ProcessBuilder()
+                .command(args)
+                .redirectErrorStream(true);
+        if (workingDirectory != null) {
+            processBuilder.directory(workingDirectory);
+        }
+
+        processBuilder.environment().putAll(env);
+
+        process = processBuilder.start();
+    }
+
+    public boolean isStarted() {
+        return process != null;
+    }
+
+    public InputStream getInputStream() {
+        if (!isStarted()) {
+            throw new IllegalStateException("Not started!");
+        }
+
+        return process.getInputStream();
+    }
+
+    public List<String> gatherOutput()
+            throws IOException, InterruptedException {
+        if (!isStarted()) {
+            throw new IllegalStateException("Not started!");
+        }
+
+        BufferedReader in = new BufferedReader(
+                new InputStreamReader(getInputStream(), "UTF-8"));
+        List<String> outputLines = new ArrayList<String>();
+        String outputLine;
+        while ((outputLine = in.readLine()) != null) {
+            if (tee != null) {
+                tee.println(outputLine);
+            }
+            if (nativeOutput) {
+                Log.nativeOutput(outputLine);
+            }
+            outputLines.add(outputLine);
+        }
+
+        if (process.waitFor() != 0 && !permitNonZeroExitStatus) {
+            StringBuilder message = new StringBuilder();
+            for (String line : outputLines) {
+                message.append("\n").append(line);
+            }
+            throw new CommandFailedException(args, outputLines);
+        }
+
+        return outputLines;
+    }
+
+    public List<String> execute() {
+        try {
+            start();
+            return gatherOutput();
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to execute process: " + args, e);
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while executing process: " + args, e);
+        }
+    }
+
+    /**
+     * Executes a command with a specified timeout. If the process does not
+     * complete normally before the timeout has elapsed, it will be destroyed.
+     *
+     * @param timeoutSeconds how long to wait, or 0 to wait indefinitely
+     * @return the command's output, or null if the command timed out
+     */
+    public List<String> executeWithTimeout(int timeoutSeconds)
+            throws TimeoutException {
+        if (timeoutSeconds == 0) {
+            return execute();
+        }
+
+        try {
+            return executeLater().get(timeoutSeconds, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while executing process: " + args, e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        } finally {
+            destroy();
+        }
+    }
+
+    /**
+     * Executes the command on a new background thread. This method returns
+     * immediately.
+     *
+     * @return a future to retrieve the command's output.
+     */
+    public Future<List<String>> executeLater() {
+        ExecutorService executor = Threads.fixedThreadsExecutor("command", 1);
+        Future<List<String>> result = executor.submit(new Callable<List<String>>() {
+            public List<String> call() throws Exception {
+                start();
+                return gatherOutput();
+            }
+        });
+        executor.shutdown();
+        return result;
+    }
+
+    /**
+     * Destroys the underlying process and closes its associated streams.
+     */
+    public void destroy() {
+        if (process == null) {
+            return;
+        }
+
+        process.destroy();
+        try {
+            process.waitFor();
+            int exitValue = process.exitValue();
+            Log.verbose("received exit value " + exitValue
+                    + " from destroyed command " + this);
+        } catch (IllegalThreadStateException destroyUnsuccessful) {
+            Log.warn("couldn't destroy " + this);
+        } catch (InterruptedException e) {
+            Log.warn("couldn't destroy " + this);
+        }
+    }
+
+    @Override public String toString() {
+        String envString = !env.isEmpty() ? (Strings.join(env.entrySet(), " ") + " ") : "";
+        return envString + Strings.join(args, " ");
+    }
+
+    public static class Builder {
+        private final List<String> args = new ArrayList<String>();
+        private final Map<String, String> env = new LinkedHashMap<String, String>();
+        private File workingDirectory;
+        private boolean permitNonZeroExitStatus = false;
+        private PrintStream tee = null;
+        private boolean nativeOutput;
+        private int maxLength = -1;
+
+        public Builder args(Object... objects) {
+            for (Object object : objects) {
+                args(object.toString());
+            }
+            return this;
+        }
+
+        public Builder setNativeOutput(boolean nativeOutput) {
+            this.nativeOutput = nativeOutput;
+            return this;
+        }
+
+        public Builder args(String... args) {
+            return args(Arrays.asList(args));
+        }
+
+        public Builder args(Collection<String> args) {
+            this.args.addAll(args);
+            return this;
+        }
+
+        public Builder env(String key, String value) {
+            env.put(key, value);
+            return this;
+        }
+
+        /**
+         * Sets the working directory from which the command will be executed.
+         * This must be a <strong>local</strong> directory; Commands run on
+         * remote devices (ie. via {@code adb shell}) require a local working
+         * directory.
+         */
+        public Builder workingDirectory(File workingDirectory) {
+            this.workingDirectory = workingDirectory;
+            return this;
+        }
+
+        public Builder tee(PrintStream printStream) {
+            tee = printStream;
+            return this;
+        }
+
+        public Builder maxLength(int maxLength) {
+            this.maxLength = maxLength;
+            return this;
+        }
+
+        public Command build() {
+            return new Command(this);
+        }
+
+        public List<String> execute() {
+            return build().execute();
+        }
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/commands/CommandFailedException.java b/libs/vogar-expect/src/vogar/commands/CommandFailedException.java
new file mode 100644
index 0000000..3e08c11
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/commands/CommandFailedException.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 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 vogar.commands;
+
+import java.util.List;
+
+/**
+ * Thrown when an out of process executable does not return normally.
+ */
+public class CommandFailedException extends RuntimeException {
+
+    private final List<String> args;
+    private final List<String> outputLines;
+
+    public CommandFailedException(List<String> args, List<String> outputLines) {
+        super(formatMessage(args, outputLines));
+        this.args = args;
+        this.outputLines = outputLines;
+    }
+
+    public List<String> getArgs() {
+        return args;
+    }
+
+    public List<String> getOutputLines() {
+        return outputLines;
+    }
+
+    public static String formatMessage(List<String> args, List<String> outputLines) {
+        StringBuilder result = new StringBuilder();
+        result.append("Command failed:");
+        for (String arg : args) {
+            result.append(" ").append(arg);
+        }
+        for (String outputLine : outputLines) {
+            result.append("\n  ").append(outputLine);
+        }
+        return result.toString();
+    }
+
+    private static final long serialVersionUID = 0;
+}
diff --git a/libs/vogar-expect/src/vogar/commands/Mkdir.java b/libs/vogar-expect/src/vogar/commands/Mkdir.java
new file mode 100644
index 0000000..fc08f1b
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/commands/Mkdir.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 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 vogar.commands;
+
+import java.io.File;
+
+/**
+ * A mkdir command.
+ */
+public final class Mkdir {
+
+    public void mkdirs(File directory) {
+        new Command("mkdir", "-p", directory.getPath()).execute();
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/commands/Rm.java b/libs/vogar-expect/src/vogar/commands/Rm.java
new file mode 100644
index 0000000..5b39144
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/commands/Rm.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 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 vogar.commands;
+
+import java.io.File;
+
+/**
+ * A rm command.
+ */
+public final class Rm {
+
+    public void file(File file) {
+        new Command("rm", "-f", file.getPath()).execute();
+    }
+
+    public void directoryTree(File directory) {
+        new Command("rm", "-rf", directory.getPath()).execute();
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/util/IoUtils.java b/libs/vogar-expect/src/vogar/util/IoUtils.java
new file mode 100644
index 0000000..4f1fba1
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/util/IoUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 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 vogar.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.Socket;
+
+public final class IoUtils {
+
+    public static void closeQuietly(Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException ignored) {
+            }
+        }
+    }
+
+    public static void closeQuietly(Socket c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException ignored) {
+            }
+        }
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/util/Log.java b/libs/vogar-expect/src/vogar/util/Log.java
new file mode 100644
index 0000000..99c0807
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/util/Log.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 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 vogar.util;
+
+import java.util.List;
+
+public class Log {
+
+    private static LogOutput sLogoutput = null;
+
+    public static void setOutput(LogOutput logOutput) {
+        sLogoutput = logOutput;
+    }
+
+    public static void verbose(String s) {
+        if (sLogoutput != null) {
+            sLogoutput.verbose(s);
+        }
+    }
+
+    public static void warn(String message) {
+        if (sLogoutput != null) {
+            sLogoutput.warn(message);
+        }
+    }
+
+    /**
+     * Warns, and also puts a list of strings afterwards.
+     */
+    public static void warn(String message, List<String> list) {
+        if (sLogoutput != null) {
+            sLogoutput.warn(message, list);
+        }
+    }
+
+    public static void info(String s) {
+        if (sLogoutput != null) {
+            sLogoutput.info(s);
+        }
+    }
+
+    public static void info(String message, Throwable throwable) {
+        if (sLogoutput != null) {
+            sLogoutput.info(message, throwable);
+        }
+    }
+
+    public static void nativeOutput(String outputLine) {
+        if (sLogoutput != null) {
+            sLogoutput.nativeOutput(outputLine);
+        }
+
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/util/LogOutput.java b/libs/vogar-expect/src/vogar/util/LogOutput.java
new file mode 100644
index 0000000..8123a81
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/util/LogOutput.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 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 vogar.util;
+
+import java.util.List;
+
+public interface LogOutput {
+
+    void verbose(String s);
+
+    void warn(String message);
+
+    /**
+     * Warns, and also puts a list of strings afterwards.
+     */
+    void warn(String message, List<String> list);
+
+    void info(String s);
+
+    void info(String message, Throwable throwable);
+
+    void nativeOutput(String outputLine);
+
+}
diff --git a/libs/vogar-expect/src/vogar/util/MarkResetConsole.java b/libs/vogar-expect/src/vogar/util/MarkResetConsole.java
new file mode 100644
index 0000000..d88ce31
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/util/MarkResetConsole.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 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 vogar.util;
+
+import java.io.PrintStream;
+
+/**
+ * A console that can erase output back to a previously marked position.
+ */
+public final class MarkResetConsole {
+
+    private final PrintStream out;
+    private int row;
+    private final StringBuilder rowContent = new StringBuilder();
+
+    public MarkResetConsole(PrintStream out) {
+        this.out = out;
+    }
+
+    public void println(String text) {
+        print(text + "\n");
+    }
+
+    public void print(String text) {
+        for (int i = 0; i < text.length(); i++) {
+            if (text.charAt(i) == '\n') {
+                row++;
+                rowContent.delete(0, rowContent.length());
+            } else {
+                rowContent.append(text.charAt(i));
+            }
+        }
+
+        out.print(text);
+        out.flush();
+    }
+
+    public Mark mark() {
+        return new Mark();
+    }
+
+    public class Mark {
+        private final int markRow = row;
+        private final String markRowContent = rowContent.toString();
+
+        private Mark() {}
+
+        public void reset() {
+            /*
+             * ANSI escapes
+             * http://en.wikipedia.org/wiki/ANSI_escape_code
+             *
+             *  \u001b[K   clear the rest of the current line
+             *  \u001b[nA  move the cursor up n lines
+             *  \u001b[nB  move the cursor down n lines
+             *  \u001b[nC  move the cursor right n lines
+             *  \u001b[nD  move the cursor left n columns
+             */
+
+            for (int r = row; r > markRow; r--) {
+                // clear the line, up a line
+                System.out.print("\u001b[0G\u001b[K\u001b[1A");
+            }
+
+            // clear the line, reprint the line
+            out.print("\u001b[0G\u001b[K");
+            out.print(markRowContent);
+            rowContent.delete(0, rowContent.length());
+            rowContent.append(markRowContent);
+            row = markRow;
+        }
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/util/Strings.java b/libs/vogar-expect/src/vogar/util/Strings.java
new file mode 100644
index 0000000..f92edd8
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/util/Strings.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2009 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 vogar.util;
+
+//import com.google.common.collect.Lists;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Utility methods for strings.
+ */
+public class Strings {
+
+    private static final Pattern XML_INVALID_CHARS
+            = Pattern.compile("[^\\u0009\\u000A\\u000D\\u0020-\\uD7FF\\uE000-\\uFFFD]+");
+
+    public static String readStream(Reader reader) throws IOException {
+        StringBuilder result = new StringBuilder();
+        BufferedReader in = new BufferedReader(reader);
+        String line;
+        while ((line = in.readLine()) != null) {
+            result.append(line);
+            result.append('\n');
+        }
+        in.close();
+        return result.toString();
+    }
+
+    public static String readFile(File f) throws IOException {
+        return readStream(new InputStreamReader(new FileInputStream(f), "UTF-8"));
+    }
+
+    public static List<String> readFileLines(File f) throws IOException {
+        BufferedReader in =
+                new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8"));
+        List<String> list = new ArrayList<String>();
+        String line;
+        while ((line = in.readLine()) != null) {
+            list.add(line);
+        }
+        in.close();
+        return list;
+    }
+
+    public static String join(String delimiter, Object... objects) {
+        return join(Arrays.asList(objects), delimiter);
+    }
+
+    public static String join(Iterable<?> objects, String delimiter) {
+        Iterator<?> i = objects.iterator();
+        if (!i.hasNext()) {
+            return "";
+        }
+
+        StringBuilder result = new StringBuilder();
+        result.append(i.next());
+        while(i.hasNext()) {
+            result.append(delimiter).append(i.next());
+        }
+        return result.toString();
+    }
+
+    public static String[] objectsToStrings(Object[] objects) {
+        String[] result = new String[objects.length];
+        int i = 0;
+        for (Object o : objects) {
+            result[i++] = o.toString();
+        }
+        return result;
+    }
+
+    public static String[] objectsToStrings(Collection<?> objects) {
+        return objectsToStrings(objects.toArray());
+    }
+
+    /**
+     * Replaces XML-invalid characters with the corresponding U+XXXX code point escapes.
+     */
+    public static String xmlSanitize(String text) {
+        StringBuffer result = new StringBuffer();
+        Matcher matcher = XML_INVALID_CHARS.matcher(text);
+        while (matcher.find()) {
+            matcher.appendReplacement(result, "");
+            result.append(escapeCodePoint(matcher.group()));
+        }
+        matcher.appendTail(result);
+        return result.toString();
+    }
+
+    private static String escapeCodePoint(CharSequence cs) {
+        StringBuilder result = new StringBuilder();
+        for (int i = 0; i < cs.length(); ++i) {
+            result.append(String.format("U+%04X", (int) cs.charAt(i)));
+        }
+        return result.toString();
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/util/Threads.java b/libs/vogar-expect/src/vogar/util/Threads.java
new file mode 100644
index 0000000..83410d5
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/util/Threads.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 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 vogar.util;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utility methods for working with threads.
+ */
+public final class Threads {
+    private Threads() {}
+
+    public static ThreadFactory daemonThreadFactory(final String name) {
+        return new ThreadFactory() {
+            private int nextId = 0;
+            public synchronized Thread newThread(Runnable r) {
+                Thread thread = new Thread(r, name + "-" + (nextId++));
+                thread.setDaemon(true);
+                return thread;
+            }
+        };
+    }
+
+    public static ExecutorService threadPerCpuExecutor(String name) {
+        return fixedThreadsExecutor(name, Runtime.getRuntime().availableProcessors());
+    }
+
+    public static ExecutorService fixedThreadsExecutor(String name, int count) {
+        ThreadFactory threadFactory = daemonThreadFactory(name);
+
+        return new ThreadPoolExecutor(count, count, 10, TimeUnit.SECONDS,
+                new LinkedBlockingQueue<Runnable>(Integer.MAX_VALUE), threadFactory) {
+            @Override protected void afterExecute(Runnable runnable, Throwable throwable) {                if (throwable != null) {
+                    Log.info("Unexpected failure from " + runnable, throwable);
+                }
+            }
+        };
+    }
+}
diff --git a/libs/vogar-expect/src/vogar/util/TimeUtilities.java b/libs/vogar-expect/src/vogar/util/TimeUtilities.java
new file mode 100644
index 0000000..c5a7e3b
--- /dev/null
+++ b/libs/vogar-expect/src/vogar/util/TimeUtilities.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 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 vogar.util;
+
+/**
+ * Utilities to make it easier to work with ISO 8601 dates and times.
+ * This is a subset of the original class from http://software.jessies.org/salma-hayek/ --- please submit fixes upstream.
+ */
+public class TimeUtilities {
+    /**
+     * Returns the ISO 8601-format String corresponding to the given duration (measured in milliseconds).
+     */
+    public static String msToIsoString(long duration) {
+        long milliseconds = duration % 1000;
+        duration /= 1000;
+        long seconds = duration % 60;
+        duration /= 60;
+        long minutes = duration % 60;
+        duration /= 60;
+        long hours = duration;
+
+        StringBuilder result = new StringBuilder("P");
+        if (hours != 0) {
+            result.append(hours);
+            result.append('H');
+        }
+        if (result.length() > 1 || minutes != 0) {
+            result.append(minutes);
+            result.append('M');
+        }
+        result.append(seconds);
+        if (milliseconds != 0) {
+            result.append('.');
+            result.append(milliseconds);
+        }
+        result.append('S');
+        return result.toString();
+    }
+    
+    /**
+     * Returns a string representation of the given number of milliseconds.
+     */
+    public static String msToString(long ms) {
+        return nsToString(ms * 1000000);
+    }
+    
+    /**
+     * Returns a string representation of the given number of nanoseconds.
+     */
+    public static String nsToString(long ns) {
+        if (ns < 1000L) {
+            return Long.toString(ns) + "ns";
+        } else if (ns < 1000000L) {
+            return Long.toString(ns/1000L) + "us";
+        } else if (ns < 1000000000L) {
+            return Long.toString(ns/1000000L) + "ms";
+        } else if (ns < 60000000000L) {
+            return String.format("%.2fs", nsToS(ns));
+        } else {
+            long duration = ns;
+            long nanoseconds = duration % 1000;
+            duration /= 1000;
+            long microseconds = duration % 1000;
+            duration /= 1000;
+            long milliseconds = duration % 1000;
+            duration /= 1000;
+            long seconds = duration % 60;
+            duration /= 60;
+            long minutes = duration % 60;
+            duration /= 60;
+            long hours = duration % 24;
+            duration /= 24;
+            long days = duration;
+            
+            StringBuilder result = new StringBuilder();
+            if (days != 0) {
+                result.append(days);
+                result.append('d');
+            }
+            if (result.length() > 1 || hours != 0) {
+                result.append(hours);
+                result.append('h');
+            }
+            if (result.length() > 1 || minutes != 0) {
+                result.append(minutes);
+                result.append('m');
+            }
+            result.append(seconds);
+            result.append('s');
+            return result.toString();
+        }
+    }
+    
+    /**
+     * Converts nanoseconds into (fractional) seconds.
+     */
+    public static double nsToS(long ns) {
+        return ((double) ns)/1000000000.0;
+    }
+
+    private TimeUtilities() {
+    }
+}
diff --git a/tests/Android.mk b/tests/Android.mk
index bdd8b8a..838d01d 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -23,7 +23,6 @@
               src/android/os/cts/IEmptyService.aidl
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := CtsTestAnnotationsLib
 
 LOCAL_JNI_SHARED_LIBRARIES := libcts_jni
 
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 43eb5fc..54b7a4f 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -65,6 +65,7 @@
     <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.DUMP" />
+    <uses-permission android:name="android.permission.NFC" />
 
     <!-- Used for PackageManager test, don't delete this INTERNET permission -->
     <uses-permission android:name="android.permission.INTERNET" />
@@ -79,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" />
@@ -867,8 +869,6 @@
             </intent-filter>
         </service>
 
-        <activity android:name="android.app.cts.ListActivityTestHelper"
-            android:label="ListActivityTestHelper" />
         <activity android:name="android.app.ActivityGroup"
             android:label="ActivityGroup" />
 
@@ -932,6 +932,8 @@
 
         <activity android:name="android.opengl.cts.OpenGlEsVersionStubActivity"/>
 
+        <activity android:name="android.opengl.cts.EglConfigStubActivity"/>
+
         <activity android:name="android.preference.cts.PreferenceStubActivity">
             <meta-data android:name="android.preference" 
                     android:resource="@xml/preferences_from_intent" />
diff --git a/tests/ProcessTest/Android.mk b/tests/ProcessTest/Android.mk
index be1e7de..ba58e87 100644
--- a/tests/ProcessTest/Android.mk
+++ b/tests/ProcessTest/Android.mk
@@ -20,7 +20,7 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := framework-tests android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner
 
 LOCAL_AAPT_FLAGS = -c xx_YY -c cs
 
diff --git a/tests/appsecurity-tests/Android.mk b/tests/appsecurity-tests/Android.mk
index a0f7f41..4ec9922 100644
--- a/tests/appsecurity-tests/Android.mk
+++ b/tests/appsecurity-tests/Android.mk
@@ -23,7 +23,7 @@
 
 LOCAL_MODULE := CtsAppSecurityTests
 
-LOCAL_JAVA_LIBRARIES := hosttestlib ddmlib junit
+LOCAL_JAVA_LIBRARIES := hosttestlib ddmlib-prebuilt junit
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 
diff --git a/tests/appsecurity-tests/src/com/android/cts/appsecurity/AppSecurityTests.java b/tests/appsecurity-tests/src/com/android/cts/appsecurity/AppSecurityTests.java
index e34e3e8..f162edc 100644
--- a/tests/appsecurity-tests/src/com/android/cts/appsecurity/AppSecurityTests.java
+++ b/tests/appsecurity-tests/src/com/android/cts/appsecurity/AppSecurityTests.java
@@ -18,11 +18,16 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Map;
 
 import junit.framework.Test;
 
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.InstallException;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.TimeoutException;
 import com.android.ddmlib.testrunner.ITestRunListener;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestIdentifier;
@@ -85,7 +90,7 @@
      * Test that an app that declares the same shared uid as an existing app, cannot be installed
      * if it is signed with a different certificate.
      */
-    public void testSharedUidDifferentCerts() throws IOException {
+    public void testSharedUidDifferentCerts() throws InstallException {
         Log.i(LOG_TAG, "installing apks with shared uid, but different certs");
         try {
             // cleanup test apps that might be installed from previous partial test run
@@ -111,7 +116,7 @@
      * Test that an app update cannot be installed over an existing app if it has a different
      * certificate.
      */
-    public void testAppUpgradeDifferentCerts() throws IOException {
+    public void testAppUpgradeDifferentCerts() throws InstallException {
         Log.i(LOG_TAG, "installing app upgrade with different certs");
         try {
             // cleanup test app that might be installed from previous partial test run
@@ -134,7 +139,8 @@
     /**
      * Test that an app cannot access another app's private data.
      */
-    public void testAppFailAccessPrivateData() throws IOException {
+    public void testAppFailAccessPrivateData() throws InstallException, TimeoutException,
+            AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException {
         Log.i(LOG_TAG, "installing app that attempts to access another app's private data");
         try {
             // cleanup test app that might be installed from previous partial test run
@@ -163,7 +169,7 @@
     /**
      * Test that uninstall of an app removes its private data.
      */
-    public void testUninstallRemovesData() throws IOException {
+    public void testUninstallRemovesData() throws Exception {
         Log.i(LOG_TAG, "Uninstalling app, verifying data is removed.");
         try {
             // cleanup test app that might be installed from previous partial test run
@@ -194,7 +200,8 @@
     /**
      * Test that an app cannot instrument another app that is signed with different certificate.
      */
-    public void testInstrumentationDiffCert() throws IOException {
+    public void testInstrumentationDiffCert() throws InstallException, TimeoutException,
+            AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException {
         Log.i(LOG_TAG, "installing app that attempts to instrument another app");
         try {
             // cleanup test app that might be installed from previous partial test run
@@ -225,7 +232,8 @@
      * Test that an app cannot use a signature-enforced permission if it is signed with a different
      * certificate than the app that declared the permission.
      */
-    public void testPermissionDiffCert() throws IOException {
+    public void testPermissionDiffCert() throws InstallException, TimeoutException,
+            AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException {
         Log.i(LOG_TAG, "installing app that attempts to use permission of another app");
         try {
             // cleanup test app that might be installed from previous partial test run
@@ -264,8 +272,14 @@
      *
      * @param pkgName Android application package for tests
      * @return <code>true</code> if all tests passed.
+     * @throws TimeoutException in case of a timeout on the connection.
+     * @throws AdbCommandRejectedException if adb rejects the command
+     * @throws ShellCommandUnresponsiveException if the device did not output any test result for
+     * a period longer than the max time to output.
+     * @throws IOException if connection to device was lost.
      */
-    private boolean runDeviceTests(String pkgName) {
+    private boolean runDeviceTests(String pkgName) throws AdbCommandRejectedException,
+            ShellCommandUnresponsiveException, IOException, TimeoutException {
     	return runDeviceTests(pkgName, null, null);
     }
 
@@ -275,7 +289,9 @@
      * @param pkgName Android application package for tests
      * @return <code>true</code> if all tests passed.
      */
-    private boolean runDeviceTests(String pkgName, String testClassName, String testMethodName) {
+    private boolean runDeviceTests(String pkgName, String testClassName, String testMethodName)
+            throws AdbCommandRejectedException, IOException, ShellCommandUnresponsiveException,
+                   TimeoutException {
         CollectingTestRunListener listener = doRunTests(pkgName, testClassName, testMethodName);
         return listener.didAllTestsPass();
     }
@@ -284,9 +300,15 @@
      * Helper method to run tests and return the listener that collected the results.
      * @param pkgName Android application package for tests
      * @return the {@link CollectingTestRunListener}
+     * @throws TimeoutException in case of a timeout on the connection.
+     * @throws AdbCommandRejectedException if adb rejects the command
+     * @throws ShellCommandUnresponsiveException if the device did not output any test result for
+     * a period longer than the max time to output.
+     * @throws IOException if connection to device was lost.
      */
     private CollectingTestRunListener doRunTests(String pkgName, String testClassName,
-            String testMethodName) {
+            String testMethodName) throws AdbCommandRejectedException, IOException,
+                    ShellCommandUnresponsiveException, TimeoutException {
         RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(pkgName, getDevice());
         if (testClassName != null && testMethodName != null) {
             testRunner.setMethodName(testClassName, testMethodName);
@@ -301,7 +323,7 @@
         private boolean mAllTestsPassed = true;
         private String mTestRunErrorMessage = null;
 
-        public void testEnded(TestIdentifier test) {
+        public void testEnded(TestIdentifier test,  Map<String, String> metrics) {
             // ignore
         }
 
@@ -313,7 +335,7 @@
             mAllTestsPassed = false;
         }
 
-        public void testRunEnded(long elapsedTime) {
+        public void testRunEnded(long elapsedTime, Map<String, String> resultBundle) {
             // ignore
         }
 
@@ -324,7 +346,7 @@
             mTestRunErrorMessage = errorMessage;
         }
 
-        public void testRunStarted(int testCount) {
+        public void testRunStarted(String runName, int testCount) {
             // ignore
         }
 
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml
index 53ab2cf..683ec9e 100644
--- a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml
@@ -20,16 +20,66 @@
     An app that declares a permission that requires a matching signature to
     access.
     -->
-
     <permission android:name="com.android.cts.permissionWithSignature"
         android:protectionLevel="signature" />
+    <uses-permission android:name="com.android.cts.permissionWithSignature" />
+
+    <!-- A permission this app will not hold. -->
+    <permission android:name="com.android.cts.permissionNotUsedWithSignature"
+        android:protectionLevel="signature" />
 
     <application>
+        <receiver android:name="GrantUriPermission" android:exported="true">
+        </receiver>
+
         <!-- Need a way for another app to try to access the permission. So create a content
         provider which is enforced by the permission -->
         <provider android:name="PermissionContentProvider"
                 android:authorities="ctspermissionwithsignature"
                 android:readPermission="com.android.cts.permissionWithSignature"
-                android:writePermission="com.android.cts.permissionWithSignature" />
+                android:writePermission="com.android.cts.permissionWithSignature">
+        </provider>
+
+        <!-- Need a way for another app to try to access the permission, but will
+             grant uri access. -->
+        <provider android:name="PermissionContentProviderGranting"
+                android:authorities="ctspermissionwithsignaturegranting"
+                android:readPermission="com.android.cts.permissionWithSignature"
+                android:writePermission="com.android.cts.permissionWithSignature">
+            <grant-uri-permission android:pathPattern="/foo.*" />
+            <grant-uri-permission android:pathPattern="/yes.*" />
+        </provider>
+
+        <!-- Nobody else should get access to this -->
+        <provider android:name="PrivateContentProvider"
+                android:authorities="ctsprivateprovider"
+                android:exported="false">
+        </provider>
+
+        <!-- Nobody else should get access to this, but we will grant uri access -->
+        <provider android:name="PrivateContentProviderGranting"
+                android:authorities="ctsprivateprovidergranting"
+                android:exported="false">
+            <grant-uri-permission android:pathPattern="/foo.*" />
+            <grant-uri-permission android:pathPattern="/yes.*" />
+        </provider>
+
+        <!-- Target for tests about how path permissions interact with granting
+             URI permissions. -->
+        <provider android:name="PermissionContentProviderPath"
+                android:authorities="ctspermissionwithsignaturepath"
+                android:readPermission="com.android.cts.permissionNotUsedWithSignature"
+                android:writePermission="com.android.cts.permissionNotUsedWithSignature">
+            <path-permission
+                    android:pathPrefix="/foo"
+                    android:readPermission="com.android.cts.permissionWithSignature"
+                    android:writePermission="com.android.cts.permissionWithSignature" />
+            <path-permission
+                    android:pathPrefix="/yes"
+                    android:readPermission="com.android.cts.permissionWithSignature"
+                    android:writePermission="com.android.cts.permissionWithSignature" />
+            <grant-uri-permission android:pathPattern=".*" />
+        </provider>
+        
     </application>
 </manifest>
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
new file mode 100644
index 0000000..8c14575
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
@@ -0,0 +1,30 @@
+package com.android.cts.permissiondeclareapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class GrantUriPermission extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Intent newIntent = (Intent)intent.getParcelableExtra("intent");
+        boolean service = intent.getBooleanExtra("service", false);
+        try {
+            if (!service) {
+                newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                context.startActivity(newIntent);
+            } else {
+                context.startService(newIntent);
+            }
+            if (isOrderedBroadcast()) {
+                setResultCode(101);
+            }
+        } catch (SecurityException e) {
+            Log.i("GrantUriPermission", "Security exception", e);
+            if (isOrderedBroadcast()) {
+                setResultCode(100);
+            }
+        }
+    }
+}
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProvider.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProvider.java
index e68bb1b..900664f 100644
--- a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProvider.java
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProvider.java
@@ -34,7 +34,7 @@
 
     @Override
     public String getType(Uri uri) {
-        return null;
+        return "got/theMIME";
     }
 
     @Override
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderGranting.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderGranting.java
new file mode 100644
index 0000000..97bd827
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderGranting.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 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.permissiondeclareapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Empty content provider, all permissions are enforced in manifest
+ */
+public class PermissionContentProviderGranting extends ContentProvider {
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        // do nothing
+        return 0;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "got/theMIME";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public boolean onCreate() {
+        return false;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        return 0;
+    }
+}
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderPath.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderPath.java
new file mode 100644
index 0000000..4f301a3
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderPath.java
@@ -0,0 +1,45 @@
+package com.android.cts.permissiondeclareapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Empty content provider, all permissions are enforced in manifest
+ */
+public class PermissionContentProviderPath extends ContentProvider {
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        // do nothing
+        return 0;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "got/theMIME";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public boolean onCreate() {
+        return false;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        return 0;
+    }
+}
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProvider.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProvider.java
new file mode 100644
index 0000000..64ec6e7
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProvider.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 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.permissiondeclareapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Empty content provider, all permissions are enforced in manifest
+ */
+public class PrivateContentProvider extends ContentProvider {
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        // do nothing
+        return 0;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "got/theMIME";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public boolean onCreate() {
+        return false;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        return 0;
+    }
+}
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProviderGranting.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProviderGranting.java
new file mode 100644
index 0000000..f9ae96b
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProviderGranting.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 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.permissiondeclareapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Empty content provider, all permissions are enforced in manifest
+ */
+public class PrivateContentProviderGranting extends ContentProvider {
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        // do nothing
+        return 0;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "got/theMIME";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public boolean onCreate() {
+        return false;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        return 0;
+    }
+}
diff --git a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/AndroidManifest.xml b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/AndroidManifest.xml
index b915ebc..e01bc90 100644
--- a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/AndroidManifest.xml
+++ b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/AndroidManifest.xml
@@ -17,14 +17,21 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android.cts.usespermissiondiffcertapp">
 
-    <!--
-    This app declares it uses a permission that requires same signature as app that declares it.
-    -->
-
+    <!-- We say we want to use the other app's permission, but it is signed with
+         a different cert so it should fail. -->
     <uses-permission android:name="com.android.cts.permissionWithSignature"/>
 
+    <!-- This is a permission we can have, which the other app can require for
+         access. -->
+    <permission android:name="com.android.cts.permissionAllowedWithSignature"
+        android:protectionLevel="signature" />
+    <uses-permission android:name="com.android.cts.permissionAllowedWithSignature"/>
+
     <application>
         <uses-library android:name="android.test.runner"/>
+        <activity android:name=".ReceiveUriActivity" android:exported="true"
+                android:launchMode="singleTop" />
+        <service android:name=".ReceiveUriService" android:exported="true" />
     </application>
 
     <instrumentation android:targetPackage="com.android.cts.usespermissiondiffcertapp"
diff --git a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
index 510d5e4..1723077 100644
--- a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+++ b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
@@ -16,21 +16,805 @@
 
 package com.android.cts.usespermissiondiffcertapp;
 
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
 import android.net.Uri;
+import android.os.SystemClock;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
 /**
  * Tests that signature-enforced permissions cannot be accessed by apps signed
  * with different certs than app that declares the permission.
+ * 
+ * Accesses app cts/tests/appsecurity-tests/test-apps/PermissionDeclareApp/...
  */
 public class AccessPermissionWithDiffSigTest extends AndroidTestCase {
+    static final ComponentName GRANT_URI_PERM_COMP
+            = new ComponentName("com.android.cts.permissiondeclareapp",
+                    "com.android.cts.permissiondeclareapp.GrantUriPermission");
+    static final Uri PERM_URI = Uri.parse("content://ctspermissionwithsignature");
+    static final Uri PERM_URI_GRANTING = Uri.parse("content://ctspermissionwithsignaturegranting");
+    static final Uri PERM_URI_PATH = Uri.parse("content://ctspermissionwithsignaturepath");
+    static final Uri PRIV_URI = Uri.parse("content://ctsprivateprovider");
+    static final Uri PRIV_URI_GRANTING = Uri.parse("content://ctsprivateprovidergranting");
+
+    static final String EXPECTED_MIME_TYPE = "got/theMIME";
+    
+    public void assertReadingContentUriNotAllowed(Uri uri, String msg) {
+        try {
+            getContext().getContentResolver().query(uri, null, null, null, null);
+            fail("expected SecurityException reading " + uri + ": " + msg);
+        } catch (SecurityException expected) {
+            assertNotNull("security exception's error message.", expected.getMessage());
+        }
+    }
+
+    public void assertWritingContentUriNotAllowed(Uri uri, String msg) {
+        try {
+            getContext().getContentResolver().insert(uri, new ContentValues());
+            fail("expected SecurityException writing " + uri + ": " + msg);
+        } catch (SecurityException expected) {
+            assertNotNull("security exception's error message.", expected.getMessage());
+        }
+    }
 
     /**
      * Test that the ctspermissionwithsignature content provider cannot be read,
      * since this app lacks the required certs
      */
     public void testReadProviderWithDiff() {
-        assertReadingContentUriRequiresPermission(Uri.parse("content://ctspermissionwithsignature"),
+        assertReadingContentUriRequiresPermission(PERM_URI,
                 "com.android.cts.permissionWithSignature");
     }
+
+    /**
+     * Test that the ctspermissionwithsignature content provider cannot be written,
+     * since this app lacks the required certs
+     */
+    public void testWriteProviderWithDiff() {
+        assertWritingContentUriRequiresPermission(PERM_URI,
+                "com.android.cts.permissionWithSignature");
+    }
+
+    /**
+     * Test that the ctsprivateprovider content provider cannot be read,
+     * since it is not exported from its app.
+     */
+    public void testReadProviderWhenPrivate() {
+        assertReadingContentUriNotAllowed(PRIV_URI,
+                "shouldn't read private provider");
+    }
+
+    /**
+     * Test that the ctsprivateprovider content provider cannot be written,
+     * since it is not exported from its app.
+     */
+    public void testWriteProviderWhenPrivate() {
+        assertWritingContentUriNotAllowed(PRIV_URI,
+                "shouldn't write private provider");
+    }
+
+    public void doTryGrantUriActivityPermissionToSelf(Uri uri, int mode) {
+        Intent grantIntent = new Intent();
+        grantIntent.setData(uri);
+        grantIntent.addFlags(mode | Intent.FLAG_ACTIVITY_NEW_TASK);
+        grantIntent.setClass(getContext(), ReceiveUriActivity.class);
+        try {
+            ReceiveUriActivity.clearStarted();
+            getContext().startActivity(grantIntent);
+            ReceiveUriActivity.waitForStart();
+            fail("expected SecurityException granting " + uri + " to activity");
+        } catch (SecurityException e) {
+            // This is what we want.
+        }
+    }
+
+    /**
+     * Test that we can't grant a permission to ourself.
+     */
+    public void testGrantReadUriActivityPermissionToSelf() {
+        doTryGrantUriActivityPermissionToSelf(
+                Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+    }
+
+    /**
+     * Test that we can't grant a permission to ourself.
+     */
+    public void testGrantWriteUriActivityPermissionToSelf() {
+        doTryGrantUriActivityPermissionToSelf(
+                Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    /**
+     * Test that we can't grant a permission to ourself.
+     */
+    public void testGrantReadUriActivityPrivateToSelf() {
+        doTryGrantUriActivityPermissionToSelf(
+                Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+    }
+
+    /**
+     * Test that we can't grant a permission to ourself.
+     */
+    public void testGrantWriteUriActivityPrivateToSelf() {
+        doTryGrantUriActivityPermissionToSelf(
+                Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    public void doTryGrantUriServicePermissionToSelf(Uri uri, int mode) {
+        Intent grantIntent = new Intent();
+        grantIntent.setData(uri);
+        grantIntent.addFlags(mode);
+        grantIntent.setClass(getContext(), ReceiveUriService.class);
+        try {
+            getContext().startService(grantIntent);
+            fail("expected SecurityException granting " + uri + " to service");
+        } catch (SecurityException e) {
+            // This is what we want.
+        }
+    }
+
+    /**
+     * Test that we can't grant a permission to ourself.
+     */
+    public void testGrantReadUriServicePermissionToSelf() {
+        doTryGrantUriServicePermissionToSelf(
+                Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+    }
+
+    /**
+     * Test that we can't grant a permission to ourself.
+     */
+    public void testGrantWriteUriServicePermissionToSelf() {
+        doTryGrantUriServicePermissionToSelf(
+                Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    /**
+     * Test that we can't grant a permission to ourself.
+     */
+    public void testGrantReadUriServicePrivateToSelf() {
+        doTryGrantUriServicePermissionToSelf(
+                Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+    }
+
+    /**
+     * Test that we can't grant a permission to ourself.
+     */
+    public void testGrantWriteUriServicePrivateToSelf() {
+        doTryGrantUriServicePermissionToSelf(
+                Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    static class GrantResultReceiver extends BroadcastReceiver {
+        boolean mHaveResult = false;
+        boolean mGoodResult = false;
+        boolean mSucceeded = false;
+        
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (this) {
+                mHaveResult = true;
+                switch (getResultCode()) {
+                    case 100:
+                        mGoodResult = true;
+                        mSucceeded = false;
+                        break;
+                    case 101:
+                        mGoodResult = true;
+                        mSucceeded = true;
+                        break;
+                    default:
+                        mGoodResult = false;
+                        break;
+                }
+                notifyAll();
+            }
+        }
+        
+        void assertSuccess(String failureMessage) {
+            synchronized (this) {
+                final long startTime = SystemClock.uptimeMillis();
+                while (!mHaveResult) {
+                    try {
+                        wait(5000);
+                    } catch (InterruptedException e) {
+                    }
+                    if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+                        throw new RuntimeException("Timeout");
+                    }
+                }
+                if (!mGoodResult) {
+                    fail("Broadcast receiver did not return good result");
+                }
+                if (!mSucceeded) {
+                    fail(failureMessage);
+                }
+            }
+        }
+        
+        void assertFailure(String failureMessage) {
+            synchronized (this) {
+                final long startTime = SystemClock.uptimeMillis();
+                while (!mHaveResult) {
+                    try {
+                        wait(5000);
+                    } catch (InterruptedException e) {
+                    }
+                    if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+                        throw new RuntimeException("Timeout");
+                    }
+                }
+                if (!mGoodResult) {
+                    fail("Broadcast receiver did not return good result");
+                }
+                if (mSucceeded) {
+                    fail(failureMessage);
+                }
+            }
+        }
+    }
+    
+    void grantUriPermissionFail(Uri uri, int mode, boolean service) {
+        Intent grantIntent = new Intent();
+        grantIntent.setData(uri);
+        grantIntent.addFlags(mode);
+        grantIntent.setClass(getContext(),
+                service ? ReceiveUriService.class : ReceiveUriActivity.class);
+        Intent intent = new Intent();
+        intent.setComponent(GRANT_URI_PERM_COMP);
+        intent.putExtra("intent", grantIntent);
+        intent.putExtra("service", service);
+        GrantResultReceiver receiver = new GrantResultReceiver();
+        getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null);
+        receiver.assertFailure("Able to grant URI permission to " + uri + " when should not");
+    }
+
+    void doTestGrantUriPermissionFail(Uri uri) {
+        grantUriPermissionFail(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
+        grantUriPermissionFail(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+        grantUriPermissionFail(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
+        grantUriPermissionFail(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
+    }
+    
+    /**
+     * Test that the ctspermissionwithsignature content provider can not grant
+     * URI permissions to others.
+     */
+    public void testGrantPermissionNonGrantingFail() {
+        doTestGrantUriPermissionFail(PERM_URI);
+    }
+
+    /**
+     * Test that the ctspermissionwithsignaturegranting content provider can not grant
+     * URI permissions to paths outside of the grant tree
+     */
+    public void testGrantPermissionOutsideGrantingFail() {
+        doTestGrantUriPermissionFail(PERM_URI_GRANTING);
+        doTestGrantUriPermissionFail(Uri.withAppendedPath(PERM_URI_GRANTING, "invalid"));
+    }
+
+    /**
+     * Test that the ctsprivateprovider content provider can not grant
+     * URI permissions to others.
+     */
+    public void testGrantPrivateNonGrantingFail() {
+        doTestGrantUriPermissionFail(PRIV_URI);
+    }
+
+    /**
+     * Test that the ctsprivateprovidergranting content provider can not grant
+     * URI permissions to paths outside of the grant tree
+     */
+    public void testGrantPrivateOutsideGrantingFail() {
+        doTestGrantUriPermissionFail(PRIV_URI_GRANTING);
+        doTestGrantUriPermissionFail(Uri.withAppendedPath(PRIV_URI_GRANTING, "invalid"));
+    }
+
+    void grantUriPermission(Uri uri, int mode, boolean service) {
+        Intent grantIntent = new Intent();
+        grantIntent.setData(uri);
+        grantIntent.addFlags(mode);
+        grantIntent.setClass(getContext(),
+                service ? ReceiveUriService.class : ReceiveUriActivity.class);
+        Intent intent = new Intent();
+        intent.setComponent(GRANT_URI_PERM_COMP);
+        intent.putExtra("intent", grantIntent);
+        intent.putExtra("service", service);
+        getContext().sendBroadcast(intent);
+    }
+
+    void doTestGrantActivityUriReadPermission(Uri uri) {
+        final Uri subUri = Uri.withAppendedPath(uri, "foo");
+        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+        final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
+        final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
+
+        // Precondition: no current access.
+        assertReadingContentUriNotAllowed(subUri, "shouldn't read when starting test");
+        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read when starting test");
+
+        // --------------------------------
+
+        ReceiveUriActivity.clearStarted();
+        grantUriPermission(subUri, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // See if we now have access to the provider.
+        getContext().getContentResolver().query(subUri, null, null, null, null);
+
+        // But not writing.
+        assertWritingContentUriNotAllowed(subUri, "shouldn't write from granted read");
+
+        // And not to the base path.
+        assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
+
+        // And not to a sub path.
+        assertReadingContentUriNotAllowed(subSubUri, "shouldn't read non-granted sub URI");
+
+        // --------------------------------
+
+        ReceiveUriActivity.clearNewIntent();
+        grantUriPermission(sub2Uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForNewIntent();
+
+        if (false) {
+            synchronized (this) {
+                Log.i("**", "******************************* WAITING!!!");
+                try {
+                    wait(10000);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+
+        // See if we now have access to the provider.
+        getContext().getContentResolver().query(sub2Uri, null, null, null, null);
+
+        // And still have access to the original URI.
+        getContext().getContentResolver().query(subUri, null, null, null, null);
+
+        // But not writing.
+        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write from granted read");
+
+        // And not to the base path.
+        assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
+
+        // And not to a sub path.
+        assertReadingContentUriNotAllowed(sub2SubUri, "shouldn't read non-granted sub URI");
+
+        // And make sure we can't generate a permission to a running activity.
+        doTryGrantUriActivityPermissionToSelf(
+                Uri.withAppendedPath(uri, "hah"),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        doTryGrantUriActivityPermissionToSelf(
+                Uri.withAppendedPath(uri, "hah"),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        // --------------------------------
+
+        // Dispose of activity.
+        ReceiveUriActivity.finishCurInstanceSync();
+
+        synchronized (this) {
+            Log.i("**", "******************************* WAITING!!!");
+            try {
+                wait(100);
+            } catch (InterruptedException e) {
+            }
+        }
+
+        // Ensure reading no longer allowed.
+        assertReadingContentUriNotAllowed(subUri, "shouldn't read after losing granted URI");
+        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read after losing granted URI");
+    }
+
+    void doTestGrantActivityUriWritePermission(Uri uri) {
+        final Uri subUri = Uri.withAppendedPath(uri, "foo");
+        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+        final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
+        final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
+
+        // Precondition: no current access.
+        assertWritingContentUriNotAllowed(subUri, "shouldn't write when starting test");
+        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write when starting test");
+
+        // --------------------------------
+
+        ReceiveUriActivity.clearStarted();
+        grantUriPermission(subUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // See if we now have access to the provider.
+        getContext().getContentResolver().insert(subUri, new ContentValues());
+
+        // But not reading.
+        assertReadingContentUriNotAllowed(subUri, "shouldn't read from granted write");
+
+        // And not to the base path.
+        assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
+
+        // And not a sub-path.
+        assertWritingContentUriNotAllowed(subSubUri, "shouldn't write non-granted sub URI");
+
+        // --------------------------------
+
+        ReceiveUriActivity.clearNewIntent();
+        grantUriPermission(sub2Uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForNewIntent();
+
+        if (false) {
+            synchronized (this) {
+                Log.i("**", "******************************* WAITING!!!");
+                try {
+                    wait(10000);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+
+        // See if we now have access to the provider.
+        getContext().getContentResolver().insert(sub2Uri, new ContentValues());
+
+        // And still have access to the original URI.
+        getContext().getContentResolver().insert(subUri, new ContentValues());
+
+        // But not reading.
+        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read from granted write");
+
+        // And not to the base path.
+        assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
+
+        // And not a sub-path.
+        assertWritingContentUriNotAllowed(sub2SubUri, "shouldn't write non-granted sub URI");
+
+        // And make sure we can't generate a permission to a running activity.
+        doTryGrantUriActivityPermissionToSelf(
+                Uri.withAppendedPath(uri, "hah"),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        doTryGrantUriActivityPermissionToSelf(
+                Uri.withAppendedPath(uri, "hah"),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        // --------------------------------
+
+        // Dispose of activity.
+        ReceiveUriActivity.finishCurInstanceSync();
+
+        synchronized (this) {
+            Log.i("**", "******************************* WAITING!!!");
+            try {
+                wait(100);
+            } catch (InterruptedException e) {
+            }
+        }
+
+        // Ensure writing no longer allowed.
+        assertWritingContentUriNotAllowed(subUri, "shouldn't write after losing granted URI");
+        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write after losing granted URI");
+    }
+
+    /**
+     * Test that the ctspermissionwithsignaturegranting content provider can grant a read
+     * permission.
+     */
+    public void testGrantReadPermissionFromStartActivity() {
+        doTestGrantActivityUriReadPermission(PERM_URI_GRANTING);
+    }
+
+    /**
+     * Test that the ctspermissionwithsignaturegranting content provider can grant a write
+     * permission.
+     */
+    public void testGrantWritePermissionFromStartActivity() {
+        doTestGrantActivityUriWritePermission(PERM_URI_GRANTING);
+    }
+
+    /**
+     * Test that the ctsprivateprovidergranting content provider can grant a read
+     * permission.
+     */
+    public void testGrantReadPrivateFromStartActivity() {
+        doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING);
+    }
+
+    /**
+     * Test that the ctsprivateprovidergranting content provider can grant a write
+     * permission.
+     */
+    public void testGrantWritePrivateFromStartActivity() {
+        doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING);
+    }
+
+    void doTestGrantServiceUriReadPermission(Uri uri) {
+        final Uri subUri = Uri.withAppendedPath(uri, "foo");
+        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+        final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
+        final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
+
+        ReceiveUriService.stop(getContext());
+
+        // Precondition: no current access.
+        assertReadingContentUriNotAllowed(subUri, "shouldn't read when starting test");
+        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read when starting test");
+
+        // --------------------------------
+
+        ReceiveUriService.clearStarted();
+        grantUriPermission(subUri, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
+        ReceiveUriService.waitForStart();
+
+        int firstStartId = ReceiveUriService.getCurStartId();
+
+        // See if we now have access to the provider.
+        getContext().getContentResolver().query(subUri, null, null, null, null);
+
+        // But not writing.
+        assertWritingContentUriNotAllowed(subUri, "shouldn't write from granted read");
+
+        // And not to the base path.
+        assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
+
+        // And not to a sub path.
+        assertReadingContentUriNotAllowed(subSubUri, "shouldn't read non-granted sub URI");
+
+        // --------------------------------
+
+        // Send another Intent to it.
+        ReceiveUriService.clearStarted();
+        grantUriPermission(sub2Uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
+        ReceiveUriService.waitForStart();
+
+        if (false) {
+            synchronized (this) {
+                Log.i("**", "******************************* WAITING!!!");
+                try {
+                    wait(10000);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+
+        // See if we now have access to the provider.
+        getContext().getContentResolver().query(sub2Uri, null, null, null, null);
+
+        // And still to the previous URI.
+        getContext().getContentResolver().query(subUri, null, null, null, null);
+
+        // But not writing.
+        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write from granted read");
+
+        // And not to the base path.
+        assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
+
+        // And not to a sub path.
+        assertReadingContentUriNotAllowed(sub2SubUri, "shouldn't read non-granted sub URI");
+
+        // --------------------------------
+
+        // Stop the first command.
+        ReceiveUriService.stopCurWithId(firstStartId);
+
+        // Ensure reading no longer allowed.
+        assertReadingContentUriNotAllowed(subUri, "shouldn't read after losing granted URI");
+
+        // And make sure we can't generate a permission to a running service.
+        doTryGrantUriActivityPermissionToSelf(subUri,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        doTryGrantUriActivityPermissionToSelf(subUri,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        // --------------------------------
+
+        // Dispose of service.
+        ReceiveUriService.stopSync(getContext());
+
+        // Ensure reading no longer allowed.
+        assertReadingContentUriNotAllowed(subUri, "shouldn't read after losing granted URI");
+        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read after losing granted URI");
+    }
+
+    void doTestGrantServiceUriWritePermission(Uri uri) {
+        final Uri subUri = Uri.withAppendedPath(uri, "foo");
+        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+        final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
+        final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
+
+        ReceiveUriService.stop(getContext());
+
+        // Precondition: no current access.
+        assertWritingContentUriNotAllowed(subUri, "shouldn't write when starting test");
+        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write when starting test");
+
+        // --------------------------------
+
+        ReceiveUriService.clearStarted();
+        grantUriPermission(subUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
+        ReceiveUriService.waitForStart();
+
+        int firstStartId = ReceiveUriService.getCurStartId();
+
+        // See if we now have access to the provider.
+        getContext().getContentResolver().insert(subUri, new ContentValues());
+
+        // But not reading.
+        assertReadingContentUriNotAllowed(subUri, "shouldn't read from granted write");
+
+        // And not to the base path.
+        assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
+
+        // And not a sub-path.
+        assertWritingContentUriNotAllowed(subSubUri, "shouldn't write non-granted sub URI");
+
+        // --------------------------------
+
+        // Send another Intent to it.
+        ReceiveUriService.clearStarted();
+        grantUriPermission(sub2Uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
+        ReceiveUriService.waitForStart();
+
+        // See if we now have access to the provider.
+        getContext().getContentResolver().insert(sub2Uri, new ContentValues());
+
+        // And still to the previous URI.
+        getContext().getContentResolver().insert(subUri, new ContentValues());
+
+        // But not reading.
+        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read from granted write");
+
+        // And not to the base path.
+        assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
+
+        // And not a sub-path.
+        assertWritingContentUriNotAllowed(sub2SubUri, "shouldn't write non-granted sub URI");
+
+        if (false) {
+            synchronized (this) {
+                Log.i("**", "******************************* WAITING!!!");
+                try {
+                    wait(10000);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+
+        // --------------------------------
+
+        // Stop the first command.
+        ReceiveUriService.stopCurWithId(firstStartId);
+
+        // Ensure writing no longer allowed.
+        assertWritingContentUriNotAllowed(subUri, "shouldn't write after losing granted URI");
+
+        // And make sure we can't generate a permission to a running service.
+        doTryGrantUriActivityPermissionToSelf(subUri,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        doTryGrantUriActivityPermissionToSelf(subUri,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        // --------------------------------
+
+        // Dispose of service.
+        ReceiveUriService.stopSync(getContext());
+
+        // Ensure writing no longer allowed.
+        assertWritingContentUriNotAllowed(subUri, "shouldn't write after losing granted URI");
+        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write after losing granted URI");
+    }
+
+    public void testGrantReadPermissionFromStartService() {
+        doTestGrantServiceUriReadPermission(PERM_URI_GRANTING);
+    }
+
+    public void testGrantWritePermissionFromStartService() {
+        doTestGrantServiceUriWritePermission(PERM_URI_GRANTING);
+    }
+
+    public void testGrantReadPrivateFromStartService() {
+        doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING);
+    }
+
+    public void testGrantWritePrivateFromStartService() {
+        doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING);
+    }
+
+    /**
+     * Test that ctspermissionwithsignaturepath can't grant read permissions
+     * on paths it doesn't have permission to.
+     */
+    public void testGrantReadUriActivityPathPermissionToSelf() {
+        doTryGrantUriActivityPermissionToSelf(PERM_URI_PATH,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+    }
+
+    /**
+     * Test that ctspermissionwithsignaturepath can't grant write permissions
+     * on paths it doesn't have permission to.
+     */
+    public void testGrantWriteUriActivityPathPermissionToSelf() {
+        doTryGrantUriActivityPermissionToSelf(PERM_URI_PATH,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    /**
+     * Test that ctspermissionwithsignaturepath can't grant read permissions
+     * on paths it doesn't have permission to.
+     */
+    public void testGrantReadUriActivitySubPathPermissionToSelf() {
+        doTryGrantUriActivityPermissionToSelf(
+                Uri.withAppendedPath(PERM_URI_PATH, "foo"),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+    }
+
+    /**
+     * Test that ctspermissionwithsignaturepath can't grant write permissions
+     * on paths it doesn't have permission to.
+     */
+    public void testGrantWriteUriActivitySubPathPermissionToSelf() {
+        doTryGrantUriActivityPermissionToSelf(
+                Uri.withAppendedPath(PERM_URI_PATH, "foo"),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    /**
+     * Test that the ctspermissionwithsignaturepath content provider can grant a read
+     * permission.
+     */
+    public void testGrantReadPathPermissionFromStartActivity() {
+        doTestGrantActivityUriReadPermission(PERM_URI_PATH);
+    }
+
+    /**
+     * Test that the ctspermissionwithsignaturepath content provider can grant a write
+     * permission.
+     */
+    public void testGrantWritePathPermissionFromStartActivity() {
+        doTestGrantActivityUriWritePermission(PERM_URI_PATH);
+    }
+
+    /**
+     * Test that the ctspermissionwithsignaturepath content provider can grant a read
+     * permission.
+     */
+    public void testGrantReadPathPermissionFromStartService() {
+        doTestGrantServiceUriReadPermission(PERM_URI_PATH);
+    }
+
+    /**
+     * Test that the ctspermissionwithsignaturepath content provider can grant a write
+     * permission.
+     */
+    public void testGrantWritePathPermissionFromStartService() {
+        doTestGrantServiceUriWritePermission(PERM_URI_PATH);
+    }
+
+    public void testGetMimeTypePermission() {
+        // Precondition: no current access.
+        assertWritingContentUriNotAllowed(PERM_URI, "shouldn't write when starting test");
+        assertWritingContentUriNotAllowed(PERM_URI, "shouldn't write when starting test");
+        
+        // All apps should be able to get MIME type regardless of permission.
+        assertEquals(getContext().getContentResolver().getType(PERM_URI), EXPECTED_MIME_TYPE);
+    }
+
+    public void testGetMimeTypePrivate() {
+        // Precondition: no current access.
+        assertWritingContentUriNotAllowed(PRIV_URI, "shouldn't write when starting test");
+        assertWritingContentUriNotAllowed(PRIV_URI, "shouldn't write when starting test");
+        
+        // All apps should be able to get MIME type even if provider is private.
+        assertEquals(getContext().getContentResolver().getType(PRIV_URI), EXPECTED_MIME_TYPE);
+    }
 }
diff --git a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java
new file mode 100644
index 0000000..0ddc4a5
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2010 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.usespermissiondiffcertapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.MessageQueue.IdleHandler;
+import android.util.Log;
+
+public class ReceiveUriActivity extends Activity {
+    static final String TAG = "ReceiveUriActivity";
+    private static final Object sLock = new Object();
+    private static boolean sStarted;
+    private static boolean sNewIntent;
+    private static boolean sDestroyed;
+    private static ReceiveUriActivity sCurInstance;
+
+    Handler mHandler = new Handler();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        synchronized (sLock) {
+            Log.i(TAG, "onCreate: sCurInstance=" + sCurInstance);
+            if (sCurInstance != null) {
+                finishCurInstance();
+            }
+            sCurInstance = this;
+            sStarted = true;
+            sDestroyed = false;
+            sLock.notifyAll();
+        }
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+
+        synchronized (sLock) {
+            Log.i(TAG, "onNewIntent: sCurInstance=" + sCurInstance);
+            sNewIntent = true;
+            sLock.notifyAll();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "onDestroy: sCurInstance=" + sCurInstance);
+        Looper.myQueue().addIdleHandler(new IdleHandler() {
+            @Override
+            public boolean queueIdle() {
+                synchronized (sLock) {
+                    sDestroyed = true;
+                    sLock.notifyAll();
+                }
+                return false;
+            }
+        });
+    }
+
+    public static void finishCurInstance() {
+        synchronized (sLock) {
+            if (sCurInstance != null) {
+                sCurInstance.finish();
+                sCurInstance = null;
+            }
+        }
+    }
+
+    public static void finishCurInstanceSync() {
+        finishCurInstance();
+
+        synchronized (sLock) {
+            final long startTime = SystemClock.uptimeMillis();
+            while (!sDestroyed) {
+                try {
+                    sLock.wait(5000);
+                } catch (InterruptedException e) {
+                }
+                if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+                    throw new RuntimeException("Timeout");
+                }
+            }
+        }
+    }
+
+    public static void clearStarted() {
+        synchronized (sLock) {
+            sStarted = false;
+        }
+    }
+
+    public static void clearNewIntent() {
+        synchronized (sLock) {
+            sNewIntent = false;
+        }
+    }
+
+    public static void waitForStart() {
+        synchronized (sLock) {
+            final long startTime = SystemClock.uptimeMillis();
+            while (!sStarted) {
+                try {
+                    sLock.wait(5000);
+                } catch (InterruptedException e) {
+                }
+                if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+                    throw new RuntimeException("Timeout");
+                }
+            }
+        }
+    }
+
+    public static void waitForNewIntent() {
+        synchronized (sLock) {
+            final long startTime = SystemClock.uptimeMillis();
+            while (!sNewIntent) {
+                try {
+                    sLock.wait(5000);
+                } catch (InterruptedException e) {
+                }
+                if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+                    throw new RuntimeException("Timeout");
+                }
+            }
+        }
+    }
+}
diff --git a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriService.java b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriService.java
new file mode 100644
index 0000000..68d78c0
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriService.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010 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.usespermissiondiffcertapp;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.MessageQueue.IdleHandler;
+
+public class ReceiveUriService extends Service {
+    private static final Object sLock = new Object();
+    private static boolean sStarted;
+    private static boolean sDestroyed;
+    private static int sCurStartId;
+    private static ReceiveUriService sCurInstance;
+
+    Handler mHandler = new Handler();
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        synchronized (sLock) {
+            sCurStartId = startId;
+            sCurInstance = this;
+            sStarted = true;
+            sDestroyed = false;
+            sLock.notifyAll();
+        }
+
+        return START_REDELIVER_INTENT;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Looper.myQueue().addIdleHandler(new IdleHandler() {
+            @Override
+            public boolean queueIdle() {
+                synchronized (sLock) {
+                    sDestroyed = true;
+                    sCurInstance = null;
+                    sLock.notifyAll();
+                }
+                return false;
+            }
+        });
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    public static void stop(Context context) {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(
+                "com.android.cts.usespermissiondiffcertapp",
+                "com.android.cts.usespermissiondiffcertapp.ReceiveUriService"));
+        context.stopService(intent);
+    }
+
+    public static int getCurStartId() {
+        synchronized (sLock) {
+            return sCurStartId;
+        }
+    }
+
+    public static void stopCurWithId(int id) {
+        synchronized (sLock) {
+            sCurInstance.stopSelf(id);
+        }
+    }
+
+    public static void stopSync(Context context) {
+        stop(context);
+
+        synchronized (sLock) {
+            final long startTime = SystemClock.uptimeMillis();
+            while (!sDestroyed) {
+                try {
+                    sLock.wait(5000);
+                } catch (InterruptedException e) {
+                }
+                if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+                    throw new RuntimeException("Timeout");
+                }
+            }
+        }
+    }
+
+    public static void clearStarted() {
+        synchronized (sLock) {
+            sStarted = false;
+        }
+    }
+
+    public static void waitForStart() {
+        synchronized (sLock) {
+            final long startTime = SystemClock.uptimeMillis();
+            while (!sStarted) {
+                try {
+                    sLock.wait(5000);
+                } catch (InterruptedException e) {
+                }
+                if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+                    throw new RuntimeException("Timeout");
+                }
+            }
+        }
+    }
+}
diff --git a/tests/core/annotation/Android.mk b/tests/core/annotation/Android.mk
deleted file mode 100644
index 4778710..0000000
--- a/tests/core/annotation/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Annotation Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/annotation/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.annotation
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/annotation/AndroidManifest.xml b/tests/core/annotation/AndroidManifest.xml
deleted file mode 100644
index 7e34d2a..0000000
--- a/tests/core/annotation/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.annotation">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/archive/Android.mk b/tests/core/archive/Android.mk
deleted file mode 100644
index 78edd09..0000000
--- a/tests/core/archive/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Archive Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/archive/src/test/java/org) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.archive
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/archive/AndroidManifest.xml b/tests/core/archive/AndroidManifest.xml
deleted file mode 100644
index 22ba6f7..0000000
--- a/tests/core/archive/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.archive">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/concurrent/Android.mk b/tests/core/concurrent/Android.mk
deleted file mode 100644
index d42c6da..0000000
--- a/tests/core/concurrent/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Concurrent Tests
-##########################################################
-include $(CLEAR_VARS)
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/concurrent/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.concurrent
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/concurrent/AndroidManifest.xml b/tests/core/concurrent/AndroidManifest.xml
deleted file mode 100644
index b3ee187..0000000
--- a/tests/core/concurrent/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.concurrent">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/crypto/Android.mk b/tests/core/crypto/Android.mk
deleted file mode 100644
index 2450ad1..0000000
--- a/tests/core/crypto/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# crypto Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/crypto/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java/) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.crypto
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/crypto/AndroidManifest.xml b/tests/core/crypto/AndroidManifest.xml
deleted file mode 100644
index 25052ee..0000000
--- a/tests/core/crypto/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.crypto">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/ctscore.mk b/tests/core/ctscore.mk
index ab7f7be..337b3f5 100644
--- a/tests/core/ctscore.mk
+++ b/tests/core/ctscore.mk
@@ -17,8 +17,7 @@
 # and when built explicitly put them in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := CtsTestAnnotationsLib
+LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle
 
 LOCAL_PROGUARD_ENABLED := disabled
 
diff --git a/tests/core/dom/Android.mk b/tests/core/dom/Android.mk
index 47b3dbb..69d4af3 100644
--- a/tests/core/dom/Android.mk
+++ b/tests/core/dom/Android.mk
@@ -23,10 +23,13 @@
 ##########################################################
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/dom/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
+LOCAL_SRC_FILES := $(call all-java-files-under,../../../../libcore/dom/src/test/java) \
+	$(call all-java-files-under,../../../../libcore/junit/src/test/java/junit) \
+	$(call all-java-files-under,../../../../libcore/support/src/test/java/)
 
 LOCAL_PACKAGE_NAME := android.core.tests.dom
 
+# for java.* javax.* support classes in libcore/support/src/test/java
+LOCAL_DX_FLAGS := --core-library
+
 include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/logging/Android.mk b/tests/core/logging/Android.mk
deleted file mode 100644
index 89d1ca9..0000000
--- a/tests/core/logging/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Logging Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/logging/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.logging
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/logging/AndroidManifest.xml b/tests/core/logging/AndroidManifest.xml
deleted file mode 100644
index c6e5bee..0000000
--- a/tests/core/logging/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.logging">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/luni-io/Android.mk b/tests/core/luni-io/Android.mk
index f2aae36..ed39e03 100644
--- a/tests/core/luni-io/Android.mk
+++ b/tests/core/luni-io/Android.mk
@@ -23,14 +23,16 @@
 ##########################################################
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/tests/api/java/io) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java/) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/org/apache/harmony/luni/tests/pkg1) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/org/apache/harmony/luni/tests/pkg2) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/luni/AllTestsIo.java \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
+LOCAL_SRC_FILES := $(call all-java-files-under,../../../../libcore/luni/src/test/java/tests/api/java/io) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io) \
+	$(call all-java-files-under,../../../../libcore/support/src/test/java/) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/org/apache/harmony/luni/tests/pkg1) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/org/apache/harmony/luni/tests/pkg2) \
+	../../../../libcore/luni/src/test/java/tests/luni/AllTestsIo.java
 
 LOCAL_PACKAGE_NAME := android.core.tests.luni.io
 
+# for java.* javax.* support classes in libcore/support/src/test/java
+LOCAL_DX_FLAGS := --core-library
+
 include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/luni-lang/Android.mk b/tests/core/luni-lang/Android.mk
index 225bdf9..a31b881 100644
--- a/tests/core/luni-lang/Android.mk
+++ b/tests/core/luni-lang/Android.mk
@@ -23,12 +23,14 @@
 ##########################################################
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/tests/api/java/lang) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java/) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/luni/AllTestsLang.java \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
+LOCAL_SRC_FILES := $(call all-java-files-under,../../../../libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/tests/api/java/lang) \
+	$(call all-java-files-under,../../../../libcore/support/src/test/java/) \
+	../../../../libcore/luni/src/test/java/tests/luni/AllTestsLang.java
 
 LOCAL_PACKAGE_NAME := android.core.tests.luni.lang
 
+# for java.* javax.* support classes in libcore/support/src/test/java
+LOCAL_DX_FLAGS := --core-library
+
 include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/luni-net/Android.mk b/tests/core/luni-net/Android.mk
index 1092ff1..8a6508c 100644
--- a/tests/core/luni-net/Android.mk
+++ b/tests/core/luni-net/Android.mk
@@ -23,14 +23,17 @@
 ##########################################################
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/tests/api/java/net) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/http) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/https) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java/) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/luni/AllTestsNet.java \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
+LOCAL_SRC_FILES := \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/libcore/java/net) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/http) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/https) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net) \
+	$(call all-java-files-under,../../../../libcore/support/src/test/java/) \
+	../../../../libcore/luni/src/test/java/tests/luni/AllTestsNet.java
 
 LOCAL_PACKAGE_NAME := android.core.tests.luni.net
 
+# for java.* javax.* support classes in libcore/support/src/test/java
+LOCAL_DX_FLAGS := --core-library
+
 include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/luni-util/Android.mk b/tests/core/luni-util/Android.mk
index 101cde8..5468c17 100644
--- a/tests/core/luni-util/Android.mk
+++ b/tests/core/luni-util/Android.mk
@@ -23,12 +23,14 @@
 ##########################################################
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/util) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/tests/api/java/util) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java/) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/luni/AllTestsUtil.java \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
+LOCAL_SRC_FILES := $(call all-java-files-under,../../../../libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/util) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/tests/api/java/util) \
+	$(call all-java-files-under,../../../../libcore/support/src/test/java/) \
+	../../../../libcore/luni/src/test/java/tests/luni/AllTestsUtil.java
 
 LOCAL_PACKAGE_NAME := android.core.tests.luni.util
 
+# for java.* javax.* support classes in libcore/support/src/test/java
+LOCAL_DX_FLAGS := --core-library
+
 include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/math/Android.mk b/tests/core/math/Android.mk
deleted file mode 100644
index 4c559cd..0000000
--- a/tests/core/math/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Math Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/math/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.math
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/math/AndroidManifest.xml b/tests/core/math/AndroidManifest.xml
deleted file mode 100644
index f87fa13..0000000
--- a/tests/core/math/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.math">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/nio/Android.mk b/tests/core/nio/Android.mk
deleted file mode 100644
index 6ebd1bc..0000000
--- a/tests/core/nio/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Nio Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/nio/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.nio
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/nio/AndroidManifest.xml b/tests/core/nio/AndroidManifest.xml
deleted file mode 100644
index 4c79bfa..0000000
--- a/tests/core/nio/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.nio">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/nio_char/Android.mk b/tests/core/nio_char/Android.mk
deleted file mode 100644
index e3b463a..0000000
--- a/tests/core/nio_char/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# NioChar Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/nio_char/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.nio_char
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/nio_char/AndroidManifest.xml b/tests/core/nio_char/AndroidManifest.xml
deleted file mode 100644
index b8de2c9..0000000
--- a/tests/core/nio_char/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.nio_char">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/prefs/Android.mk b/tests/core/prefs/Android.mk
deleted file mode 100644
index f8f225c..0000000
--- a/tests/core/prefs/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Prefs Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/prefs/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.prefs
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/prefs/AndroidManifest.xml b/tests/core/prefs/AndroidManifest.xml
deleted file mode 100644
index d994c72..0000000
--- a/tests/core/prefs/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.prefs">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/regex/Android.mk b/tests/core/regex/Android.mk
deleted file mode 100644
index 70927a0..0000000
--- a/tests/core/regex/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Regex Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/regex/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.regex
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/regex/AndroidManifest.xml b/tests/core/regex/AndroidManifest.xml
deleted file mode 100644
index ddd2165..0000000
--- a/tests/core/regex/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.regex">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java b/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
index b5f1919..5aa2c3b 100644
--- a/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
+++ b/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
@@ -22,11 +22,8 @@
 import dalvik.annotation.BrokenTest;
 import dalvik.annotation.SideEffect;
 
-import android.annotation.cts.RequiredFeatures;
-import android.app.Instrumentation;
 import android.app.KeyguardManager;
 import android.content.Context;
-import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.test.suitebuilder.TestMethod;
@@ -36,10 +33,7 @@
 import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.TimeZone;
 
 import junit.framework.AssertionFailedError;
@@ -67,8 +61,6 @@
 
     private static final String REPORT_VALUE_ID = "InstrumentationCtsTestRunner";
 
-    private static final int REPORT_VALUE_RESULT_OMITTED = -3;
-
     /**
      * True if (and only if) we are running in single-test mode (as opposed to
      * batch mode).
@@ -87,6 +79,7 @@
         System.setProperty("java.home", cacheDir.getAbsolutePath());
         System.setProperty("user.home", cacheDir.getAbsolutePath());
         System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
+        System.setProperty("user.dir", cacheDir.getAbsolutePath());
         System.setProperty("javax.net.ssl.trustStore",
                 "/etc/security/cacerts.bks");
 
@@ -234,8 +227,6 @@
                 Predicates.not(new HasAnnotation(BrokenTest.class));
         builderRequirements.add(brokenTestPredicate);
 
-        builderRequirements.add(getFeaturePredicate());
-
         if (!mSingleTest) {
             Predicate<TestMethod> sideEffectPredicate =
                     Predicates.not(new HasAnnotation(SideEffect.class));
@@ -243,64 +234,4 @@
         }
         return builderRequirements;
     }
-
-    /**
-     * Send back an indication that a test was omitted. InstrumentationTestRunner won't run omitted
-     * tests, but CTS needs to know that the test was omitted. Otherwise, it will attempt to rerun
-     * the test thinking that ADB must have crashed or something.
-     */
-    private void sendOmittedStatus(TestMethod t) {
-        Bundle bundle = new Bundle();
-        bundle.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
-        bundle.putInt(InstrumentationTestRunner.REPORT_KEY_NUM_TOTAL, 1);
-        bundle.putInt(InstrumentationTestRunner.REPORT_KEY_NUM_CURRENT, 1);
-        bundle.putString(InstrumentationTestRunner.REPORT_KEY_NAME_CLASS,
-                t.getEnclosingClassname());
-        bundle.putString(InstrumentationTestRunner.REPORT_KEY_NAME_TEST,
-                t.getName());
-
-        // First status message causes CTS to print out the test name like "Class#test..."
-        sendStatus(InstrumentationTestRunner.REPORT_VALUE_RESULT_START, bundle);
-
-        // Second status message causes CTS to complete the line like "Class#test...(omitted)"
-        sendStatus(REPORT_VALUE_RESULT_OMITTED, bundle);
-    }
-
-
-    private Predicate<TestMethod> getFeaturePredicate() {
-        return new Predicate<TestMethod>() {
-            public boolean apply(TestMethod t) {
-                if (isValidTest(t)) {
-                    // InstrumentationTestRunner will run the test and send back results.
-                    return true;
-                } else {
-                    // InstrumentationTestRunner WON'T run the test, so send back omitted status.
-                    sendOmittedStatus(t);
-                    return false;
-                }
-            }
-
-            private boolean isValidTest(TestMethod t) {
-                Set<String> features = new HashSet<String>();
-                add(features, t.getAnnotation(RequiredFeatures.class));
-                add(features, t.getEnclosingClass().getAnnotation(RequiredFeatures.class));
-
-                // Run the test only if the device supports all the features.
-                PackageManager packageManager = getContext().getPackageManager();
-                FeatureInfo[] featureInfos = packageManager.getSystemAvailableFeatures();
-                if (featureInfos != null) {
-                    for (FeatureInfo featureInfo : featureInfos) {
-                        features.remove(featureInfo.name);
-                    }
-                }
-                return features.isEmpty();
-            }
-
-            private void add(Set<String> features, RequiredFeatures annotation) {
-                if (annotation != null) {
-                    Collections.addAll(features, annotation.value());
-                }
-            }
-        };
-    }
 }
diff --git a/tests/core/security/Android.mk b/tests/core/security/Android.mk
deleted file mode 100644
index 76c0ca5..0000000
--- a/tests/core/security/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Security Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/security/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.security
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/security/AndroidManifest.xml b/tests/core/security/AndroidManifest.xml
deleted file mode 100644
index 3960a82..0000000
--- a/tests/core/security/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.security">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/sql/Android.mk b/tests/core/sql/Android.mk
deleted file mode 100644
index fe9b4cc..0000000
--- a/tests/core/sql/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Sql Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/sql/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.sql
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/sql/AndroidManifest.xml b/tests/core/sql/AndroidManifest.xml
deleted file mode 100644
index 0ebcab6..0000000
--- a/tests/core/sql/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.sql">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/text/Android.mk b/tests/core/text/Android.mk
deleted file mode 100644
index 8b9fd1f..0000000
--- a/tests/core/text/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Text Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/text/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.text
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/text/AndroidManifest.xml b/tests/core/text/AndroidManifest.xml
deleted file mode 100644
index baf0031..0000000
--- a/tests/core/text/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.text">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/xml/Android.mk b/tests/core/xml/Android.mk
index ba784b9..4f8af07 100644
--- a/tests/core/xml/Android.mk
+++ b/tests/core/xml/Android.mk
@@ -23,12 +23,20 @@
 ##########################################################
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/xml/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/dom/src/test) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
+LOCAL_SRC_FILES := $(call all-java-files-under,../../../../libcore/xml/src/test/java) \
+	$(call all-java-files-under,../../../../libcore/dom/src/test) \
+	$(call all-java-files-under,../../../../libcore/junit/src/test/java/junit) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/org/apache/harmony/xml) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/tests/api/javax/xml/parsers) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/tests/api/org/xml/sax) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/tests/api/org/xml/sax/support) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/tests/org/w3c/dom) \
+	$(call all-java-files-under,../../../../libcore/luni/src/test/java/tests/xml) \
+	$(call all-java-files-under,../../../../libcore/support/src/test/java)
 
 LOCAL_PACKAGE_NAME := android.core.tests.xml
 
+# for java.* javax.* support classes in libcore/support/src/test/java
+LOCAL_DX_FLAGS := --core-library
+
 include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/xnet/Android.mk b/tests/core/xnet/Android.mk
deleted file mode 100644
index 2415f38..0000000
--- a/tests/core/xnet/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2009 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)
-
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-#
-# Xnet Tests
-##########################################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,../../../../dalvik/libcore/x-net/src/test/java) \
-	$(call all-java-files-under,../../../../dalvik/libcore/luni/src/test/java/junit) \
-	$(call all-java-files-under,../../../../dalvik/libcore/support/src/test/java) \
-	../../../../dalvik/libcore/luni/src/test/java/tests/TestSuiteFactory.java
-
-LOCAL_PACKAGE_NAME := android.core.tests.xnet
-
-include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/xnet/AndroidManifest.xml b/tests/core/xnet/AndroidManifest.xml
deleted file mode 100644
index 82e5dd7..0000000
--- a/tests/core/xnet/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 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.core.tests.xnet">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/apps/CtsVerifier/tests/Android.mk b/tests/deviceadmin/Android.mk
similarity index 85%
copy from apps/CtsVerifier/tests/Android.mk
copy to tests/deviceadmin/Android.mk
index b9572fb..7322ad5 100644
--- a/apps/CtsVerifier/tests/Android.mk
+++ b/tests/deviceadmin/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
+# 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.
@@ -12,9 +11,9 @@
 # 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
@@ -25,9 +24,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CtsVerifierTests
-
-LOCAL_INSTRUMENTATION_FOR := CtsVerifier
+LOCAL_PACKAGE_NAME := CtsDeviceAdmin
 
 LOCAL_SDK_VERSION := current
 
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/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
new file mode 100644
index 0000000..3ca13f9
--- /dev/null
+++ b/tests/expectations/knownfailures.txt
@@ -0,0 +1,44 @@
+[
+/* These tests consistently fail on GRH78. */
+{ name: "android.location.cts.GeocoderTest#testGetFromLocation" },
+{ name: "android.location.cts.GeocoderTest#testGetFromLocationName" },
+{ name: "android.webkit.cts.WebSettingsTest#testSetAppCacheEnabled" },
+{ name: "android.net.cts.SSLCertificateSocketFactoryTest" },
+
+{
+  description: "Flaky tests that need to be rewritten or deleted.",
+  names: [
+    "android.webkit.cts.CacheManagerTest#testCacheFile",
+    "android.widget.cts.AutoCompleteTextViewTest#testOnFilterComplete"
+  ]
+},
+{
+  bug: 4464677,
+  name: "android.media.cts.MediaRecorderTest#testSetCamera"
+},
+{
+  bug: 5104153,
+  name: "android.media.cts.MediaRecorderTest#testSetMaxDuration"
+},
+{
+  bug: 5103805,
+  name: "android.media.cts.MediaRecorderTest#testSetMaxFileSize"
+},
+{
+  description: "Broken test that is fixed in a future release.",
+  name: "android.provider.cts.MediaStore_Audio_Playlists_MembersTest"
+},
+{
+  description: "These tests pass when executed individually but fail when running CTS as a whole on GRH78.",
+  bug: 3184701,
+  names: [
+    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyConnection",
+    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnection",
+    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testConsequentProxyConnection",
+    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnection_doOutput",
+    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnectionFailed",
+    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyConnection_Not_Found_Response"
+  ]
+}
+
+]
diff --git a/tests/jni/Android.mk b/tests/jni/Android.mk
index f20fcb5..3f38bce 100644
--- a/tests/jni/Android.mk
+++ b/tests/jni/Android.mk
@@ -26,6 +26,7 @@
 
 LOCAL_SRC_FILES := \
 		CtsJniOnLoad.cpp \
+		android_os_cts_FileUtils.cpp \
 		android_net_cts_NetlinkSocket.cpp
 
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) 
diff --git a/tests/jni/CtsJniOnLoad.cpp b/tests/jni/CtsJniOnLoad.cpp
index 7b9dea5..51d61f9 100644
--- a/tests/jni/CtsJniOnLoad.cpp
+++ b/tests/jni/CtsJniOnLoad.cpp
@@ -22,6 +22,8 @@
 extern int register_android_os_cts_CpuFeatures(JNIEnv*);
 #endif
 
+extern int register_android_os_cts_FileUtils(JNIEnv*);
+
 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
     JNIEnv *env = NULL;
 
@@ -35,6 +37,10 @@
     }
 #endif
 
+    if (register_android_os_cts_FileUtils(env)) {
+      return JNI_ERR;
+    }
+
     if (register_android_net_cts_NetlinkSocket(env)) {
         return JNI_ERR;
     }
diff --git a/tests/jni/android_os_cts_FileUtils.cpp b/tests/jni/android_os_cts_FileUtils.cpp
new file mode 100644
index 0000000..d78d26c
--- /dev/null
+++ b/tests/jni/android_os_cts_FileUtils.cpp
@@ -0,0 +1,142 @@
+/* 
+ * 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.
+ */
+
+#include <grp.h>
+#include <jni.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+static jclass gFileStatusClass;
+static jfieldID gFileStatusDevFieldID;
+static jfieldID gFileStatusInoFieldID;
+static jfieldID gFileStatusModeFieldID;
+static jfieldID gFileStatusNlinkFieldID;
+static jfieldID gFileStatusUidFieldID;
+static jfieldID gFileStatusGidFieldID;
+static jfieldID gFileStatusSizeFieldID;
+static jfieldID gFileStatusBlksizeFieldID;
+static jfieldID gFileStatusBlocksFieldID;
+static jfieldID gFileStatusAtimeFieldID;
+static jfieldID gFileStatusMtimeFieldID;
+static jfieldID gFileStatusCtimeFieldID;
+
+/*
+ * Native methods used by
+ * cts/tests/src/android/os/cts/FileUtils.java
+ *
+ * Copied from hidden API: frameworks/base/core/jni/android_os_FileUtils.cpp
+ */
+
+jboolean android_os_cts_FileUtils_getFileStatus(JNIEnv* env, jobject thiz,
+        jstring path, jobject fileStatus, jboolean statLinks)
+{
+    const char* pathStr = env->GetStringUTFChars(path, NULL);
+    jboolean ret = false;
+    struct stat s;
+
+    int res = statLinks == true ? lstat(pathStr, &s) : stat(pathStr, &s);
+
+    if (res == 0) {
+        ret = true;
+        if (fileStatus != NULL) {
+            env->SetIntField(fileStatus, gFileStatusDevFieldID, s.st_dev);
+            env->SetIntField(fileStatus, gFileStatusInoFieldID, s.st_ino);
+            env->SetIntField(fileStatus, gFileStatusModeFieldID, s.st_mode);
+            env->SetIntField(fileStatus, gFileStatusNlinkFieldID, s.st_nlink);
+            env->SetIntField(fileStatus, gFileStatusUidFieldID, s.st_uid);
+            env->SetIntField(fileStatus, gFileStatusGidFieldID, s.st_gid);
+            env->SetLongField(fileStatus, gFileStatusSizeFieldID, s.st_size);
+            env->SetIntField(fileStatus, gFileStatusBlksizeFieldID, s.st_blksize);
+            env->SetLongField(fileStatus, gFileStatusBlocksFieldID, s.st_blocks);
+            env->SetLongField(fileStatus, gFileStatusAtimeFieldID, s.st_atime);
+            env->SetLongField(fileStatus, gFileStatusMtimeFieldID, s.st_mtime);
+            env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime);
+        }
+    }
+
+    env->ReleaseStringUTFChars(path, pathStr);
+
+    return ret;
+}
+
+jstring android_os_cts_FileUtils_getUserName(JNIEnv* env, jobject thiz,
+        jint uid)
+{
+    struct passwd *pwd = getpwuid(uid);
+    return env->NewStringUTF(pwd->pw_name);
+}
+
+jstring android_os_cts_FileUtils_getGroupName(JNIEnv* env, jobject thiz,
+        jint gid)
+{
+    struct group *grp = getgrgid(gid);
+    return env->NewStringUTF(grp->gr_name);
+}
+
+jint android_os_cts_FileUtils_setPermissions(JNIEnv* env, jobject clazz,
+        jstring file, jint mode)
+{
+    const char *fileStr = env->GetStringUTFChars(file, NULL);
+    if (fileStr == NULL) {
+        return -1;
+    }
+
+    if (strlen(fileStr) <= 0) {
+        env->ReleaseStringUTFChars(file, fileStr);
+        return ENOENT;
+    } 
+
+    jint returnValue = chmod(fileStr, mode) == 0 ? 0 : errno;
+    env->ReleaseStringUTFChars(file, fileStr);
+    return returnValue;
+}
+
+static JNINativeMethod gMethods[] = {
+    {  "getFileStatus", "(Ljava/lang/String;Landroid/os/cts/FileUtils$FileStatus;Z)Z",
+            (void *) android_os_cts_FileUtils_getFileStatus  },
+    {  "getUserName", "(I)Ljava/lang/String;",
+            (void *) android_os_cts_FileUtils_getUserName  },
+    {  "getGroupName", "(I)Ljava/lang/String;",
+            (void *) android_os_cts_FileUtils_getGroupName  },
+    {  "setPermissions", "(Ljava/lang/String;I)I",
+            (void *) android_os_cts_FileUtils_setPermissions },
+};
+
+int register_android_os_cts_FileUtils(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/os/cts/FileUtils");
+
+    gFileStatusClass = env->FindClass("android/os/cts/FileUtils$FileStatus");
+    gFileStatusDevFieldID = env->GetFieldID(gFileStatusClass, "dev", "I");
+    gFileStatusInoFieldID = env->GetFieldID(gFileStatusClass, "ino", "I");
+    gFileStatusModeFieldID = env->GetFieldID(gFileStatusClass, "mode", "I");
+    gFileStatusNlinkFieldID = env->GetFieldID(gFileStatusClass, "nlink", "I");
+    gFileStatusUidFieldID = env->GetFieldID(gFileStatusClass, "uid", "I");
+    gFileStatusGidFieldID = env->GetFieldID(gFileStatusClass, "gid", "I");
+    gFileStatusSizeFieldID = env->GetFieldID(gFileStatusClass, "size", "J");
+    gFileStatusBlksizeFieldID = env->GetFieldID(gFileStatusClass, "blksize", "I");
+    gFileStatusBlocksFieldID = env->GetFieldID(gFileStatusClass, "blocks", "J");
+    gFileStatusAtimeFieldID = env->GetFieldID(gFileStatusClass, "atime", "J");
+    gFileStatusMtimeFieldID = env->GetFieldID(gFileStatusClass, "mtime", "J");
+    gFileStatusCtimeFieldID = env->GetFieldID(gFileStatusClass, "ctime", "J");
+
+    return env->RegisterNatives(clazz, gMethods, 
+            sizeof(gMethods) / sizeof(JNINativeMethod)); 
+}
diff --git a/tests/res/drawable/baseline_jpeg.jpg b/tests/res/drawable/baseline_jpeg.jpg
new file mode 100644
index 0000000..ed5251c
--- /dev/null
+++ b/tests/res/drawable/baseline_jpeg.jpg
Binary files differ
diff --git a/tests/res/drawable/baseline_restart_jpeg.jpg b/tests/res/drawable/baseline_restart_jpeg.jpg
new file mode 100644
index 0000000..00dcc5b
--- /dev/null
+++ b/tests/res/drawable/baseline_restart_jpeg.jpg
Binary files differ
diff --git a/tests/res/drawable/bmp_test.bmp b/tests/res/drawable/bmp_test.bmp
new file mode 100644
index 0000000..5ec6dd4
--- /dev/null
+++ b/tests/res/drawable/bmp_test.bmp
Binary files differ
diff --git a/tests/res/drawable/gif_test.gif b/tests/res/drawable/gif_test.gif
new file mode 100644
index 0000000..d1c2815
--- /dev/null
+++ b/tests/res/drawable/gif_test.gif
Binary files differ
diff --git a/tests/res/drawable/png_test.png b/tests/res/drawable/png_test.png
new file mode 100644
index 0000000..5230051
--- /dev/null
+++ b/tests/res/drawable/png_test.png
Binary files differ
diff --git a/tests/res/drawable/progressive_jpeg.jpg b/tests/res/drawable/progressive_jpeg.jpg
new file mode 100644
index 0000000..6b58be4
--- /dev/null
+++ b/tests/res/drawable/progressive_jpeg.jpg
Binary files differ
diff --git a/tests/res/drawable/progressive_restart_jpeg.jpg b/tests/res/drawable/progressive_restart_jpeg.jpg
new file mode 100644
index 0000000..352c7a8
--- /dev/null
+++ b/tests/res/drawable/progressive_restart_jpeg.jpg
Binary files differ
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/res/raw/test1.obb b/tests/res/raw/test1.obb
new file mode 100644
index 0000000..a6b3e1c
--- /dev/null
+++ b/tests/res/raw/test1.obb
Binary files differ
diff --git a/tests/res/raw/test1_nosig.obb b/tests/res/raw/test1_nosig.obb
new file mode 100644
index 0000000..5c3573f
--- /dev/null
+++ b/tests/res/raw/test1_nosig.obb
Binary files differ
diff --git a/tests/res/raw/test1_wrongpackage.obb b/tests/res/raw/test1_wrongpackage.obb
new file mode 100644
index 0000000..d0aafe1
--- /dev/null
+++ b/tests/res/raw/test1_wrongpackage.obb
Binary files differ
diff --git a/tests/res/values-large/configVarying.xml b/tests/res/values-large/configVarying.xml
new file mode 100755
index 0000000..7b2df7c
--- /dev/null
+++ b/tests/res/values-large/configVarying.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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>
+    <item type="configVarying" name="simple">simple large</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag large</item>
+    </bag>
+    <item type="configVarying" name="large">large</item>
+</resources>
diff --git a/tests/res/values-normal/configVarying.xml b/tests/res/values-normal/configVarying.xml
new file mode 100755
index 0000000..b45ee49
--- /dev/null
+++ b/tests/res/values-normal/configVarying.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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>
+    <item type="configVarying" name="simple">simple normal</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag normal</item>
+    </bag>
+    <item type="configVarying" name="normal">normal</item>
+</resources>
diff --git a/tests/res/values-small/configVarying.xml b/tests/res/values-small/configVarying.xml
new file mode 100755
index 0000000..15a9f8f
--- /dev/null
+++ b/tests/res/values-small/configVarying.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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>
+    <item type="configVarying" name="simple">simple small</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag small</item>
+    </bag>
+    <item type="configVarying" name="small">small</item>
+</resources>
diff --git a/tests/res/values-xlarge/configVarying.xml b/tests/res/values-xlarge/configVarying.xml
new file mode 100755
index 0000000..fb9cad7
--- /dev/null
+++ b/tests/res/values-xlarge/configVarying.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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>
+    <item type="configVarying" name="simple">simple xlarge</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag xlarge</item>
+    </bag>
+    <item type="configVarying" name="xlarge">xlarge</item>
+</resources>
diff --git a/tests/res/values/configVarying.xml b/tests/res/values/configVarying.xml
index a2d5b97..de1b09e 100755
--- a/tests/res/values/configVarying.xml
+++ b/tests/res/values/configVarying.xml
@@ -19,4 +19,8 @@
     <bag type="configVarying" name="bag">
         <item name="testString">bag default</item>
     </bag>
+    <item type="configVarying" name="small">default</item>
+    <item type="configVarying" name="normal">default</item>
+    <item type="configVarying" name="large">default</item>
+    <item type="configVarying" name="xlarge">default</item>
 </resources>
diff --git a/tests/src/android/app/cts/ListActivityTestHelper.java b/tests/src/android/app/cts/ListActivityTestHelper.java
deleted file mode 100644
index 4e35bd8..0000000
--- a/tests/src/android/app/cts/ListActivityTestHelper.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2008 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.app.cts;
-
-import android.app.ListActivity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-import com.android.cts.stub.R;
-
-public class ListActivityTestHelper extends ListActivity {
-    public ListView listView;
-    public View view;
-    public int itemPosition;
-    public long itemId;
-    public boolean isOnContentChangedCalled = false;
-    public static boolean isOnRestoreInstanceStateCalled = false;
-    public boolean isSubActivityFinished = false;
-
-    private static final int WAIT_BEFORE_FINISH = 1;
-
-    @Override
-    public void onListItemClick(ListView l, View v, int position, long id) {
-        super.onListItemClick(l, v, position, id);
-        listView = l;
-        view = v;
-        itemPosition = position;
-        itemId = id;
-    }
-
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        setListAdapter(new ArrayAdapter<String>(this,
-                R.layout.list_activity_layout, STRING_ITEMS));
-        Intent intent = new Intent(TestedScreen.WAIT_BEFORE_FINISH);
-        intent.setClass(this, LocalScreen.class);
-        startActivityForResult(intent, WAIT_BEFORE_FINISH);
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        switch (requestCode) {
-            case WAIT_BEFORE_FINISH:
-                isSubActivityFinished = true;
-                break;
-            default:
-                break;
-        }
-    }
-
-    public static final String[] STRING_ITEMS = {
-            "Item 0", "Item 1", "Item 2", "Item 3", "Item 4", "Item 5"
-    };
-
-    @Override
-    public void onContentChanged() {
-        isOnContentChangedCalled = true;
-        super.onContentChanged();
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Bundle state) {
-        isOnRestoreInstanceStateCalled = true;
-        super.onRestoreInstanceState(state);
-    }
-
-}
diff --git a/tests/src/android/hardware/cts/CameraStubActivity.java b/tests/src/android/hardware/cts/CameraStubActivity.java
index 50bd13b..8ab7fbd 100644
--- a/tests/src/android/hardware/cts/CameraStubActivity.java
+++ b/tests/src/android/hardware/cts/CameraStubActivity.java
@@ -24,9 +24,7 @@
 import com.android.cts.stub.R;
 
 public class CameraStubActivity extends Activity {
-
-    public static SurfaceView mSurfaceView;
-
+    private SurfaceView mSurfaceView;
     private final int LAYOUT_WIDTH = 480;
     private final int LAYOUT_HEIGHT = 320;
 
@@ -43,4 +41,8 @@
         mSurfaceView.getHolder().setFixedSize(LAYOUT_WIDTH, LAYOUT_HEIGHT);
         mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
     }
+
+    public SurfaceView getSurfaceView() {
+        return mSurfaceView;
+    }
 }
diff --git a/tests/src/android/opengl/cts/EglConfigGLSurfaceView.java b/tests/src/android/opengl/cts/EglConfigGLSurfaceView.java
new file mode 100644
index 0000000..03e8d94
--- /dev/null
+++ b/tests/src/android/opengl/cts/EglConfigGLSurfaceView.java
@@ -0,0 +1,327 @@
+/*
+ * 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.opengl.cts;
+
+import android.content.Context;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * {@link GLSurfaceView} that uses the EGL configuration specified. Draws a couple frames of a red
+ * triangle and then calls the callback.
+ */
+public class EglConfigGLSurfaceView extends GLSurfaceView {
+
+    private static final String TAG = EglConfigGLSurfaceView.class.getName();
+
+    private final int mConfigId;
+
+    private final Runnable mCallback;
+
+    public EglConfigGLSurfaceView(Context context, int configId, int contextClientVersion,
+            Runnable callback) {
+        super(context);
+        mConfigId = configId;
+        mCallback = callback;
+        setEGLConfigChooser(new ConfigChooser());
+        setEGLContextClientVersion(contextClientVersion);
+        setRenderer(contextClientVersion == 1
+                ? new Renderer()
+                : new Renderer20());
+    }
+
+    private class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
+
+        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+            int[] attributeList = new int[] {
+                    EGL10.EGL_CONFIG_ID, mConfigId,
+                    EGL10.EGL_NONE
+            };
+
+            EGLConfig[] configs = new EGLConfig[1];
+            if (egl.eglChooseConfig(display, attributeList, configs, 1, new int[] {1})) {
+                // Print out the configuration since we may crash...
+                printConfig(egl, display, configs[0]);
+                return configs[0];
+            } else {
+                throw new IllegalStateException("Could not get EGL config...");
+            }
+        }
+    }
+
+    private class Renderer implements GLSurfaceView.Renderer {
+
+        private int mNumFrames;
+
+        private FloatBuffer mFloatBuffer;
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+            float[] triangleVertices = {
+                    0.0f, 1.0f, -1.0f,
+                    -1.0f, -1.0f, -1.0f,
+                    1.0f, -1.0f, -1.0f
+            };
+
+            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleVertices.length * 4);
+            byteBuffer.order(ByteOrder.nativeOrder());
+
+            mFloatBuffer = ByteBuffer.allocateDirect(triangleVertices.length * 4)
+                    .order(ByteOrder.nativeOrder())
+                    .asFloatBuffer();
+            mFloatBuffer.put(triangleVertices).position(0);
+
+            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+        }
+
+        public void onDrawFrame(GL10 gl) {
+            gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
+            gl.glColor4f(1.0f, 0, 0, 0);
+            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFloatBuffer);
+            gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 9);
+
+            if (++mNumFrames == 10) {
+                post(mCallback);
+            }
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            gl.glViewport(0, 0, width, height);
+        }
+    }
+
+    private class Renderer20 implements GLSurfaceView.Renderer {
+
+        private FloatBuffer mFloatBuffer;
+
+        private final String mVertexShader =
+            "attribute vec4 aPosition;\n" +
+            "void main() {\n" +
+            "  gl_Position = aPosition;\n" +
+            "}\n";
+
+        private final String mFragmentShader =
+            "void main() {\n" +
+            "  gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);\n" +
+            "}\n";
+
+        private int mProgram;
+
+        private int maPositionHandle;
+
+        private int mNumFrames;
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+            mProgram = createProgram(mVertexShader, mFragmentShader);
+            if (mProgram == 0) {
+                throw new RuntimeException("Could not create program");
+            }
+
+            maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
+            checkGlError("glGetAttribLocation aPosition");
+            if (maPositionHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for aPosition");
+            }
+
+            float[] triangleVertices = {
+                    0.0f, 1.0f, 0.0f,
+                    -1.0f, -1.0f, 0.0f,
+                    1.0f, -1.0f, 0.0f
+            };
+
+            mFloatBuffer = ByteBuffer.allocateDirect(triangleVertices.length * 4)
+                    .order(ByteOrder.nativeOrder())
+                    .asFloatBuffer();
+            mFloatBuffer.put(triangleVertices).position(0);
+        }
+
+        private int createProgram(String vertexSource, String fragmentSource) {
+            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+            if (vertexShader == 0) {
+                return 0;
+            }
+
+            int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+            if (pixelShader == 0) {
+                return 0;
+            }
+
+            int program = GLES20.glCreateProgram();
+            if (program != 0) {
+                GLES20.glAttachShader(program, vertexShader);
+                checkGlError("glAttachShader");
+                GLES20.glAttachShader(program, pixelShader);
+                checkGlError("glAttachShader");
+                GLES20.glLinkProgram(program);
+                int[] linkStatus = new int[1];
+                GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+                if (linkStatus[0] != GLES20.GL_TRUE) {
+                    Log.e(TAG, "Could not link program: ");
+                    Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+                    GLES20.glDeleteProgram(program);
+                    program = 0;
+                }
+            }
+            return program;
+        }
+
+        private int loadShader(int shaderType, String source) {
+            int shader = GLES20.glCreateShader(shaderType);
+            if (shader != 0) {
+                GLES20.glShaderSource(shader, source);
+                GLES20.glCompileShader(shader);
+                int[] compiled = new int[1];
+                GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+                if (compiled[0] == 0) {
+                    Log.e(TAG, "Could not compile shader " + shaderType + ":");
+                    Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
+                    GLES20.glDeleteShader(shader);
+                    shader = 0;
+                }
+            }
+            return shader;
+        }
+
+        private void checkGlError(String op) {
+            int error;
+            while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+                Log.e(TAG, op + ": glError " + error);
+                throw new RuntimeException(op + ": glError " + error);
+            }
+        }
+
+        @Override
+        public void onDrawFrame(GL10 gl) {
+            GLES20.glClearColor(0, 0, 0, 1);
+            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+            GLES20.glUseProgram(mProgram);
+
+            GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
+                    0, mFloatBuffer);
+            checkGlError("glVertexAttribPointer maPosition");
+
+            GLES20.glEnableVertexAttribArray(maPositionHandle);
+            checkGlError("glEnableVertexAttribArray maPositionHandle");
+
+            GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
+            checkGlError("glDrawArrays");
+
+            if (++mNumFrames == 10) {
+                post(mCallback);
+            }
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            GLES20.glViewport(0, 0, width, height);
+        }
+    }
+
+    /** Ripped from the NDK sample GL2JNIView class. */
+    private static void printConfig(EGL10 egl, EGLDisplay display,
+            EGLConfig config) {
+        int[] attributes = {
+                EGL10.EGL_BUFFER_SIZE,
+                EGL10.EGL_ALPHA_SIZE,
+                EGL10.EGL_BLUE_SIZE,
+                EGL10.EGL_GREEN_SIZE,
+                EGL10.EGL_RED_SIZE,
+                EGL10.EGL_DEPTH_SIZE,
+                EGL10.EGL_STENCIL_SIZE,
+                EGL10.EGL_CONFIG_CAVEAT,
+                EGL10.EGL_CONFIG_ID,
+                EGL10.EGL_LEVEL,
+                EGL10.EGL_MAX_PBUFFER_HEIGHT,
+                EGL10.EGL_MAX_PBUFFER_PIXELS,
+                EGL10.EGL_MAX_PBUFFER_WIDTH,
+                EGL10.EGL_NATIVE_RENDERABLE,
+                EGL10.EGL_NATIVE_VISUAL_ID,
+                EGL10.EGL_NATIVE_VISUAL_TYPE,
+                0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+                EGL10.EGL_SAMPLES,
+                EGL10.EGL_SAMPLE_BUFFERS,
+                EGL10.EGL_SURFACE_TYPE,
+                EGL10.EGL_TRANSPARENT_TYPE,
+                EGL10.EGL_TRANSPARENT_RED_VALUE,
+                EGL10.EGL_TRANSPARENT_GREEN_VALUE,
+                EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+                0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
+                0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+                0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
+                0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
+                EGL10.EGL_LUMINANCE_SIZE,
+                EGL10.EGL_ALPHA_MASK_SIZE,
+                EGL10.EGL_COLOR_BUFFER_TYPE,
+                EGL10.EGL_RENDERABLE_TYPE,
+                0x3042 // EGL10.EGL_CONFORMANT
+        };
+        String[] names = {
+                "EGL_BUFFER_SIZE",
+                "EGL_ALPHA_SIZE",
+                "EGL_BLUE_SIZE",
+                "EGL_GREEN_SIZE",
+                "EGL_RED_SIZE",
+                "EGL_DEPTH_SIZE",
+                "EGL_STENCIL_SIZE",
+                "EGL_CONFIG_CAVEAT",
+                "EGL_CONFIG_ID",
+                "EGL_LEVEL",
+                "EGL_MAX_PBUFFER_HEIGHT",
+                "EGL_MAX_PBUFFER_PIXELS",
+                "EGL_MAX_PBUFFER_WIDTH",
+                "EGL_NATIVE_RENDERABLE",
+                "EGL_NATIVE_VISUAL_ID",
+                "EGL_NATIVE_VISUAL_TYPE",
+                "EGL_PRESERVED_RESOURCES",
+                "EGL_SAMPLES",
+                "EGL_SAMPLE_BUFFERS",
+                "EGL_SURFACE_TYPE",
+                "EGL_TRANSPARENT_TYPE",
+                "EGL_TRANSPARENT_RED_VALUE",
+                "EGL_TRANSPARENT_GREEN_VALUE",
+                "EGL_TRANSPARENT_BLUE_VALUE",
+                "EGL_BIND_TO_TEXTURE_RGB",
+                "EGL_BIND_TO_TEXTURE_RGBA",
+                "EGL_MIN_SWAP_INTERVAL",
+                "EGL_MAX_SWAP_INTERVAL",
+                "EGL_LUMINANCE_SIZE",
+                "EGL_ALPHA_MASK_SIZE",
+                "EGL_COLOR_BUFFER_TYPE",
+                "EGL_RENDERABLE_TYPE",
+                "EGL_CONFORMANT"
+        };
+        int[] value = new int[1];
+        for (int i = 0; i < attributes.length; i++) {
+            int attribute = attributes[i];
+            String name = names[i];
+            if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
+                Log.w(TAG, String.format("  %s: %d\n", name, value[0]));
+            } else {
+                // Log.w(TAG, String.format("  %s: failed\n", name));
+                while (egl.eglGetError() != EGL10.EGL_SUCCESS);
+            }
+        }
+    }
+}
diff --git a/tests/src/android/opengl/cts/EglConfigStubActivity.java b/tests/src/android/opengl/cts/EglConfigStubActivity.java
new file mode 100644
index 0000000..ab1a6d0
--- /dev/null
+++ b/tests/src/android/opengl/cts/EglConfigStubActivity.java
@@ -0,0 +1,92 @@
+/*
+ * 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.opengl.cts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * {@link Activity} with a {@link GLSurfaceView} that chooses a specific configuration.
+ */
+public class EglConfigStubActivity extends Activity {
+
+    public static final String CONFIG_ID_EXTRA = "eglConfigId";
+
+    public static final String CONTEXT_CLIENT_VERSION_EXTRA = "eglContextClientVersion";
+
+    private EglConfigGLSurfaceView mView;
+
+    private CountDownLatch mFinishedDrawing;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        int configId = getConfigId();
+        int contextClientVersion = getContextClientVersion();
+        setTitle("EGL Config Id: " + configId + " Client Version: " + contextClientVersion);
+
+        mFinishedDrawing = new CountDownLatch(1);
+        mView = new EglConfigGLSurfaceView(this, configId, contextClientVersion, new Runnable() {
+            @Override
+            public void run() {
+                mFinishedDrawing.countDown();
+            }
+        });
+        setContentView(mView);
+    }
+
+    private int getConfigId() {
+        Intent intent = getIntent();
+        if (intent != null) {
+            return intent.getIntExtra(CONFIG_ID_EXTRA, 0);
+        } else {
+            return 0;
+        }
+    }
+
+    private int getContextClientVersion() {
+        Intent intent = getIntent();
+        if (intent != null) {
+            return intent.getIntExtra(CONTEXT_CLIENT_VERSION_EXTRA, 0);
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    public void waitToFinishDrawing() throws InterruptedException {
+        if (!mFinishedDrawing.await(3, TimeUnit.SECONDS)) {
+            throw new IllegalStateException("Coudn't finish drawing frames!");
+        }
+    }
+}
diff --git a/tests/src/android/os/cts/FileUtils.java b/tests/src/android/os/cts/FileUtils.java
new file mode 100644
index 0000000..feaf7d7
--- /dev/null
+++ b/tests/src/android/os/cts/FileUtils.java
@@ -0,0 +1,122 @@
+/*
+ * 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.os.cts;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Bits and pieces copied from hidden API of android.os.FileUtils. */
+public class FileUtils {
+
+    public static final int S_IFSOCK = 0140000;
+    public static final int S_IFLNK = 0120000;
+    public static final int S_IFREG = 0100000;
+    public static final int S_IFBLK = 0060000;
+    public static final int S_IFDIR = 0040000;
+    public static final int S_IFCHR = 0020000;
+    public static final int S_IFIFO = 0010000;
+
+    public static final int S_ISUID = 0004000;
+    public static final int S_ISGID = 0002000;
+    public static final int S_ISVTX = 0001000;
+
+    public static final int S_IRWXU = 00700;
+    public static final int S_IRUSR = 00400;
+    public static final int S_IWUSR = 00200;
+    public static final int S_IXUSR = 00100;
+
+    public static final int S_IRWXG = 00070;
+    public static final int S_IRGRP = 00040;
+    public static final int S_IWGRP = 00020;
+    public static final int S_IXGRP = 00010;
+
+    public static final int S_IRWXO = 00007;
+    public static final int S_IROTH = 00004;
+    public static final int S_IWOTH = 00002;
+    public static final int S_IXOTH = 00001;
+
+    static {
+        System.loadLibrary("cts_jni");
+    }
+
+    public static class FileStatus {
+
+        public int dev;
+        public int ino;
+        public int mode;
+        public int nlink;
+        public int uid;
+        public int gid;
+        public int rdev;
+        public long size;
+        public int blksize;
+        public long blocks;
+        public long atime;
+        public long mtime;
+        public long ctime;
+
+        public boolean hasModeFlag(int flag) {
+            return (mode & flag) == flag;
+        }
+    }
+
+    /**
+     * @param path of the file to stat
+     * @param status object to set the fields on
+     * @param statLinks or don't stat links (lstat vs stat)
+     * @return whether or not we were able to stat the file
+     */
+    public native static boolean getFileStatus(String path, FileStatus status, boolean statLinks);
+
+    public native static String getUserName(int uid);
+
+    public native static String getGroupName(int gid);
+
+    public native static int setPermissions(String file, int mode);
+
+    /**
+     * Copy data from a source stream to destFile.
+     * Return true if succeed, return false if failed.
+     */
+    public static boolean copyToFile(InputStream inputStream, File destFile) {
+        try {
+            if (destFile.exists()) {
+                destFile.delete();
+            }
+            FileOutputStream out = new FileOutputStream(destFile);
+            try {
+                byte[] buffer = new byte[4096];
+                int bytesRead;
+                while ((bytesRead = inputStream.read(buffer)) >= 0) {
+                    out.write(buffer, 0, bytesRead);
+                }
+            } finally {
+                out.flush();
+                try {
+                    out.getFD().sync();
+                } catch (IOException e) {
+                }
+                out.close();
+            }
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+}
diff --git a/tests/src/android/text/format/cts/LocaleUtils.java b/tests/src/android/text/format/cts/LocaleUtils.java
index d6001c4..131d745 100644
--- a/tests/src/android/text/format/cts/LocaleUtils.java
+++ b/tests/src/android/text/format/cts/LocaleUtils.java
@@ -22,14 +22,10 @@
 
 public class LocaleUtils {
 
-    /** Return whether or not the specified locale is available on the system. */
-    public static boolean isSupportedLocale(Context context, Locale locale) {
-        String[] locales = context.getAssets().getLocales();
-        for (String availableLocale : locales) {
-            if (locale.toString().equals(availableLocale)) {
-                return true;
-            }
-        }
-        return false;
+    /** Return whether or not the given locale is the device's current locale. */
+    public static boolean isCurrentLocale(Context context, Locale locale) {
+        // TODO: Change the locale on the device using public API if it becomes available.
+        Locale currentLocale = context.getResources().getConfiguration().locale;
+        return locale.equals(currentLocale);
     }
 }
diff --git a/tests/src/android/webkit/cts/CtsTestServer.java b/tests/src/android/webkit/cts/CtsTestServer.java
old mode 100644
new mode 100755
index 78eed43..240aaa6
--- a/tests/src/android/webkit/cts/CtsTestServer.java
+++ b/tests/src/android/webkit/cts/CtsTestServer.java
@@ -27,6 +27,7 @@
 import org.apache.http.StatusLine;
 import org.apache.http.client.utils.URLEncodedUtils;
 import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.FileEntity;
 import org.apache.http.entity.InputStreamEntity;
 import org.apache.http.entity.StringEntity;
 import org.apache.http.impl.DefaultHttpServerConnection;
@@ -38,10 +39,15 @@
 
 import android.content.Context;
 import android.content.res.AssetManager;
+import android.net.Uri;
+import android.os.Environment;
 import android.util.Log;
 import android.webkit.MimeTypeMap;
 
+import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
@@ -58,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;
 
@@ -79,8 +89,15 @@
 
     public static final String FAVICON_PATH = "/favicon.ico";
     public static final String USERAGENT_PATH = "/useragent.html";
+
+    public static final String TEST_DOWNLOAD_PATH = "/download.html";
+    private static final String DOWNLOAD_ID_PARAMETER = "downloadId";
+    private static final String NUM_BYTES_PARAMETER = "numBytes";
+
     public static final String ASSET_PREFIX = "/assets/";
     public static final String FAVICON_ASSET_PATH = ASSET_PREFIX + "webkit/favicon.png";
+    public static final String APPCACHE_PATH = "/appcache.html";
+    public static final String APPCACHE_MANIFEST_PATH = "/appcache.manifest";
     public static final String REDIRECT_PREFIX = "/redirect";
     public static final String DELAY_PREFIX = "/delayed";
     public static final String BINARY_PREFIX = "/binary";
@@ -343,11 +360,32 @@
         return sb.toString();
     }
 
-    public String getLastRequestUrl() {
+    public String getAppCacheUrl() {
+        StringBuilder sb = new StringBuilder(getBaseUri());
+        sb.append(APPCACHE_PATH);
+        return sb.toString();
+    }
+
+    /**
+     * @param downloadId used to differentiate the files created for each test
+     * @param numBytes of the content that the CTS server should send back
+     * @return url to get the file from
+     */
+    public String getTestDownloadUrl(String downloadId, int numBytes) {
+        return Uri.parse(getBaseUri())
+                .buildUpon()
+                .path(TEST_DOWNLOAD_PATH)
+                .appendQueryParameter(DOWNLOAD_ID_PARAMETER, downloadId)
+                .appendQueryParameter(NUM_BYTES_PARAMETER, Integer.toString(numBytes))
+                .build()
+                .toString();
+    }
+
+    public synchronized String getLastRequestUrl() {
         return mLastQuery;
     }
 
-    public int getRequestCount() {
+    public synchronized int getRequestCount() {
         return mRequestCount;
     }
 
@@ -356,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;
     }
 
@@ -365,21 +403,26 @@
      * 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;
     }
 
     /**
      * Generate a response to the given request.
      * @throws InterruptedException
+     * @throws IOException
      */
-    private HttpResponse getResponse(HttpRequest request) throws InterruptedException {
+    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();
@@ -499,11 +542,45 @@
             }
             response.setEntity(createEntity("<html><head><title>" + agent + "</title></head>" +
                     "<body>" + agent + "</body></html>"));
+        } else if (path.equals(TEST_DOWNLOAD_PATH)) {
+            response = createTestDownloadResponse(Uri.parse(uriString));
         } else if (path.equals(SHUTDOWN_PREFIX)) {
             response = createResponse(HttpStatus.SC_OK);
             // We cannot close the socket here, because we need to respond.
             // Status must be set to OK, or else the test will fail due to
             // a RunTimeException.
+        } else if (path.equals(APPCACHE_PATH)) {
+            response = createResponse(HttpStatus.SC_OK);
+            response.setEntity(createEntity("<!DOCTYPE HTML>" +
+                    "<html manifest=\"appcache.manifest\">" +
+                    "  <head>" +
+                    "    <title>Waiting</title>" +
+                    "    <script>" +
+                    "      function updateTitle() { document.title = \"Done\"; }" +
+                    "      window.applicationCache.onnoupdate = updateTitle;" +
+                    "      window.applicationCache.oncached = updateTitle;" +
+                    "      window.applicationCache.onupdateready = updateTitle;" +
+                    "      window.applicationCache.onobsolete = updateTitle;" +
+                    "      window.applicationCache.onerror = updateTitle;" +
+                    "    </script>" +
+                    "  </head>" +
+                    "  <body>AppCache test</body>" +
+                    "</html>"));
+        } else if (path.equals(APPCACHE_MANIFEST_PATH)) {
+            response = createResponse(HttpStatus.SC_OK);
+            try {
+                StringEntity entity = new StringEntity("CACHE MANIFEST");
+                // This entity property is not used when constructing the response, (See
+                // AbstractMessageWriter.write(), which is called by
+                // AbstractHttpServerConnection.sendResponseHeader()) so we have to set this header
+                // manually.
+                // TODO: Should we do this for all responses from this server?
+                entity.setContentType("text/cache-manifest");
+                response.setEntity(entity);
+                response.setHeader("Content-Type", "text/cache-manifest");
+            } catch (UnsupportedEncodingException e) {
+                Log.w(TAG, "Unexpected UnsupportedEncodingException");
+            }
         }
         if (response == null) {
             response = createResponse(HttpStatus.SC_NOT_FOUND);
@@ -516,15 +593,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));
     }
@@ -532,7 +611,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.
@@ -551,7 +630,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");
@@ -562,12 +641,49 @@
         return null;
     }
 
+    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))
+                : 0;
+        HttpResponse response = createResponse(HttpStatus.SC_OK);
+        response.setHeader("Content-Length", Integer.toString(numBytes));
+        response.setEntity(createFileEntity(downloadId, numBytes));
+        return response;
+    }
+
+    private static FileEntity createFileEntity(String downloadId, int numBytes) throws IOException {
+        String storageState = Environment.getExternalStorageState();
+        if (Environment.MEDIA_MOUNTED.equalsIgnoreCase(storageState)) {
+            File storageDir = Environment.getExternalStorageDirectory();
+            File file = new File(storageDir, downloadId + ".bin");
+            BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
+            byte data[] = new byte[1024];
+            for (int i = 0; i < data.length; i++) {
+                data[i] = 1;
+            }
+            try {
+                for (int i = 0; i < numBytes / data.length; i++) {
+                    stream.write(data);
+                }
+                stream.write(data, 0, numBytes % data.length);
+                stream.flush();
+            } finally {
+                stream.close();
+            }
+            return new FileEntity(file, "application/octet-stream");
+        } else {
+            throw new IllegalStateException("External storage must be mounted for this test!");
+        }
+    }
+
     private static class ServerThread extends Thread {
         private CtsTestServer mServer;
         private ServerSocket mSocket;
         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
@@ -649,12 +765,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
@@ -664,19 +781,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.
@@ -684,18 +794,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/src/android/widget/cts/WidgetTestUtils.java b/tests/src/android/widget/cts/WidgetTestUtils.java
index 0a8f6dc..2df6629 100644
--- a/tests/src/android/widget/cts/WidgetTestUtils.java
+++ b/tests/src/android/widget/cts/WidgetTestUtils.java
@@ -48,7 +48,8 @@
         }
 
         // b1 and b2 are all not null.
-        if (b1.getWidth() != b2.getWidth() || b1.getHeight() != b2.getHeight()) {
+        if (b1.getWidth() != b2.getWidth() || b1.getHeight() != b2.getHeight()
+            || b1.getConfig() != b2.getConfig()) {
             Assert.fail("the bitmaps are not equal");
         }
 
@@ -119,4 +120,19 @@
         options.inScaled = false;
         return BitmapFactory.decodeResource(resources, resId, options);
     }
+
+    /**
+     * Retrieve a dithered bitmap that can be used for comparison on any density
+     * @param resources
+     * @param config the preferred config for the returning bitmap
+     * @return the {@link Bitmap} or <code>null</code>
+     */
+    public static Bitmap getUnscaledAndDitheredBitmap(Resources resources,
+            int resId, Bitmap.Config config) {
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inDither = true;
+        options.inScaled = false;
+        options.inPreferredConfig = config;
+        return BitmapFactory.decodeResource(resources, resId, options);
+    }
 }
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
index e72e4db..c306c79 100644
--- a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
@@ -150,7 +150,9 @@
 
     private void validateOptions(Bundle expectedOptions, Bundle actualOptions) {
         if (expectedOptions == null) {
-            assertNull(actualOptions);
+            if (actualOptions != null) {
+              assertTrue(actualOptions.isEmpty());
+            }
         } else {
             assertNotNull(actualOptions);
             assertEquals(expectedOptions.get(OPTION_NAME_1), actualOptions.get(OPTION_NAME_1));
diff --git a/apps/CtsVerifier/tests/Android.mk b/tests/tests/admin/Android.mk
similarity index 85%
rename from apps/CtsVerifier/tests/Android.mk
rename to tests/tests/admin/Android.mk
index b9572fb..3228d85 100644
--- a/apps/CtsVerifier/tests/Android.mk
+++ b/tests/tests/admin/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
+# 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.
@@ -12,9 +11,9 @@
 # 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
@@ -25,9 +24,9 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CtsVerifierTests
+LOCAL_PACKAGE_NAME := CtsAdminTestCases
 
-LOCAL_INSTRUMENTATION_FOR := CtsVerifier
+LOCAL_INSTRUMENTATION_FOR := CtsDeviceAdmin
 
 LOCAL_SDK_VERSION := current
 
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/app/Android.mk b/tests/tests/app/Android.mk
index 46cd43b..a9b75df 100644
--- a/tests/tests/app/Android.mk
+++ b/tests/tests/app/Android.mk
@@ -22,7 +22,6 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := CtsTestAnnotationsLib
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/app/src/android/app/cts/AlarmManagerTest.java b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
index dc2ff92..7c03a75 100644
--- a/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
@@ -16,28 +16,19 @@
 
 package android.app.cts;
 
-import java.util.TimeZone;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.ToBeFixed;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.os.IBinder;
 import android.os.SystemClock;
-import android.app.cts.ISecondary;
 import android.test.AndroidTestCase;
 
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.TestTargets;
-import dalvik.annotation.TestStatus;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.ToBeFixed;
-
 @TestTargetClass(AlarmManager.class)
 public class AlarmManagerTest extends AndroidTestCase {
     private AlarmManager mAlarmManager;
@@ -52,11 +43,8 @@
     private long mWakeupTime;
     private MockAlarmReceiver mMockAlarmReceiver;
 
-    private Sync mSync;
-
     private final int TIME_DELTA = 200;
     private final int TIME_DELAY = 2000;
-    private ISecondary mSecondaryService = null;
 
     class Sync {
         public boolean mIsConnected;
@@ -83,79 +71,6 @@
     }
 
     @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "setTimeZone",
-        args = {java.lang.String.class}
-    )
-    @BrokenTest("Broken by CL148448. Default timezone of the test and the service differ.")
-    public void testSetTimeZone() throws Exception {
-        mSync = new Sync();
-        final String ACTION = "android.app.REMOTESERVICE";
-        mServiceIntent = new Intent(ACTION);
-        mContext.startService(mServiceIntent);
-        mContext.bindService(new Intent(ISecondary.class.getName()), mSecondaryConnection,
-                Context.BIND_AUTO_CREATE);
-
-        synchronized (mSync) {
-            if (!mSync.mIsConnected) {
-                mSync.wait();
-            }
-        }
-        final TimeZone currentZone = TimeZone.getDefault();
-
-        // test timeZone is null, timeZone won't be set
-        String timeZone = null;
-        mAlarmManager.setTimeZone(timeZone);
-        TimeZone values = TimeZone.getDefault();
-        assertEquals(currentZone.getID(), values.getID());
-        // test another process's timezone
-
-        assertEquals(currentZone.getID(), mSecondaryService.getTimeZoneID());
-
-        // nothing in timZone, timeZone won't be set
-        timeZone = "";
-        mAlarmManager.setTimeZone(timeZone);
-        values = TimeZone.getDefault();
-        assertEquals(currentZone.getID(), values.getID());
-        // test timeZone as different time zone
-        String[] timeZones = TimeZone.getAvailableIDs();
-        // set different time zone
-        timeZone = currentZone.getID().equals(timeZones[0]) ? timeZones[1] : timeZones[0];
-        mAlarmManager.setTimeZone(timeZone);
-        Thread.sleep(TIME_DELAY);
-        values = TimeZone.getDefault();
-        TimeZone zone = TimeZone.getTimeZone(timeZone);
-        assertEquals(zone.getID(), values.getID());
-
-        // test another process's timezone
-        assertEquals(zone.getID(), mSecondaryService.getTimeZoneID());
-
-        // set time zone as origin time zone
-        TimeZone.setDefault(currentZone);
-
-        mContext.stopService(mServiceIntent);
-        mServiceIntent = null;
-    }
-
-    private ServiceConnection mSecondaryConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            mSecondaryService = ISecondary.Stub.asInterface(service);
-            synchronized (mSync) {
-                mSync.mIsConnected = true;
-                mSync.notify();
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            mSecondaryService = null;
-            synchronized (mSync) {
-                mSync.mIsDisConnected = true;
-                mSync.notify();
-            }
-        }
-    };
-
-    @TestTargetNew(
         level = TestLevel.PARTIAL,
         method = "set",
         args = {int.class, long.class, android.app.PendingIntent.class}
@@ -216,7 +131,7 @@
         Thread.sleep(TIME_DELAY);
         assertTrue(mMockAlarmReceiver.alarmed);
     }
-    
+
     @TestTargetNew(
         level = TestLevel.PARTIAL,
         method = "setRepeating",
diff --git a/tests/tests/app/src/android/app/cts/DialogTest.java b/tests/tests/app/src/android/app/cts/DialogTest.java
index 28c2c20..77c55fb 100644
--- a/tests/tests/app/src/android/app/cts/DialogTest.java
+++ b/tests/tests/app/src/android/app/cts/DialogTest.java
@@ -520,24 +520,22 @@
         assertTrue(d.onKeyDownReturn);
     }
 
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "onKeyMultiple",
-        args = {int.class, int.class, android.view.KeyEvent.class}
-    )
-    public void testOnKeyMultiple() {
-        popDialog(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
-        final TestDialog d = (TestDialog) mActivity.getDialog();
+     @TestTargetNew(
+         level = TestLevel.COMPLETE,
+         method = "onKeyMultiple",
+         args = {int.class, int.class, android.view.KeyEvent.class}
+     )
+     public void testOnKeyMultiple() {
+         popDialog(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
+         final TestDialog d = (TestDialog) mActivity.getDialog();
 
-        // System call onTouchEvent
-        assertNull(d.keyMultipleEvent);
-        mInstrumentation.sendKeySync(
-                new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_UNKNOWN));
-        assertTrue(d.isOnKeyMultipleCalled);
-        assertFalse(d.onKeyMultipleReturn);
-        assertEquals(KeyEvent.KEYCODE_UNKNOWN, d.keyMultipleEvent.getKeyCode());
-        assertEquals(KeyEvent.ACTION_MULTIPLE, d.keyMultipleEvent.getAction());
-    }
+         assertNull(d.keyMultipleEvent);
+         d.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_UNKNOWN));
+         assertTrue(d.isOnKeyMultipleCalled);
+         assertFalse(d.onKeyMultipleReturn);
+         assertEquals(KeyEvent.KEYCODE_UNKNOWN, d.keyMultipleEvent.getKeyCode());
+         assertEquals(KeyEvent.ACTION_MULTIPLE, d.keyMultipleEvent.getAction());
+     }
 
     @TestTargets({
         @TestTargetNew(
diff --git a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
new file mode 100644
index 0000000..3920d58
--- /dev/null
+++ b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -0,0 +1,236 @@
+/*
+ * 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.app.cts;
+
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.test.AndroidTestCase;
+import android.view.animation.cts.DelayedCheck;
+import android.webkit.cts.CtsTestServer;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class DownloadManagerTest extends AndroidTestCase {
+
+    /**
+     * According to the CDD Section 7.6.1, the DownloadManager implementation must be able to
+     * download individual files of 55 MB.
+     */
+    private static final int MINIMUM_DOWNLOAD_BYTES = 55 * 1024 * 1024;
+
+    private DownloadManager mDownloadManager;
+
+    private CtsTestServer mWebServer;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+        mWebServer = new CtsTestServer(mContext);
+        clearDownloads();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mWebServer.shutdown();
+    }
+
+    public void testDownloadManager() throws Exception {
+        DownloadCompleteReceiver receiver =
+                new DownloadCompleteReceiver(2, TimeUnit.SECONDS.toMillis(3));
+        try {
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            long goodId = mDownloadManager.enqueue(new Request(getGoodUrl()));
+            long badId = mDownloadManager.enqueue(new Request(getBadUrl()));
+
+            int allDownloads = getTotalNumberDownloads();
+            assertEquals(2, allDownloads);
+
+            assertDownloadQueryableById(goodId);
+            assertDownloadQueryableById(badId);
+
+            receiver.waitForDownloadComplete();
+
+            assertDownloadQueryableByStatus(DownloadManager.STATUS_SUCCESSFUL);
+            assertDownloadQueryableByStatus(DownloadManager.STATUS_FAILED);
+
+            assertRemoveDownload(goodId, allDownloads - 1);
+            assertRemoveDownload(badId, allDownloads - 2);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    public void testMinimumDownload() throws Exception {
+        DownloadCompleteReceiver receiver =
+                new DownloadCompleteReceiver(1, TimeUnit.MINUTES.toMillis(2));
+        try {
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            long id = mDownloadManager.enqueue(new Request(getMinimumDownloadUrl()));
+            receiver.waitForDownloadComplete();
+
+            ParcelFileDescriptor fileDescriptor = mDownloadManager.openDownloadedFile(id);
+            assertEquals(MINIMUM_DOWNLOAD_BYTES, fileDescriptor.getStatSize());
+
+            Cursor cursor = null;
+            try {
+                cursor = mDownloadManager.query(new Query().setFilterById(id));
+                assertTrue(cursor.moveToNext());
+                assertEquals(DownloadManager.STATUS_SUCCESSFUL, cursor.getInt(
+                        cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)));
+                assertEquals(MINIMUM_DOWNLOAD_BYTES, cursor.getInt(
+                        cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)));
+                assertFalse(cursor.moveToNext());
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+
+            assertRemoveDownload(id, 0);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    private class DownloadCompleteReceiver extends BroadcastReceiver {
+
+        private final CountDownLatch mReceiveLatch;
+
+        private final long waitTimeMs;
+
+        public DownloadCompleteReceiver(int numDownload, long waitTimeMs) {
+            this.mReceiveLatch = new CountDownLatch(numDownload);
+            this.waitTimeMs = waitTimeMs;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mReceiveLatch.countDown();
+        }
+
+        public void waitForDownloadComplete() throws InterruptedException {
+            assertTrue("Make sure you have WiFi or some other connectivity for this test.",
+                    mReceiveLatch.await(waitTimeMs, TimeUnit.MILLISECONDS));
+        }
+    }
+
+    private void clearDownloads() {
+        if (getTotalNumberDownloads() > 0) {
+            Cursor cursor = null;
+            try {
+                Query query = new Query();
+                cursor = mDownloadManager.query(query);
+                int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
+                long[] removeIds = new long[cursor.getCount()];
+                for (int i = 0; cursor.moveToNext(); i++) {
+                    removeIds[i] = cursor.getLong(columnIndex);
+                }
+                assertEquals(removeIds.length, mDownloadManager.remove(removeIds));
+                assertEquals(0, getTotalNumberDownloads());
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        }
+    }
+
+    private Uri getGoodUrl() {
+        return Uri.parse(mWebServer.getTestDownloadUrl("cts-good-download", 0));
+    }
+
+    private Uri getBadUrl() {
+        return Uri.parse(mWebServer.getBaseUri() + "/nosuchurl");
+    }
+
+    private Uri getMinimumDownloadUrl() {
+        return Uri.parse(mWebServer.getTestDownloadUrl("cts-minimum-download",
+                MINIMUM_DOWNLOAD_BYTES));
+    }
+
+    private int getTotalNumberDownloads() {
+        Cursor cursor = null;
+        try {
+            Query query = new Query();
+            cursor = mDownloadManager.query(query);
+            return cursor.getCount();
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void assertDownloadQueryableById(long downloadId) {
+        Cursor cursor = null;
+        try {
+            Query query = new Query().setFilterById(downloadId);
+            cursor = mDownloadManager.query(query);
+            assertEquals(1, cursor.getCount());
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void assertDownloadQueryableByStatus(final int status) {
+        new DelayedCheck() {
+            @Override
+            protected boolean check() {
+                Cursor cursor= null;
+                try {
+                    Query query = new Query().setFilterByStatus(status);
+                    cursor = mDownloadManager.query(query);
+                    return 1 == cursor.getCount();
+                } finally {
+                    if (cursor != null) {
+                        cursor.close();
+                    }
+                }
+            }
+        }.run();
+    }
+
+    private void assertRemoveDownload(long removeId, int expectedNumDownloads) {
+        Cursor cursor = null;
+        try {
+            assertEquals(1, mDownloadManager.remove(removeId));
+            Query query = new Query();
+            cursor = mDownloadManager.query(query);
+            assertEquals(expectedNumDownloads, cursor.getCount());
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+}
diff --git a/tests/tests/app/src/android/app/cts/InstrumentationTest.java b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
index fce9972..e3eb46d 100644
--- a/tests/tests/app/src/android/app/cts/InstrumentationTest.java
+++ b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
@@ -36,10 +36,12 @@
 import android.os.IBinder;
 import android.os.SystemClock;
 import android.test.InstrumentationTestCase;
+import android.view.InputQueue;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.SurfaceHolder;
 import android.view.View;
 import android.view.Window;
 import android.view.ViewGroup.LayoutParams;
@@ -956,6 +958,14 @@
             @Override
             public void togglePanel(int featureId, KeyEvent event) {
             }
+            
+            @Override
+            public void takeSurface(SurfaceHolder.Callback2 callback) {
+            }
+            
+            @Override
+            public void takeInputQueue(InputQueue.Callback queue) {
+            }
         }
     }
 
diff --git a/tests/tests/app/src/android/app/cts/LauncherActivityTest.java b/tests/tests/app/src/android/app/cts/LauncherActivityTest.java
index b8129e6..4e5e7d4 100644
--- a/tests/tests/app/src/android/app/cts/LauncherActivityTest.java
+++ b/tests/tests/app/src/android/app/cts/LauncherActivityTest.java
@@ -16,7 +16,6 @@
 
 package android.app.cts;
 
-import dalvik.annotation.BrokenTest;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
@@ -84,7 +83,6 @@
             args = {int.class}
         )
     })
-    @BrokenTest("flaky test, assertTrue(mActivity.isOnListItemClick) intermittently fails")
     public void testLaunchActivity() {
         // Constructor of LaunchActivity can't be invoked directly.
         new LauncherActivityStub();
@@ -109,6 +107,9 @@
         // There should be an activity(but with uncertain content) in position 0.
         assertNotNull(mActivity.intentForPosition(0));
         // Test onListItemClick
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
         sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
         assertTrue(mActivity.isOnListItemClick);
     }
diff --git a/tests/tests/app/src/android/app/cts/ListActivityTest.java b/tests/tests/app/src/android/app/cts/ListActivityTest.java
deleted file mode 100644
index d3a43f9..0000000
--- a/tests/tests/app/src/android/app/cts/ListActivityTest.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2008 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.app.cts;
-
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestTargets;
-
-import android.app.ListActivity;
-import android.test.ActivityInstrumentationTestCase2;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-@TestTargetClass(ListActivity.class)
-public class ListActivityTest extends ActivityInstrumentationTestCase2<ListActivityTestHelper> {
-    private ListActivityTestHelper mStubListActivity;
-
-    public ListActivityTest() {
-        super("com.android.cts.stub", ListActivityTestHelper.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mStubListActivity = getActivity();
-        assertNotNull(mStubListActivity);
-    }
-
-    protected void waitForAction() throws InterruptedException {
-        final long TIMEOUT_MSEC = 20000;
-        final int TIME_SLICE_MSEC = 100;
-        final long endTime = System.currentTimeMillis() + TIMEOUT_MSEC;
-        while (!mStubListActivity.isSubActivityFinished && System.currentTimeMillis() < endTime) {
-            Thread.sleep(TIME_SLICE_MSEC);
-        }
-    }
-
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "onListItemClick",
-            args = {ListView.class, View.class, int.class, long.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getListView",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getSelectedItemId",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getSelectedItemPosition",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "setListAdapter",
-            args = {android.widget.ListAdapter.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "setSelection",
-            args = {int.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getListAdapter",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "onContentChanged",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "onRestoreInstanceState",
-            args = {android.os.Bundle.class}
-        )
-    })
-    @BrokenTest(value="flaky test. bug 2334738")
-    public void testListActivity() throws Throwable {
-        waitForAction();
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        getInstrumentation().waitForIdleSync();
-        // view should get created on list item click
-        assertNotNull(mStubListActivity.view);
-        String s = (String) ((TextView) mStubListActivity.view).getText();
-        int pos = mStubListActivity.itemPosition;
-        long id = mStubListActivity.itemId;
-        assertEquals(0, id);
-        assertEquals(0, pos);
-        assertEquals(ListActivityTestHelper.STRING_ITEMS[pos], s);
-        assertEquals(ListActivityTestHelper.STRING_ITEMS.length,
-                mStubListActivity.listView.getCount());
-        assertEquals(id, mStubListActivity.getSelectedItemId());
-        assertEquals(pos, mStubListActivity.getSelectedItemPosition());
-
-        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        s = (String) ((TextView) mStubListActivity.view).getText();
-        pos = mStubListActivity.itemPosition;
-        id = mStubListActivity.itemId;
-        assertEquals(1, id);
-        assertEquals(1, pos);
-        assertEquals(ListActivityTestHelper.STRING_ITEMS[pos], s);
-        assertEquals(ListActivityTestHelper.STRING_ITEMS.length,
-                mStubListActivity.listView.getCount());
-        assertEquals(id, mStubListActivity.getSelectedItemId());
-        assertEquals(pos, mStubListActivity.getSelectedItemPosition());
-
-        final int selectPos = 2;
-        assertTrue(mStubListActivity.isOnContentChangedCalled);
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                mStubListActivity.setSelection(selectPos);
-            }
-        });
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        s = (String) ((TextView) mStubListActivity.view).getText();
-        pos = mStubListActivity.itemPosition;
-        id = mStubListActivity.itemId;
-        assertEquals(ListActivityTestHelper.STRING_ITEMS[selectPos], s);
-        assertEquals(ListActivityTestHelper.STRING_ITEMS.length,
-                mStubListActivity.listView.getCount());
-        assertEquals(selectPos, id);
-        assertEquals(selectPos, pos);
-        assertEquals(selectPos, mStubListActivity.getSelectedItemId());
-        assertEquals(selectPos, mStubListActivity.getSelectedItemPosition());
-
-        final ArrayAdapter<String> arrayAdapter = (ArrayAdapter<String>)
-                mStubListActivity.getListAdapter();
-        assertNotNull(arrayAdapter);
-        final String[] str = ListActivityTestHelper.STRING_ITEMS;
-        final int len = str.length;
-        assertEquals(len, arrayAdapter.getCount());
-        for (int i = 0; i < len; i++) {
-            assertEquals(str[i], arrayAdapter.getItem(i));
-        }
-
-        assertNotNull(mStubListActivity.getListView());
-        assertEquals(arrayAdapter, mStubListActivity.getListView().getAdapter());
-        assertTrue(mStubListActivity.isOnContentChangedCalled);
-        assertFalse(ListActivityTestHelper.isOnRestoreInstanceStateCalled);
-        OrientationTestUtils.toggleOrientationSync(mStubListActivity, getInstrumentation());
-        assertTrue(ListActivityTestHelper.isOnRestoreInstanceStateCalled);
-     }
-}
diff --git a/tests/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/tests/app/src/android/app/cts/SystemFeaturesTest.java
index ac3d03a..1f7c9f8 100644
--- a/tests/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -30,9 +30,12 @@
 import android.hardware.Camera;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
+import android.hardware.Camera.CameraInfo;
 import android.hardware.Camera.Parameters;
 import android.location.LocationManager;
+import android.net.sip.SipManager;
 import android.net.wifi.WifiManager;
+import android.nfc.NfcAdapter;
 import android.telephony.TelephonyManager;
 import android.test.InstrumentationTestCase;
 
@@ -104,10 +107,39 @@
     }
 
     public void testCameraFeatures() {
+        int numCameras = Camera.getNumberOfCameras();
+        if (numCameras == 0) {
+            assertNotAvailable(PackageManager.FEATURE_CAMERA);
+            assertNotAvailable(PackageManager.FEATURE_CAMERA_AUTOFOCUS);
+            assertNotAvailable(PackageManager.FEATURE_CAMERA_FLASH);
+            assertNotAvailable(PackageManager.FEATURE_CAMERA_FRONT);
+        } else {
+            checkFrontCamera();
+            checkRearCamera();
+        }
+    }
+
+    private void checkFrontCamera() {
+        CameraInfo info = new CameraInfo();
+        int numCameras = Camera.getNumberOfCameras();
+        int frontCameraId = -1;
+        for (int i = 0; i < numCameras; i++) {
+            Camera.getCameraInfo(i, info);
+            if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
+                frontCameraId = i;
+            }
+        }
+
+        if (frontCameraId > -1) {
+            assertAvailable(PackageManager.FEATURE_CAMERA_FRONT);
+        } else {
+            assertNotAvailable(PackageManager.FEATURE_CAMERA_FRONT);
+        }
+    }
+
+    private void checkRearCamera() {
         Camera camera = null;
         try {
-            // Try getting a camera. This is unlikely to fail but implentations without a camera
-            // could return null or throw an exception.
             camera = Camera.open();
             if (camera != null) {
                 assertAvailable(PackageManager.FEATURE_CAMERA);
@@ -129,10 +161,6 @@
                 assertNotAvailable(PackageManager.FEATURE_CAMERA_AUTOFOCUS);
                 assertNotAvailable(PackageManager.FEATURE_CAMERA_FLASH);
             }
-        } catch (RuntimeException e) {
-            assertNotAvailable(PackageManager.FEATURE_CAMERA);
-            assertNotAvailable(PackageManager.FEATURE_CAMERA_AUTOFOCUS);
-            assertNotAvailable(PackageManager.FEATURE_CAMERA_FLASH);
         } finally {
             if (camera != null) {
                 camera.release();
@@ -167,6 +195,14 @@
         }
     }
 
+    public void testNfcFeatures() {
+        if (NfcAdapter.getDefaultAdapter(mContext) != null) {
+            assertAvailable(PackageManager.FEATURE_NFC);
+        } else {
+            assertNotAvailable(PackageManager.FEATURE_NFC);
+        }
+    }
+
     /**
      * Check that the sensor features reported by the PackageManager correspond to the sensors
      * returned by {@link SensorManager#getSensorList(int)}.
@@ -176,8 +212,12 @@
 
         assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_ACCELEROMETER,
                 Sensor.TYPE_ACCELEROMETER);
+        assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_BAROMETER,
+                Sensor.TYPE_PRESSURE);
         assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_COMPASS,
                 Sensor.TYPE_MAGNETIC_FIELD);
+        assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_GYROSCOPE,
+                Sensor.TYPE_GYROSCOPE);
         assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_LIGHT,
                 Sensor.TYPE_LIGHT);
         assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_PROXIMITY,
@@ -201,6 +241,29 @@
         return features;
     }
 
+    public void testSipFeatures() {
+        if (SipManager.newInstance(mContext) != null) {
+            assertAvailable(PackageManager.FEATURE_SIP);
+        } else {
+            assertNotAvailable(PackageManager.FEATURE_SIP);
+            assertNotAvailable(PackageManager.FEATURE_SIP_VOIP);
+        }
+
+        if (SipManager.isApiSupported(mContext)) {
+            assertAvailable(PackageManager.FEATURE_SIP);
+        } else {
+            assertNotAvailable(PackageManager.FEATURE_SIP);
+            assertNotAvailable(PackageManager.FEATURE_SIP_VOIP);
+        }
+
+        if (SipManager.isVoipSupported(mContext)) {
+            assertAvailable(PackageManager.FEATURE_SIP);
+            assertAvailable(PackageManager.FEATURE_SIP_VOIP);
+        } else {
+            assertNotAvailable(PackageManager.FEATURE_SIP_VOIP);
+        }
+    }
+
     /**
      * Check that if the PackageManager declares a sensor feature that the device has at least
      * one sensor that matches that feature. Also check that if a PackageManager does not declare
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
index a3032bb..796298d 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
@@ -19,30 +19,41 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothServerSocket;
+import android.content.pm.PackageManager;
+import android.test.AndroidTestCase;
 
 import java.io.IOException;
 import java.util.Set;
 import java.util.UUID;
 
-import junit.framework.TestCase;
-
 /**
  * Very basic test, just of the static methods of {@link
  * BluetoothAdapter}.
  */
-public class BasicAdapterTest extends TestCase {
+public class BasicAdapterTest extends AndroidTestCase {
     private static final int DISABLE_TIMEOUT = 5000;  // ms timeout for BT disable
     private static final int ENABLE_TIMEOUT = 10000;  // ms timeout for BT enable
     private static final int POLL_TIME = 100;         // ms to poll BT state
 
+    private boolean mHasBluetooth;
+
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_BLUETOOTH);
+    }
+
     public void test_getDefaultAdapter() {
         /*
          * Note: If the target doesn't support Bluetooth at all, then
-         * this method will return null. The assumption here is that
-         * you won't bother running this test on a target that doesn't
-         * purport to support Bluetooth.
+         * this method should return null.
          */
-        assertNotNull(BluetoothAdapter.getDefaultAdapter());
+        if (mHasBluetooth) {
+            assertNotNull(BluetoothAdapter.getDefaultAdapter());
+        } else {
+            assertNull(BluetoothAdapter.getDefaultAdapter());
+        }
     }
 
     public void test_checkBluetoothAddress() {
@@ -94,7 +105,7 @@
             "00:00:e0:00:00:00"));
         assertFalse(BluetoothAdapter.checkBluetoothAddress(
             "00:00:0f:00:00:00"));
-        
+
         assertTrue(BluetoothAdapter.checkBluetoothAddress(
             "00:00:00:00:00:00"));
         assertTrue(BluetoothAdapter.checkBluetoothAddress(
@@ -105,6 +116,10 @@
 
     /** Checks enable(), disable(), getState(), isEnabled() */
     public void test_enableDisable() {
+        if (!mHasBluetooth) {
+            // Skip the test if bluetooth is not present.
+            return;
+        }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
 
         for (int i=0; i<5; i++) {
@@ -114,6 +129,10 @@
     }
 
     public void test_getAddress() {
+        if (!mHasBluetooth) {
+            // Skip the test if bluetooth is not present.
+            return;
+        }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         enable(adapter);
 
@@ -121,6 +140,10 @@
     }
 
     public void test_getName() {
+        if (!mHasBluetooth) {
+            // Skip the test if bluetooth is not present.
+            return;
+        }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         enable(adapter);
 
@@ -129,6 +152,10 @@
     }
 
     public void test_getBondedDevices() {
+        if (!mHasBluetooth) {
+            // Skip the test if bluetooth is not present.
+            return;
+        }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         enable(adapter);
 
@@ -140,6 +167,10 @@
     }
 
     public void test_getRemoteDevice() {
+        if (!mHasBluetooth) {
+            // Skip the test if bluetooth is not present.
+            return;
+        }
         // getRemoteDevice() should work even with Bluetooth disabled
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         disable(adapter);
@@ -161,6 +192,10 @@
     }
 
     public void test_listenUsingRfcommWithServiceRecord() throws IOException {
+        if (!mHasBluetooth) {
+            // Skip the test if bluetooth is not present.
+            return;
+        }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         enable(adapter);
 
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index bd8c260..07ee067 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -25,6 +25,8 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
+import android.provider.MediaStore;
+import android.provider.Settings;
 import android.test.AndroidTestCase;
 
 import java.util.List;
@@ -171,4 +173,75 @@
             assertCanBeHandled(intent);
         }
     }
+
+    /**
+     * Test start camera by intent
+     */
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "Intent",
+        args = {java.lang.String.class}
+    )
+    public void testCamera() {
+        PackageManager packageManager = mContext.getPackageManager();
+        if (packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
+                || packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
+            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+            assertCanBeHandled(intent);
+
+            intent.setAction(MediaStore.ACTION_VIDEO_CAPTURE);
+            assertCanBeHandled(intent);
+
+            intent.setAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+            assertCanBeHandled(intent);
+
+            intent.setAction(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
+            assertCanBeHandled(intent);
+        }
+    }
+
+    public void testSettings() {
+        assertCanBeHandled(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_ADD_ACCOUNT));
+        assertCanBeHandled(new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_APN_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+                .setData(Uri.parse("package:com.android.cts.stub")));
+
+        // TODO: Uncomment in HC. This appears to be broken in Froyo and Gingerbread...
+        // assertCanBeHandled(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS));
+
+        assertCanBeHandled(new Intent(Settings.ACTION_APPLICATION_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_DATE_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_DISPLAY_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_LOCALE_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_MEMORY_CARD_SETTINGS));
+
+        // TODO: Seems to not work at on NS, Xoom, Xoom WiFi
+        // assertCanBeHandled(new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS));
+
+        assertCanBeHandled(new Intent(Settings.ACTION_PRIVACY_SETTINGS));
+
+        // TODO: Seems to not work at on NS, Xoom, Xoom WiFi
+        // assertCanBeHandled(new Intent(Settings.ACTION_QUICK_LAUNCH_SETTINGS));
+
+        assertCanBeHandled(new Intent(Settings.ACTION_SEARCH_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_SECURITY_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_SOUND_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_SYNC_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_SYSTEM_UPDATE_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_USER_DICTIONARY_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_WIFI_IP_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_WIFI_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
+    }
 }
diff --git a/tests/tests/content/src/android/content/cts/ContextTest.java b/tests/tests/content/src/android/content/cts/ContextTest.java
index 3eac36a..c2a27ae 100644
--- a/tests/tests/content/src/android/content/cts/ContextTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextTest.java
@@ -16,7 +16,14 @@
 
 package android.content.cts;
 
-import java.io.IOException;
+import com.android.cts.stub.R;
+import com.android.internal.util.XmlUtils;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+import dalvik.annotation.ToBeFixed;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -24,18 +31,12 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources.Theme;
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
 import android.util.Xml;
 
-import com.android.cts.stub.R;
-import com.android.internal.util.XmlUtils;
-
-import dalvik.annotation.TestTargets;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.ToBeFixed;
+import java.io.IOException;
 
 @TestTargetClass(Context.class)
 public class ContextTest extends AndroidTestCase {
@@ -45,6 +46,7 @@
     protected void setUp() throws Exception {
         super.setUp();
         mContext = getContext();
+        mContext.setTheme(R.style.Test_Theme);
     }
 
     @TestTargets({
@@ -109,6 +111,46 @@
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
+            method = "getTheme",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setTheme",
+            args = {int.class}
+        )
+    })
+    public void testAccessTheme() {
+        mContext.setTheme(R.style.Test_Theme);
+        final Theme testTheme = mContext.getTheme();
+        assertNotNull(testTheme);
+
+        int[] attrs = {
+            android.R.attr.windowNoTitle,
+            android.R.attr.panelColorForeground,
+            android.R.attr.panelColorBackground
+        };
+        TypedArray attrArray = null;
+        try {
+            attrArray = testTheme.obtainStyledAttributes(attrs);
+            assertTrue(attrArray.getBoolean(0, false));
+            assertEquals(0xff000000, attrArray.getColor(1, 0));
+            assertEquals(0xffffffff, attrArray.getColor(2, 0));
+        } finally {
+            if (attrArray != null) {
+                attrArray.recycle();
+                attrArray = null;
+            }
+        }
+
+        // setTheme only works for the first time
+        mContext.setTheme(android.R.style.Theme_Black);
+        assertSame(testTheme, mContext.getTheme());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
             notes = "",
             method = "obtainStyledAttributes",
             args = {int[].class}
@@ -136,7 +178,7 @@
     public void testObtainStyledAttributes() {
         // Test obtainStyledAttributes(int[])
         TypedArray testTypedArray = mContext
-                .obtainStyledAttributes(com.android.internal.R.styleable.View);
+                .obtainStyledAttributes(android.R.styleable.View);
         assertNotNull(testTypedArray);
         assertTrue(testTypedArray.length() > 2);
         assertTrue(testTypedArray.length() > 0);
@@ -144,7 +186,7 @@
 
         // Test obtainStyledAttributes(int, int[])
         testTypedArray = mContext.obtainStyledAttributes(android.R.style.TextAppearance_Small,
-                com.android.internal.R.styleable.TextAppearance);
+                android.R.styleable.TextAppearance);
         assertNotNull(testTypedArray);
         assertTrue(testTypedArray.length() > 2);
         testTypedArray.recycle();
@@ -166,14 +208,14 @@
 
         // Test obtainStyledAttributes(AttributeSet, int[])
         testTypedArray = mContext.obtainStyledAttributes(getAttributeSet(R.layout.context_layout),
-                com.android.internal.R.styleable.DatePicker);
+                android.R.styleable.DatePicker);
         assertNotNull(testTypedArray);
         assertEquals(2, testTypedArray.length());
         testTypedArray.recycle();
 
         // Test obtainStyledAttributes(AttributeSet, int[], int, int)
         testTypedArray = mContext.obtainStyledAttributes(getAttributeSet(R.layout.context_layout),
-                com.android.internal.R.styleable.DatePicker, 0, 0);
+                android.R.styleable.DatePicker, 0, 0);
         assertNotNull(testTypedArray);
         assertEquals(2, testTypedArray.length());
         testTypedArray.recycle();
diff --git a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
index ac402ef..6ad14e0 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
@@ -18,7 +18,6 @@
 
 import com.android.cts.stub.R;
 
-import dalvik.annotation.BrokenTest;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
@@ -36,8 +35,6 @@
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.res.TypedArray;
-import android.content.res.Resources.Theme;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteCursorDriver;
 import android.database.sqlite.SQLiteDatabase;
@@ -238,40 +235,6 @@
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
-            method = "getTheme",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "setTheme",
-            args = {int.class}
-        )
-    })
-    @BrokenTest("needs investigation")
-    public void testAccessTheme() {
-        mContextWrapper.setTheme(R.style.Test_Theme);
-        final Theme testTheme = mContextWrapper.getTheme();
-        assertNotNull(testTheme);
-
-        int[] attrs = {
-            android.R.attr.windowNoTitle,
-            android.R.attr.panelColorForeground,
-            android.R.attr.panelColorBackground
-        };
-        TypedArray attrArray = testTheme.obtainStyledAttributes(attrs);
-
-        assertTrue(attrArray.getBoolean(0, false));
-        assertEquals(0xff000000, attrArray.getColor(1, 0));
-        assertEquals(0xffffffff, attrArray.getColor(2, 0));
-
-        // setTheme only works for the first time
-        mContextWrapper.setTheme(android.R.style.Theme_Black);
-        assertSame(testTheme, mContextWrapper.getTheme());
-    }
-
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
             method = "registerReceiver",
             args = {BroadcastReceiver.class, IntentFilter.class}
         ),
diff --git a/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java b/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
new file mode 100644
index 0000000..bc81c4c
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2010 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.content.cts;
+
+import com.android.cts.stub.R;
+
+import dalvik.annotation.BrokenTest;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+import dalvik.annotation.ToBeFixed;
+
+import android.app.QueuedWork;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * Test {@link SharedPreferences}.
+ */
+@TestTargetClass(SharedPreferences.class)
+public class SharedPreferencesTest extends AndroidTestCase {
+    private static final String TAG = "SharedPreferencesTest";
+
+    private Context mContext;
+    private ContextWrapper mContextWrapper;
+
+    private File mPrefsFile;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getContext();
+        mContextWrapper = new ContextWrapper(mContext);
+
+        SharedPreferences prefs = getPrefs();
+        prefs.edit().clear().commit();
+
+        // Duplicated from ContextImpl.java.  Not ideal, but there wasn't a better
+        // way to reach into Context{Wrapper,Impl} to ask where this file lives.
+        mPrefsFile = new File("/data/data/com.android.cts.stub/shared_prefs",
+                              "com.android.cts.stub_preferences.xml");
+        mPrefsFile.delete();
+    }
+
+    private SharedPreferences getPrefs() {
+        return PreferenceManager.getDefaultSharedPreferences(mContext);
+    }
+
+    public void testNoFileInitially() {
+        assertFalse(mPrefsFile.exists());
+    }
+
+    public void testCommitCreatesFiles() {
+        SharedPreferences prefs = getPrefs();
+        assertFalse(mPrefsFile.exists());
+        prefs.edit().putString("foo", "bar").commit();
+        assertTrue(mPrefsFile.exists());
+    }
+
+    public void testDefaults() {
+        SharedPreferences prefs = getPrefs();
+        String key = "not-set";
+        assertFalse(prefs.contains(key));
+        assertEquals(0, prefs.getAll().size());
+        assertTrue(prefs.getAll().isEmpty());
+        assertEquals(false, prefs.getBoolean(key, false));
+        assertEquals(true, prefs.getBoolean(key, true));
+        assertEquals(0.5f, prefs.getFloat(key, 0.5f));
+        assertEquals(123, prefs.getInt(key, 123));
+        assertEquals(999L, prefs.getLong(key, 999L));
+        assertEquals("default", prefs.getString(key, "default"));
+    }
+
+    private abstract class RedundantWriteTest {
+        // Do some initial operation on editor.  No commit needed.
+        public abstract void setUp(SharedPreferences.Editor editor);
+
+        // Do some later operation on editor (e.g. a redundant edit).
+        // No commit needed.
+        public abstract void subsequentEdit(SharedPreferences.Editor editor);
+
+        public boolean expectingMutation() {
+            return false;
+        }
+
+        // Tests that a redundant edit after an initital setup doesn't
+        // result in a duplicate write-out to disk.
+        public final void test() throws Exception {
+            SharedPreferences prefs = getPrefs();
+            SharedPreferences.Editor editor;
+
+            assertFalse(mPrefsFile.exists());
+            prefs.edit().commit();
+            assertTrue(mPrefsFile.exists());
+
+            editor = prefs.edit();
+            setUp(editor);
+            editor.commit();
+            long modtimeMillis1 = mPrefsFile.lastModified();
+
+            // Wait a second and modify the preferences in a dummy,
+            // redundant way.  Wish I could inject a clock or disk mock
+            // here, but can't.  Instead relying on checking modtime of
+            // file on disk.
+            Thread.sleep(1000); // ms
+
+            editor = prefs.edit();
+            subsequentEdit(editor);
+            editor.commit();
+
+            long modtimeMillis2 = mPrefsFile.lastModified();
+            assertEquals(expectingMutation(), modtimeMillis1 != modtimeMillis2);
+        }
+    };
+
+    public void testRedundantBoolean() throws Exception {
+        new RedundantWriteTest() {
+            public void setUp(SharedPreferences.Editor editor) {
+                editor.putBoolean("foo", true);
+            }
+            public void subsequentEdit(SharedPreferences.Editor editor) {
+                editor.putBoolean("foo", true);
+            }
+        }.test();
+    }
+
+    public void testRedundantString() throws Exception {
+        new RedundantWriteTest() {
+            public void setUp(SharedPreferences.Editor editor) {
+                editor.putString("foo", "bar");
+            }
+            public void subsequentEdit(SharedPreferences.Editor editor) {
+                editor.putString("foo", "bar");
+            }
+        }.test();
+    }
+
+    public void testNonRedundantString() throws Exception {
+        new RedundantWriteTest() {
+            public void setUp(SharedPreferences.Editor editor) {
+                editor.putString("foo", "bar");
+            }
+            public void subsequentEdit(SharedPreferences.Editor editor) {
+                editor.putString("foo", "baz");
+            }
+            public boolean expectingMutation() {
+                return true;
+            }
+        }.test();
+    }
+
+    public void testRedundantClear() throws Exception {
+        new RedundantWriteTest() {
+            public void setUp(SharedPreferences.Editor editor) {
+                editor.clear();
+            }
+            public void subsequentEdit(SharedPreferences.Editor editor) {
+                editor.clear();
+            }
+        }.test();
+    }
+
+    public void testNonRedundantClear() throws Exception {
+        new RedundantWriteTest() {
+            public void setUp(SharedPreferences.Editor editor) {
+                editor.putString("foo", "bar");
+            }
+            public void subsequentEdit(SharedPreferences.Editor editor) {
+                editor.clear();
+            }
+            public boolean expectingMutation() {
+                return true;
+            }
+        }.test();
+    }
+
+    public void testRedundantRemove() throws Exception {
+        new RedundantWriteTest() {
+            public void setUp(SharedPreferences.Editor editor) {
+                editor.putString("foo", "bar");
+            }
+            public void subsequentEdit(SharedPreferences.Editor editor) {
+                editor.remove("not-exist-key");
+            }
+        }.test();
+    }
+
+    public void testRedundantCommitWritesFileIfNotAlreadyExisting() {
+        SharedPreferences prefs = getPrefs();
+        assertFalse(mPrefsFile.exists());
+        prefs.edit().putString("foo", "bar").commit();
+        assertTrue(mPrefsFile.exists());
+
+        // Delete the file out from under it.  (not sure why this
+        // would happen in practice, but perhaps the app did it for
+        // some reason...)
+        mPrefsFile.delete();
+
+        // And verify that a redundant edit (which would otherwise not
+        // write do disk), still does write to disk if the file isn't
+        // there.
+        prefs.edit().putString("foo", "bar").commit();
+        assertTrue(mPrefsFile.exists());
+    }
+
+    public void testTorture() {
+        Map<String, String> expectedMap = new HashMap<String, String>();
+        Random rand = new Random();
+
+        SharedPreferences prefs = mContext.getSharedPreferences("torture", Context.MODE_PRIVATE);
+        prefs.edit().clear().commit();
+
+        for (int i = 0; i < 100; i++) {
+            prefs = mContext.getSharedPreferences("torture", Context.MODE_PRIVATE);
+            assertEquals(expectedMap, prefs.getAll());
+
+            String key = new Integer(rand.nextInt(25)).toString();
+            String value = new Integer(i).toString();
+            SharedPreferences.Editor editor = prefs.edit();
+
+            if (rand.nextInt(100) < 85) {
+                Log.d(TAG, "Setting " + key + "=" + value);
+                editor.putString(key, value);
+                expectedMap.put(key, value);
+            } else {
+                Log.d(TAG, "Removing " + key);
+                editor.remove(key);
+                expectedMap.remove(key);
+            }
+
+            // Use apply on most, but commit some too.
+            if (rand.nextInt(100) < 85) {
+                Log.d(TAG, "apply.");
+                editor.apply();
+            } else {
+                Log.d(TAG, "commit.");
+                editor.commit();
+            }
+
+            assertEquals(expectedMap, prefs.getAll());
+        }
+    }
+
+    // Checks that an in-memory commit doesn't mutate a data structure
+    // still being used while writing out to disk.
+    public void testTorture2() {
+        Random rand = new Random();
+
+        for (int fi = 0; fi < 100; fi++) {
+            String prefsName = "torture_" + fi;
+            SharedPreferences prefs = mContext.getSharedPreferences(prefsName, Context.MODE_PRIVATE);
+            prefs.edit().clear().commit();
+            Map<String, String> expectedMap = new HashMap<String, String>();
+
+            for (int applies = 0; applies < 3; applies++) {
+                SharedPreferences.Editor editor = prefs.edit();
+                for (int n = 0; n < 1000; n++) {
+                    String key = new Integer(rand.nextInt(25)).toString();
+                    String value = new Integer(n).toString();
+                    editor.putString(key, value);
+                    expectedMap.put(key, value);
+                }
+                editor.apply();
+            }
+            QueuedWork.waitToFinish();
+
+            String clonePrefsName = prefsName + "_clone";
+            File prefsFile = mContext.getSharedPrefsFile(prefsName);
+            File prefsFileClone = mContext.getSharedPrefsFile(clonePrefsName);
+            prefsFileClone.delete();
+            try {
+                FileOutputStream fos = new FileOutputStream(prefsFileClone);
+                FileInputStream fis = new FileInputStream(prefsFile);
+                byte buf[] = new byte[1024];
+                int n;
+                while ((n = fis.read(buf)) > 0) {
+                    fos.write(buf, 0, n);
+                }
+                fis.close();
+                fos.close();
+            } catch (IOException e) {
+            }
+
+            SharedPreferences clonePrefs = mContext.getSharedPreferences(
+                clonePrefsName, Context.MODE_PRIVATE);
+            assertEquals(expectedMap, clonePrefs.getAll());
+
+            prefsFile.delete();
+            prefsFileClone.delete();
+        }
+    }
+
+}
diff --git a/tests/tests/content/src/android/content/res/cts/ConfigTest.java b/tests/tests/content/src/android/content/res/cts/ConfigTest.java
index 276cb35..d68fd19 100755
--- a/tests/tests/content/src/android/content/res/cts/ConfigTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ConfigTest.java
@@ -43,7 +43,8 @@
         ORIENTATION,
         WIDTH,
         HEIGHT,
-        DENSITY
+        DENSITY,
+        SCREENLAYOUT
     }
 
     private static void checkValue(final Resources res, final int resId,
@@ -129,9 +130,11 @@
                     break;
                 case DENSITY:
                     // this is the ratio from the standard
-
                     mMetrics.density = (((float)value)/((float)DisplayMetrics.DENSITY_DEFAULT));
                     break;
+                case SCREENLAYOUT:
+                    mConfig.screenLayout = value;
+                    break;
                 default:
                     assert(false);
                     break;
@@ -303,6 +306,34 @@
         checkValue(res, R.configVarying.simple, "simple square");
         checkValue(res, R.configVarying.bag,
                 R.styleable.TestConfig, new String[]{"bag square"});
+
+        config = new TotalConfig();
+        config.setProperty(Properties.SCREENLAYOUT, Configuration.SCREENLAYOUT_SIZE_SMALL);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple small");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag small"});
+
+        config = new TotalConfig();
+        config.setProperty(Properties.SCREENLAYOUT, Configuration.SCREENLAYOUT_SIZE_NORMAL);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple normal");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag normal"});
+
+        config = new TotalConfig();
+        config.setProperty(Properties.SCREENLAYOUT, Configuration.SCREENLAYOUT_SIZE_LARGE);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple large");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag large"});
+
+        config = new TotalConfig();
+        config.setProperty(Properties.SCREENLAYOUT, Configuration.SCREENLAYOUT_SIZE_XLARGE);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple xlarge");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag xlarge"});
     }
     
     @MediumTest
@@ -378,6 +409,47 @@
                 R.styleable.TestConfig, new String[]{"bag 240dpi"});
     }
 
+    @MediumTest
+    public void testScreenSize() throws Exception {
+        // ensure that we fall back to the best available screen size
+        // for a given configuration.
+        TotalConfig config = new TotalConfig();
+        config.setProperty(Properties.SCREENLAYOUT, Configuration.SCREENLAYOUT_SIZE_SMALL);
+        Resources res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple small");
+        checkValue(res, R.configVarying.small, "small");
+        checkValue(res, R.configVarying.normal, "default");
+        checkValue(res, R.configVarying.large, "default");
+        checkValue(res, R.configVarying.xlarge, "default");
+        
+        config = new TotalConfig();
+        config.setProperty(Properties.SCREENLAYOUT, Configuration.SCREENLAYOUT_SIZE_NORMAL);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple normal");
+        checkValue(res, R.configVarying.small, "default");
+        checkValue(res, R.configVarying.normal, "normal");
+        checkValue(res, R.configVarying.large, "default");
+        checkValue(res, R.configVarying.xlarge, "default");
+        
+        config = new TotalConfig();
+        config.setProperty(Properties.SCREENLAYOUT, Configuration.SCREENLAYOUT_SIZE_LARGE);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple large");
+        checkValue(res, R.configVarying.small, "default");
+        checkValue(res, R.configVarying.normal, "normal");
+        checkValue(res, R.configVarying.large, "large");
+        checkValue(res, R.configVarying.xlarge, "default");
+        
+        config = new TotalConfig();
+        config.setProperty(Properties.SCREENLAYOUT, Configuration.SCREENLAYOUT_SIZE_XLARGE);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple xlarge");
+        checkValue(res, R.configVarying.small, "default");
+        checkValue(res, R.configVarying.normal, "normal");
+        checkValue(res, R.configVarying.large, "large");
+        checkValue(res, R.configVarying.xlarge, "xlarge");
+    }
+
 // TODO - add tests for special cases - ie, other key params seem ignored if 
 // nokeys is set
 
@@ -421,7 +493,8 @@
          */
 
         /**
-         * Precidence order: mcc, mnc, locale, orientation, density,
+         * Precidence order: mcc, mnc, locale, screenlayout-size,
+         * screenlayout-long, orientation, density,
          * touchscreen, hidden, keyboard, navigation, width-height
          */
 
diff --git a/tests/tests/database/src/android/database/cts/DatabaseCursorTest.java b/tests/tests/database/src/android/database/cts/DatabaseCursorTest.java
index 9969f40..4d0c6ac 100644
--- a/tests/tests/database/src/android/database/cts/DatabaseCursorTest.java
+++ b/tests/tests/database/src/android/database/cts/DatabaseCursorTest.java
@@ -415,11 +415,17 @@
 
     @LargeTest
     public void testManyRowsLong() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
-
+        mDatabase.beginTransaction();
         final int count = 9000;
-        for (int i = 0; i < count; i++) {
-            mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");");
+        try {
+            mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+
+            for (int i = 0; i < count; i++) {
+                mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");");
+            }
+            mDatabase.setTransactionSuccessful();
+        } finally {
+            mDatabase.endTransaction();
         }
 
         Cursor testCursor = getTestCursor(mDatabase.query("test", new String[] { "data" },
diff --git a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
index a014ac6..624f35a 100644
--- a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
+++ b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
@@ -17,240 +17,37 @@
 package android.dpi.cts;
 
 import android.content.Context;
-import android.content.res.Configuration;
 import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
 import android.view.Display;
 import android.view.WindowManager;
-import android.util.DisplayMetrics;
-
-import java.lang.Integer;
-import java.util.EnumSet;
 
 /**
- * This is verifying that the device under test is running a supported
- * resolution, and is being classified as the right Screen Layout
- * Size.
+ * Test for verifying a device's screen configuration.
  */
 public class ConfigurationTest extends AndroidTestCase {
 
-    private enum Density {
-        // It is important to keep these sorted
-        INVALID_LOW(Integer.MIN_VALUE, 99),
-        LOW (100, 140),
-        MEDIUM (141, 190),
-        HIGH (191, 250),
-        INVALID_HIGH(251, Integer.MAX_VALUE);
+    public void testScreenConfiguration() {
+        WindowManager windowManager =
+            (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+        Display display = windowManager.getDefaultDisplay();
+        DisplayMetrics metrics = new DisplayMetrics();
+        display.getMetrics(metrics);
 
-        private int low;
-        private int high;
+        double xInches = (double) metrics.widthPixels / metrics.xdpi;
+        double yInches = (double) metrics.heightPixels / metrics.ydpi;
+        double diagonalInches = Math.sqrt(Math.pow(xInches, 2) + Math.pow(yInches, 2));
+        assertTrue("Screen diagonal must be at least 2.5 inches: " + diagonalInches,
+                diagonalInches >= 2.5d);
 
-        Density(int low, int high) {
-            this.low = low;
-            this.high = high;
-        }
+        double density = 160.0d * metrics.density;
+        assertTrue("Screen density must be at least 100 dpi: " + density, density >= 100.0d);
 
-        public static Density findDensity(int value) {
-            Density density = INVALID_LOW;
-            for (Density d : EnumSet.range(Density.INVALID_LOW, Density.INVALID_HIGH)) {
-                if (value >= d.low && value <= d.high) {
-                    density = d;
-                    break;
-                }
-            }
-            return density;
-        }
-    };
-
-    /**
-     * Holds information on the current active screen's configuration.
-     */
-    private static class ActiveScreenConfiguration {
-        private final int width;
-        private final int height;
-        private final Density density;
-
-        /**
-         * Create a new ActiveScreenConfiguration.
-         *
-         * @param width the width of the screen
-         * @param height the height of the screen
-         * @param density the scaling factor for DIP from standard
-         * density (160.0)
-         */
-        public ActiveScreenConfiguration(int width,
-                                         int height,
-                                         float density) {
-            // 160 DIP is the "standard" density
-            this(width, height, Density.findDensity((int) (160.0f * density)));
-        }
-
-        protected ActiveScreenConfiguration(int width,
-                                            int height,
-                                            Density density) {
-            this.width = width;
-            this.height = height;
-            this.density = density;
-        }
-
-        public Density getDensity() {
-            return density;
-        }
-
-        public int getWidth() {
-            return width;
-        }
-
-        public int getHeight() {
-            return height;
-        }
-    }
-
-    private static class ScreenConfiguration extends ActiveScreenConfiguration {
-        private final int screenLayout;
-        private final boolean isWide;
-
-        public ScreenConfiguration(int width,
-                                   int height,
-                                   Density density,
-                                   int screenLayout,
-                                   boolean isWide) {
-            super(width, height, density);
-            this.screenLayout = screenLayout;
-            this.isWide = isWide;
-        }
-
-        public ScreenConfiguration(int width,
-                                   int height,
-                                   Density density,
-                                   int screenLayout) {
-            this(width, height, density, screenLayout, false);
-        }
-
-        public int getScreenLayout() {
-            return screenLayout;
-        }
-
-        public boolean isWide() {
-            return isWide;
-        }
-    };
-
-    private static boolean areConfigsEqual(ActiveScreenConfiguration active,
-                                           ScreenConfiguration screenConfig) {
-        if (screenConfig.isWide()) {
-            // For widescreen configs, the height is fixed but the
-            // width only specifies a minimum.  But since the device
-            // can be both landscape and portrait, we have to search
-            // for which way it is.
-            if (active.getHeight() == screenConfig.getHeight()) {
-                // active height matches config height.  Make sure
-                // that the active width is at least the config width.
-                return active.getWidth() >= screenConfig.getWidth();
-            } else if (active.getWidth() == screenConfig.getHeight()) {
-                // directions are swapped
-                return active.getHeight() >= screenConfig.getWidth();
-            } else {
-                return false;
-            }
-        } else {
-            if (active.getWidth() == screenConfig.getWidth() &&
-                active.getHeight() == screenConfig.getHeight() &&
-                active.getDensity().equals(screenConfig.getDensity())) {
-                return true;
-            }
-            // It is also possible that the device is in landscape
-            // mode, which flips the active w/h.
-            if (active.getHeight() == screenConfig.getWidth() &&
-                active.getWidth() == screenConfig.getHeight() &&
-                active.getDensity().equals(screenConfig.getDensity())) {
-                return true;
-            }
-            // nope.
-            return false;
-        }
-    }
-
-    /**
-     * Here's the current configuration table:
-     *
-     * Resoluion | Density          | Size
-     * QVGA      | low (100-140)    | small
-     * WQVGA     | low (100-140)    | normal
-     * HVGA      | medium (141-190) | normal
-     * WVGA      | high (191-250)   | normal
-     * FWVGA     | high (191-250)   | normal
-     * QHD       | high (191-250)   | normal
-     * WSVGA     | high (191-250)   | large
-
-     * VGA       | medium (141-190) | large
-     * WVGA      | medium (141-190) | large
-     * FWVGA     | medium (141-190) | large
-     *
-     * Any changes to allow additional resolutions will need to update this table
-     */
-
-    private static final ScreenConfiguration[] SUPPORTED_SCREEN_CONFIGS = {
-        // QVGA      | low (100-140)    | small
-        new ScreenConfiguration(240, 320, Density.LOW, Configuration.SCREENLAYOUT_SIZE_SMALL),
-        // WQVGA     | low (100-140)    | normal
-        new ScreenConfiguration(240, 400, Density.LOW, Configuration.SCREENLAYOUT_SIZE_NORMAL),
-        // HVGA      | medium (141-190) | normal
-        new ScreenConfiguration(480, 320, Density.MEDIUM, Configuration.SCREENLAYOUT_SIZE_NORMAL),
-        new ScreenConfiguration(640, 240, Density.MEDIUM, Configuration.SCREENLAYOUT_SIZE_NORMAL),
-        // WVGA      | high (191-250)   | normal
-        new ScreenConfiguration(640, 480, Density.HIGH, Configuration.SCREENLAYOUT_SIZE_NORMAL, true),
-        // FWVGA     | high (191-250)   | normal
-        new ScreenConfiguration(864, 480, Density.HIGH, Configuration.SCREENLAYOUT_SIZE_NORMAL),
-        // QHD     | high (191-250)   | normal
-        new ScreenConfiguration(960, 540, Density.HIGH, Configuration.SCREENLAYOUT_SIZE_NORMAL),
-        // WSVGA     | high (191-250)   | large
-        new ScreenConfiguration(1024, 600, Density.HIGH, Configuration.SCREENLAYOUT_SIZE_LARGE),
-        // WSVGA     | high (191-250)   | normal
-        new ScreenConfiguration(1024, 600, Density.HIGH, Configuration.SCREENLAYOUT_SIZE_NORMAL),
-
-        // VGA       | medium (141-190) | large
-        new ScreenConfiguration(640, 480, Density.MEDIUM, Configuration.SCREENLAYOUT_SIZE_LARGE),
-        // WVGA      | medium (141-190) | large
-        new ScreenConfiguration(640, 480, Density.MEDIUM, Configuration.SCREENLAYOUT_SIZE_LARGE, true),
-        // FWVGA     | medium (141-190) | large
-        new ScreenConfiguration(864, 480, Density.MEDIUM, Configuration.SCREENLAYOUT_SIZE_LARGE),
-
-    };
-
-    private ActiveScreenConfiguration getCurrentScreenConfig() {
-        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
-        Display display = wm.getDefaultDisplay();
-        DisplayMetrics dm = new DisplayMetrics();
-        display.getMetrics(dm);
-        return new ActiveScreenConfiguration(display.getWidth(),
-                                             display.getHeight(),
-                                             dm.density);
-    }
-
-    /**
-     * Get the current screen configuration, make sure it is a
-     * supported screen configuration and that the screenlayout size
-     * is being set correctly according to the compatibility
-     * definition.
-     */
-    public void testScreenLayoutSize() {
-        ActiveScreenConfiguration currentScreenConfig = getCurrentScreenConfig();
-        // Make sure we have a valid density for the current screent.
-        assertFalse(Density.INVALID_LOW.equals(currentScreenConfig.getDensity()));
-        assertFalse(Density.INVALID_HIGH.equals(currentScreenConfig.getDensity()));
-
-        // Look up the ScreenConfig in the supported table and make
-        // sure we find a match.
-        for (ScreenConfiguration screenConfig: SUPPORTED_SCREEN_CONFIGS) {
-            if (areConfigsEqual(currentScreenConfig, screenConfig)) {
-                Configuration config = getContext().getResources().getConfiguration();
-                int size = config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
-                if (screenConfig.getScreenLayout() == size) {
-                    // we have a match, this is a supported device.
-                    return;
-                }
-            }
-        }
-        fail("Current screen configuration is not supported.");
+        int max = Math.max(metrics.widthPixels, metrics.heightPixels);
+        int min = Math.min(metrics.widthPixels, metrics.heightPixels);
+        boolean format16x9 = Math.floor(max * 9.0d / 16.0d) <= min;
+        boolean format4x3 = Math.ceil(max * 3.0d / 4.0d) >= min;
+        assertTrue("Aspect ratio must be between 4:3 and 16:9. It was " + max + ":" + min,
+                format4x3 && format16x9);
     }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
old mode 100644
new mode 100755
index 4c6259c..6a836e0
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
@@ -30,6 +30,7 @@
 import android.os.ParcelFileDescriptor;
 import android.test.InstrumentationTestCase;
 import android.util.DisplayMetrics;
+import android.util.TypedValue;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -107,6 +108,24 @@
 
     @TestTargetNew(
         level = TestLevel.COMPLETE,
+        method = "decodeResourceStream",
+        args = {android.content.res.Resources.class, android.util.TypedValue.class,
+                java.io.InputStream.class, android.graphics.Rect.class,
+                android.graphics.BitmapFactory.Options.class}
+    )
+    public void testDecodeResourceStream() {
+        InputStream is = obtainInputStream();
+        Rect r = new Rect(1, 1, 1, 1);
+        TypedValue value = new TypedValue();
+        Bitmap b = BitmapFactory.decodeResourceStream(mRes, value, is, r, mOpt1);
+        assertNotNull(b);
+        // Test the bitmap size
+        assertEquals(START_HEIGHT, b.getHeight());
+        assertEquals(START_WIDTH, b.getWidth());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
         method = "decodeByteArray",
         args = {byte[].class, int.class, int.class, android.graphics.BitmapFactory.Options.class}
     )
@@ -174,7 +193,8 @@
                 android.graphics.BitmapFactory.Options.class}
     )
     public void testDecodeFileDescriptor1() throws IOException {
-        FileDescriptor input = obtainDescriptor(obtainPath());
+        ParcelFileDescriptor pfd = obtainParcelDescriptor(obtainPath());
+        FileDescriptor input = pfd.getFileDescriptor();
         Rect r = new Rect(1, 1, 1, 1);
         Bitmap b = BitmapFactory.decodeFileDescriptor(input, r, mOpt1);
         assertNotNull(b);
@@ -191,7 +211,8 @@
         args = {java.io.FileDescriptor.class}
     )
     public void testDecodeFileDescriptor2() throws IOException {
-        FileDescriptor input = obtainDescriptor(obtainPath());
+        ParcelFileDescriptor pfd = obtainParcelDescriptor(obtainPath());
+        FileDescriptor input = pfd.getFileDescriptor();
         Bitmap b = BitmapFactory.decodeFileDescriptor(input);
         assertNotNull(b);
         // Test the bitmap size
@@ -240,10 +261,11 @@
         return mRes.openRawResource(R.drawable.start);
     }
 
-    private FileDescriptor obtainDescriptor(String path) throws IOException {
-      File file = new File(path);
-      return(ParcelFileDescriptor.open(file,
-              ParcelFileDescriptor.MODE_READ_ONLY).getFileDescriptor());
+    private ParcelFileDescriptor obtainParcelDescriptor(String path)
+            throws IOException {
+        File file = new File(path);
+        return(ParcelFileDescriptor.open(file,
+                ParcelFileDescriptor.MODE_READ_ONLY));
     }
 
     private String obtainPath() throws IOException {
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
new file mode 100755
index 0000000..abbba4b
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2010 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.graphics.cts;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import com.android.cts.stub.R;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+@TestTargetClass(BitmapRegionDecoder.class)
+public class BitmapRegionDecoderTest extends InstrumentationTestCase {
+    private static final String TAG = "BitmapRegionDecoderTest";
+    private ArrayList<File> mFilesCreated = new ArrayList<File>(
+            NAMES_TEMP_FILES.length);
+
+    private Resources mRes;
+
+    // The test images, including baseline JPEGs and progressive JPEGs, a PNG,
+    // a GIF and a BMP.
+    private static int[] RES_IDS = new int[] {
+            R.drawable.baseline_jpeg, R.drawable.progressive_jpeg,
+            R.drawable.baseline_restart_jpeg,
+            R.drawable.progressive_restart_jpeg,
+            R.drawable.png_test, R.drawable.gif_test, R.drawable.bmp_test
+    };
+    private static String[] NAMES_TEMP_FILES = new String[] {
+        "baseline_temp.jpg", "progressive_temp.jpg", "baseline_restart_temp.jpg"
+        , "progressive_restart_temp.jpg", "png_temp.png", "gif_temp.gif",
+        "bmp_temp.bmp"
+    };
+
+    // The width and height of the above image.
+    // -1 denotes that the image format is not supported by BitmapRegionDecoder
+    private static int WIDTHS[] = new int[] {
+            1280, 1280, 1280, 1280, 640, -1, -1};
+    private static int HEIGHTS[] = new int[] {960, 960, 960, 960, 480, -1, -1};
+
+    // The number of test images, format of which is supported by BitmapRegionDecoder
+    private static int NUM_TEST_IMAGES = 5;
+
+    private static int TILE_SIZE = 256;
+
+    // Configurations for BitmapFactory.Options
+    private static Config[] COLOR_CONFIGS = new Config[] {Config.ARGB_8888,
+            Config.RGB_565};
+    private static int[] SAMPLESIZES = new int[] {1, 4};
+
+    private int[] mExpectedColors = new int [TILE_SIZE * TILE_SIZE];
+    private int[] mActualColors = new int [TILE_SIZE * TILE_SIZE];
+
+    // We allow a certain degree of discrepancy between the tile-based decoding
+    // result and the regular decoding result, because the two decoders may have
+    // different implementations. The allowable discrepancy is set to a mean
+    // square error of 3 * (1 * 1) among the RGB values.
+    private int mMseMargin = 3 * (1 * 1);
+
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mRes = getInstrumentation().getTargetContext().getResources();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        for (File file : mFilesCreated) {
+            file.delete();
+        }
+        super.tearDown();
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "newInstance",
+        args = {java.lang.String.class, boolean.class}
+    )
+    public void testNewInstanceInputStream() throws IOException {
+        for (int i = 0; i < RES_IDS.length; ++i) {
+            InputStream is = obtainInputStream(RES_IDS[i]);
+            try {
+                BitmapRegionDecoder decoder =
+                        BitmapRegionDecoder.newInstance(is, false);
+                assertEquals(WIDTHS[i], decoder.getWidth());
+                assertEquals(HEIGHTS[i], decoder.getHeight());
+            } catch (IOException e) {
+                assertEquals(WIDTHS[i], -1);
+                assertEquals(HEIGHTS[i], -1);
+            } finally {
+                if (is != null) {
+                    is.close();
+                }
+            }
+        }
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "newInstance",
+            args = {byte[].class, int.class, int.class, boolean.class}
+    )
+    public void testNewInstanceByteArray() throws IOException {
+        for (int i = 0; i < RES_IDS.length; ++i) {
+            byte[] imageData = obtainByteArray(RES_IDS[i]);
+            try {
+                BitmapRegionDecoder decoder = BitmapRegionDecoder
+                        .newInstance(imageData, 0, imageData.length, false);
+                assertEquals(WIDTHS[i], decoder.getWidth());
+                assertEquals(HEIGHTS[i], decoder.getHeight());
+            } catch (IOException e) {
+                assertEquals(WIDTHS[i], -1);
+                assertEquals(HEIGHTS[i], -1);
+            }
+        }
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "newInstance",
+            args = {java.io.FileDescriptor.class, boolean.class}
+    )
+    public void testNewInstanceStringAndFileDescriptor() throws IOException {
+        for (int i = 0; i < RES_IDS.length; ++i) {
+            String filepath = obtainPath(i);
+            ParcelFileDescriptor pfd = obtainParcelDescriptor(filepath);
+            FileDescriptor fd = pfd.getFileDescriptor();
+            try {
+                BitmapRegionDecoder decoder1 =
+                        BitmapRegionDecoder.newInstance(filepath, false);
+                assertEquals(WIDTHS[i], decoder1.getWidth());
+                assertEquals(HEIGHTS[i], decoder1.getHeight());
+
+                BitmapRegionDecoder decoder2 =
+                        BitmapRegionDecoder.newInstance(fd, false);
+                assertEquals(WIDTHS[i], decoder2.getWidth());
+                assertEquals(HEIGHTS[i], decoder2.getHeight());
+            } catch (IOException e) {
+                assertEquals(WIDTHS[i], -1);
+                assertEquals(HEIGHTS[i], -1);
+            }
+        }
+    }
+
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            method = "decodeRegion",
+            args = {android.graphics.Rect.class, android.graphics.BitmapFactory.Options.class}
+    )
+    public void testDecodeRegionInputStream() throws IOException {
+        Options opts = new BitmapFactory.Options();
+        for (int i = 0; i < NUM_TEST_IMAGES; ++i) {
+            for (int j = 0; j < SAMPLESIZES.length; ++j) {
+                for (int k = 0; k < COLOR_CONFIGS.length; ++k) {
+                    opts.inSampleSize = SAMPLESIZES[j];
+                    opts.inPreferredConfig = COLOR_CONFIGS[k];
+
+                    InputStream is1 = obtainInputStream(RES_IDS[i]);
+                    BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is1, false);
+                    InputStream is2 = obtainInputStream(RES_IDS[i]);
+                    Bitmap wholeImage = BitmapFactory.decodeStream(is2, null, opts);
+
+                    compareRegionByRegion(decoder, opts, wholeImage);
+                    wholeImage.recycle();
+                }
+            }
+        }
+    }
+
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            method = "decodeRegion",
+            args = {android.graphics.Rect.class, android.graphics.BitmapFactory.Options.class}
+    )
+    public void testDecodeRegionByteArray() throws IOException {
+        Options opts = new BitmapFactory.Options();
+        for (int i = 0; i < NUM_TEST_IMAGES; ++i) {
+            for (int j = 0; j < SAMPLESIZES.length; ++j) {
+                for (int k = 0; k < COLOR_CONFIGS.length; ++k) {
+                    opts.inSampleSize = SAMPLESIZES[j];
+                    opts.inPreferredConfig = COLOR_CONFIGS[k];
+
+                    byte[] imageData = obtainByteArray(RES_IDS[i]);
+                    BitmapRegionDecoder decoder = BitmapRegionDecoder
+                            .newInstance(imageData, 0, imageData.length, false);
+                    Bitmap wholeImage = BitmapFactory.decodeByteArray(imageData,
+                            0, imageData.length, opts);
+
+                    compareRegionByRegion(decoder, opts, wholeImage);
+                    wholeImage.recycle();
+                }
+            }
+        }
+    }
+
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            method = "decodeRegion",
+            args = {android.graphics.Rect.class, android.graphics.BitmapFactory.Options.class}
+    )
+    public void testDecodeRegionStringAndFileDescriptor() throws IOException {
+        Options opts = new BitmapFactory.Options();
+        for (int i = 0; i < NUM_TEST_IMAGES; ++i) {
+            String filepath = obtainPath(i);
+            for (int j = 0; j < SAMPLESIZES.length; ++j) {
+                for (int k = 0; k < COLOR_CONFIGS.length; ++k) {
+                    opts.inSampleSize = SAMPLESIZES[j];
+                    opts.inPreferredConfig = COLOR_CONFIGS[k];
+
+                    BitmapRegionDecoder decoder =
+                        BitmapRegionDecoder.newInstance(filepath, false);
+                    Bitmap wholeImage = BitmapFactory.decodeFile(filepath, opts);
+                    compareRegionByRegion(decoder, opts, wholeImage);
+
+                    ParcelFileDescriptor pfd1 = obtainParcelDescriptor(filepath);
+                    FileDescriptor fd1 = pfd1.getFileDescriptor();
+                    decoder = BitmapRegionDecoder.newInstance(fd1, false);
+                    ParcelFileDescriptor pfd2 = obtainParcelDescriptor(filepath);
+                    FileDescriptor fd2 = pfd2.getFileDescriptor();
+                    compareRegionByRegion(decoder, opts, wholeImage);
+                    wholeImage.recycle();
+                }
+            }
+        }
+    }
+
+    @TestTargets ({
+        @TestTargetNew(
+                level = TestLevel.COMPLETE,
+                method = "recycle",
+                args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isRecycled",
+            args = {}
+        )
+    })
+    public void testRecycle() throws IOException {
+        InputStream is = obtainInputStream(RES_IDS[0]);
+        BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
+        decoder.recycle();
+        assertTrue(decoder.isRecycled());
+        try {
+            decoder.getWidth();
+            fail("Should throw an exception!");
+        } catch (Exception e) {
+        }
+
+        try {
+            decoder.getHeight();
+            fail("Should throw an exception!");
+        } catch (Exception e) {
+        }
+
+        Rect rect = new Rect(0, 0, WIDTHS[0], HEIGHTS[0]);
+        BitmapFactory.Options opts = new BitmapFactory.Options();
+        try {
+            decoder.decodeRegion(rect, opts);
+            fail("Should throw an exception!");
+        } catch (Exception e) {
+        }
+    }
+
+    private void compareRegionByRegion(BitmapRegionDecoder decoder,
+            Options opts, Bitmap wholeImage) {
+        int width = decoder.getWidth();
+        int height = decoder.getHeight();
+        Rect rect = new Rect(0, 0, width, height);
+        int numCols = (width + TILE_SIZE - 1) / TILE_SIZE;
+        int numRows = (height + TILE_SIZE - 1) / TILE_SIZE;
+        Bitmap actual;
+        Bitmap expected;
+
+        for (int i = 0; i < numCols; ++i) {
+            for (int j = 0; j < numRows; ++j) {
+                Rect rect1 = new Rect(i * TILE_SIZE, j * TILE_SIZE,
+                        (i + 1) * TILE_SIZE, (j + 1) * TILE_SIZE);
+                rect1.intersect(rect);
+                actual = decoder.decodeRegion(rect1, opts);
+                int left = rect1.left / opts.inSampleSize;
+                int top = rect1.top / opts.inSampleSize;
+                Rect rect2 = new Rect(left, top, left + actual.getWidth(),
+                        top + actual.getHeight());
+                expected = cropBitmap(wholeImage, rect2);
+                compareBitmaps(expected, actual, mMseMargin, true);
+                actual.recycle();
+                expected.recycle();
+            }
+        }
+    }
+
+    private Bitmap cropBitmap(Bitmap wholeImage, Rect rect) {
+        Bitmap cropped = Bitmap.createBitmap(rect.width(), rect.height(),
+                wholeImage.getConfig());
+        Canvas canvas = new Canvas(cropped);
+        Rect dst = new Rect(0, 0, rect.width(), rect.height());
+        canvas.drawBitmap(wholeImage, rect, dst, null);
+        return cropped;
+    }
+
+    private InputStream obtainInputStream(int resId) {
+        return mRes.openRawResource(resId);
+    }
+
+    private byte[] obtainByteArray(int resId) throws IOException {
+        InputStream is = obtainInputStream(resId);
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int readLength;
+        while ((readLength = is.read(buffer)) != -1) {
+            os.write(buffer, 0, readLength);
+        }
+        byte[] data = os.toByteArray();
+        is.close();
+        os.close();
+        return data;
+    }
+
+    private String obtainPath(int idx) throws IOException {
+        File dir = getInstrumentation().getTargetContext().getFilesDir();
+        dir.mkdirs();
+        File file = new File(dir, NAMES_TEMP_FILES[idx]);
+        InputStream is = obtainInputStream(RES_IDS[idx]);
+        FileOutputStream fOutput = new FileOutputStream(file);
+        mFilesCreated.add(file);
+        byte[] dataBuffer = new byte[1024];
+        int readLength = 0;
+        while ((readLength = is.read(dataBuffer)) != -1) {
+            fOutput.write(dataBuffer, 0, readLength);
+        }
+        is.close();
+        fOutput.close();
+        return (file.getPath());
+    }
+
+    private ParcelFileDescriptor obtainParcelDescriptor(String path)
+            throws IOException {
+        File file = new File(path);
+        return(ParcelFileDescriptor.open(file,
+                ParcelFileDescriptor.MODE_READ_ONLY));
+    }
+
+
+    // Compare expected to actual to see if their diff is less then mseMargin.
+    // lessThanMargin is to indicate whether we expect the diff to be
+    // "less than" or "no less than".
+    private void compareBitmaps(Bitmap expected, Bitmap actual,
+            int mseMargin, boolean lessThanMargin) {
+        assertEquals("mismatching widths", expected.getWidth(),
+                actual.getWidth());
+        assertEquals("mismatching heights", expected.getHeight(),
+                actual.getHeight());
+
+        double mse = 0;
+        int width = expected.getWidth();
+        int height = expected.getHeight();
+        int[] expectedColors;
+        int[] actualColors;
+        if (width == TILE_SIZE && height == TILE_SIZE) {
+            expectedColors = mExpectedColors;
+            actualColors = mActualColors;
+        } else {
+            expectedColors = new int [width * height];
+            actualColors = new int [width * height];
+        }
+
+        expected.getPixels(expectedColors, 0, width, 0, 0, width, height);
+        actual.getPixels(actualColors, 0, width, 0, 0, width, height);
+
+        for (int row = 0; row < height; ++row) {
+            for (int col = 0; col < width; ++col) {
+                int idx = row * width + col;
+                mse += distance(expectedColors[idx], actualColors[idx]);
+            }
+        }
+        mse /= width * height;
+
+        if (lessThanMargin) {
+            assertTrue("MSE too large for normal case: " + mse,
+                    mse <= mseMargin);
+        } else {
+            assertFalse("MSE too small for abnormal case: " + mse,
+                    mse <= mseMargin);
+        }
+    }
+
+    private double distance(int exp, int actual) {
+        int r = Color.red(actual) - Color.red(exp);
+        int g = Color.green(actual) - Color.green(exp);
+        int b = Color.blue(actual) - Color.blue(exp);
+        return r * r + g * g + b * b;
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index f3bfcaf..f8d3ee9 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -15,24 +15,6 @@
  */
 package android.graphics.cts;
 
-import java.io.ByteArrayOutputStream;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.IntBuffer;
-import java.nio.ShortBuffer;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.Bitmap.Config;
-import android.os.Parcel;
-import android.test.AndroidTestCase;
-import android.widget.cts.WidgetTestUtils;
-
 import com.android.cts.stub.R;
 
 import dalvik.annotation.TestLevel;
@@ -40,9 +22,28 @@
 import dalvik.annotation.TestTargetNew;
 import dalvik.annotation.TestTargets;
 
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.Bitmap.Config;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+import android.widget.cts.WidgetTestUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
 @TestTargetClass(Bitmap.class)
 public class BitmapTest extends AndroidTestCase {
-    private static final int BUFFER_SIZE = 1016;
     private Resources mRes;
     private Bitmap mBitmap;
     private BitmapFactory.Options mOptions;
@@ -133,49 +134,51 @@
         )
     })
     public void testCopyPixelsToBuffer(){
+        final int pixSize = mBitmap.getRowBytes() * mBitmap.getHeight();
+        final int tooSmall = pixSize / 2;
+
         // abnormal case: unsupported Buffer subclass
         try{
-            mBitmap.copyPixelsToBuffer(CharBuffer.allocate(BUFFER_SIZE));
+            mBitmap.copyPixelsToBuffer(CharBuffer.allocate(pixSize));
             fail("shouldn't come to here");
         }catch(RuntimeException e1){
         }
 
         // abnormal case: Buffer not large enough for pixels
         try{
-            mBitmap.copyPixelsToBuffer(ByteBuffer.allocate(BUFFER_SIZE));
+            mBitmap.copyPixelsToBuffer(ByteBuffer.allocate(tooSmall));
             fail("shouldn't come to here");
         }catch(RuntimeException e2){
         }
 
         // normal case
-        long pixSize = mBitmap.getRowBytes() * mBitmap.getHeight();
-        ByteBuffer byteBuf = ByteBuffer.allocate(101608);
+        ByteBuffer byteBuf = ByteBuffer.allocate(pixSize);
         assertEquals(0, byteBuf.position());
         mBitmap.copyPixelsToBuffer(byteBuf);
         assertEquals(pixSize, byteBuf.position());
 
         // abnormal case: Buffer not large enough for pixels
         try{
-            mBitmap.copyPixelsToBuffer(ByteBuffer.allocate(16));
+            mBitmap.copyPixelsToBuffer(ByteBuffer.allocate(tooSmall));
             fail("shouldn't come to here");
         }catch(RuntimeException e3){
         }
 
         // normal case
-        ShortBuffer shortBuf = ShortBuffer.allocate(BUFFER_SIZE);
+        ShortBuffer shortBuf = ShortBuffer.allocate(pixSize);
         assertEquals(0, shortBuf.position());
         mBitmap.copyPixelsToBuffer(shortBuf);
         assertEquals(pixSize >> 1, shortBuf.position());
 
         // abnormal case: Buffer not large enough for pixels
         try{
-            mBitmap.copyPixelsToBuffer(ByteBuffer.allocate(16));
+            mBitmap.copyPixelsToBuffer(ByteBuffer.allocate(tooSmall));
             fail("shouldn't come to here");
         }catch(RuntimeException e4){
         }
 
         // normal case
-        IntBuffer intBuf1 = IntBuffer.allocate(BUFFER_SIZE);
+        IntBuffer intBuf1 = IntBuffer.allocate(pixSize);
         assertEquals(0, intBuf1.position());
         mBitmap.copyPixelsToBuffer(intBuf1);
         assertEquals(pixSize >> 2, intBuf1.position());
@@ -183,7 +186,7 @@
         Bitmap bitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),
                 mBitmap.getConfig());
         bitmap.copyPixelsFromBuffer(intBuf1);
-        IntBuffer intBuf2 = IntBuffer.allocate(BUFFER_SIZE);
+        IntBuffer intBuf2 = IntBuffer.allocate(pixSize);
         bitmap.copyPixelsToBuffer(intBuf2);
 
         assertEquals(intBuf1.position(), intBuf2.position());
@@ -787,6 +790,98 @@
         mBitmap.equals(Bitmap.CREATOR.createFromParcel(p));
     }
 
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getScaledHeight",
+            args = {int.class}
+    )
+    public void testGetScaledHeight1() {
+        int dummyDensity = 5;
+        Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
+        int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), dummyDensity);
+        assertNotNull(ret);
+        assertEquals(scaledHeight, ret.getScaledHeight(dummyDensity));
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getScaledHeight",
+            args = {android.util.DisplayMetrics.class}
+    )
+    public void testGetScaledHeight2() {
+        Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
+        DisplayMetrics metrics = new DisplayMetrics();
+        metrics = getContext().getResources().getDisplayMetrics();
+        int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), metrics.densityDpi);
+        assertEquals(scaledHeight, ret.getScaledHeight(metrics));
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getScaledHeight",
+            args = {android.graphics.Canvas.class}
+    )
+    public void testGetScaledHeight3() {
+        Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
+        Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
+        Canvas mCanvas = new Canvas(mMutableBitmap);
+        // set Density
+        mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH);
+        int scaledHeight = scaleFromDensity(
+                ret.getHeight(), ret.getDensity(), mCanvas.getDensity());
+        assertEquals(scaledHeight, ret.getScaledHeight(mCanvas));
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getScaledWidth",
+            args = {android.graphics.Canvas.class}
+    )
+    public void testGetScaledWidth1() {
+        int dummyDensity = 5;
+        Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
+        int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), dummyDensity);
+        assertNotNull(ret);
+        assertEquals(scaledWidth, ret.getScaledWidth(dummyDensity));
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getScaledWidth",
+            args = {android.util.DisplayMetrics.class}
+    )
+    public void testGetScaledWidth2() {
+        Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
+        DisplayMetrics metrics = new DisplayMetrics();
+        metrics = getContext().getResources().getDisplayMetrics();
+        int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), metrics.densityDpi);
+        assertEquals(scaledWidth, ret.getScaledWidth(metrics));
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getScaledWidth",
+            args = {android.graphics.Canvas.class}
+    )
+    public void testGetScaledWidth3() {
+        Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
+        Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
+        Canvas mCanvas = new Canvas(mMutableBitmap);
+        // set Density
+        mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH);
+        int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(),  mCanvas.getDensity());
+        assertEquals(scaledWidth, ret.getScaledWidth(mCanvas));
+    }
+
+    private int scaleFromDensity(int size, int sdensity, int tdensity) {
+        if (sdensity == Bitmap.DENSITY_NONE || sdensity == tdensity) {
+            return size;
+        }
+
+        // Scale by tdensity / sdensity, rounding up.
+        return ((size * tdensity) + (sdensity >> 1)) / sdensity;
+    }
+
     private int[] createColors(int size){
         int[] colors = new int[size];
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index 07612d4..5d3a174 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -21,6 +21,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
+import android.util.DisplayMetrics;
 import android.graphics.DrawFilter;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -1144,7 +1145,7 @@
         args = {float.class, float.class, float.class, float.class}
     )
     public void testClipRect6() {
-        assertTrue(mCanvas.clipRect(0, 0, 10, 31));
+        assertTrue(mCanvas.clipRect(0.5f, 0.5f, 10.5f, 31.5f));
     }
 
     @TestTargetNew(
@@ -2253,6 +2254,28 @@
         mCanvas.drawPicture(p, dst);
     }
 
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setDensity",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getDensity",
+            args = {android.util.DisplayMetrics.class}
+        )
+    })
+    public void testDensity() {
+        // set Density
+        mCanvas.setDensity(DisplayMetrics.DENSITY_DEFAULT);
+        assertEquals(DisplayMetrics.DENSITY_DEFAULT, mCanvas.getDensity());
+
+        // set Density
+        mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH);
+        assertEquals(DisplayMetrics.DENSITY_HIGH, mCanvas.getDensity());
+    }
+
     private void preCompare() {
         final float[] values = new float[FLOAT_ARRAY_LEN];
         mCanvas.getMatrix().getValues(values);
diff --git a/tests/tests/graphics/src/android/graphics/cts/Interpolator_ResultTest.java b/tests/tests/graphics/src/android/graphics/cts/Interpolator_ResultTest.java
index 3c79581..82419db 100644
--- a/tests/tests/graphics/src/android/graphics/cts/Interpolator_ResultTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/Interpolator_ResultTest.java
@@ -18,6 +18,8 @@
 
 import junit.framework.TestCase;
 import android.graphics.Interpolator.Result;
+import android.graphics.Shader.TileMode;
+
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
@@ -31,8 +33,9 @@
         args = {java.lang.String.class}
     )
     public void testValueOf() {
-        // this enum type is tested in
-        // android.graphics.InterpolatorTest#testTimeToValues1()
+        assertEquals(Result.FREEZE_START, Result.valueOf("FREEZE_START"));
+        assertEquals(Result.FREEZE_END, Result.valueOf("FREEZE_END"));
+        assertEquals(Result.NORMAL, Result.valueOf("NORMAL"));
     }
 
     @TestTargetNew(
@@ -41,8 +44,10 @@
         args = {}
     )
     public void testValues() {
-        // this enum type is tested in
-        // android.graphics.InterpolatorTest#testTimeToValues1()
+        Result[] result = Result.values();
+        assertEquals(3, result.length);
+        assertEquals(Result.NORMAL, result[0]);
+        assertEquals(Result.FREEZE_START, result[1]);
+        assertEquals(Result.FREEZE_END, result[2]);
     }
-
-}
+}
\ No newline at end of file
diff --git a/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java b/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java
index d69dd4f..d2b9af0 100644
--- a/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java
@@ -34,7 +34,6 @@
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
 
 @TestTargetClass(NinePatch.class)
 public class NinePatchTest extends AndroidTestCase {
@@ -174,7 +173,6 @@
         method = "hasAlpha",
         args = {}
     )
-    @Suppress // Suppressed for current release
     public void testHasAlpha() {
         assertFalse(mNinePatch.hasAlpha());
         assertEquals(mNinePatch.hasAlpha(), mBitmap.hasAlpha());
@@ -209,6 +207,18 @@
 
     @TestTargetNew(
         level = TestLevel.COMPLETE,
+        method = "getDensity",
+        args = {}
+    )
+    public void testGetDensity() {
+        mBitmap.setDensity(11);
+        assertEquals(11, mNinePatch.getDensity());
+        mNinePatch = new NinePatch(mBitmap, mChunk, NAME);
+        assertEquals(mNinePatch.getDensity(), mBitmap.getDensity());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
         method = "getTransparentRegion",
         args = {android.graphics.Rect.class}
     )
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintFlagsDrawFilterTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintFlagsDrawFilterTest.java
old mode 100644
new mode 100755
index bf39818..b394d28
--- a/tests/tests/graphics/src/android/graphics/cts/PaintFlagsDrawFilterTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintFlagsDrawFilterTest.java
@@ -112,7 +112,7 @@
         // underline is at least one pixel high
         assertTrue(rect.top <= rect.bottom);
         // underline is roughly the same length at the text (5% tolerance)
-        assertEquals(mTextWidth, rect.right - rect.left, mTextWidth * 0.05);
+        assertEquals(mTextWidth, rect.right - rect.left, mTextWidth * 0.053);
         // underline is under the text or at least at the bottom of it
         assertTrue(rect.top >= TEXT_Y);
     }
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
index 42e557c..20701cb 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
@@ -16,13 +16,17 @@
 
 package android.graphics.cts;
 
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
 import android.graphics.ColorFilter;
 import android.graphics.MaskFilter;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.PathEffect;
 import android.graphics.Rasterizer;
-import android.graphics.Rect;
 import android.graphics.Shader;
 import android.graphics.Typeface;
 import android.graphics.Xfermode;
@@ -31,14 +35,7 @@
 import android.graphics.Paint.Join;
 import android.graphics.Paint.Style;
 import android.test.AndroidTestCase;
-import android.text.SpannableString;
-import android.text.SpannableStringBuilder;
 import android.text.SpannedString;
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestTargets;
 
 @TestTargetClass(Paint.class)
 public class PaintTest extends AndroidTestCase {
@@ -69,225 +66,85 @@
         new Paint(p);
     }
 
-    @TestTargetNew(
-        level = TestLevel.TODO,
-        method = "breakText",
-        args = {char[].class, int.class, int.class, float.class, float[].class}
-    )
-    @BrokenTest("unknown if hardcoded values being checked are correct")
-    public void testBreakText1() {
+    public void testBreakText() {
+        String text = "HIJKLMN";
+        char[] textChars = text.toCharArray();
+        SpannedString textSpan = new SpannedString(text);
+
         Paint p = new Paint();
+        float[] widths = new float[text.length()];
+        assertEquals(text.length(), p.getTextWidths(text, widths));
 
-        char[] chars = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
-        float[] width = {8.0f, 4.0f, 3.0f, 7.0f, 6.0f, 10.0f, 9.0f};
-        float[] f = new float[1];
-
-        for (int i = 0; i < chars.length; i++) {
-            assertEquals(1, p.breakText(chars, i, 1, 20.0f, f));
-            assertEquals(width[i], f[0]);
+        float totalWidth = 0.0f;
+        for (int i = 0; i < text.length(); i++) {
+            totalWidth += widths[i];
         }
 
-        // start from 'H'
-        int indexH = 0;
-        assertEquals(4, p.breakText(chars, indexH, 4, 30.0f, f));
-        assertEquals(22.0f, f[0]);
-        assertEquals(3, p.breakText(chars, indexH, 3, 30.0f, f));
-        assertEquals(15.0f, f[0]);
-        assertEquals(2, p.breakText(chars, indexH, 2, 30.0f, f));
-        assertEquals(12.0f, f[0]);
-        assertEquals(1, p.breakText(chars, indexH, 1, 30.0f, f));
-        assertEquals(8.0f, f[0]);
-        assertEquals(0, p.breakText(chars, indexH, 0, 30.0f, f));
-        assertEquals(0.0f, f[0]);
-
-        assertEquals(1, p.breakText(chars, indexH + 2, 1, 30.0f, f));
-        assertEquals(3.0f, f[0]);
-        assertEquals(1, p.breakText(chars, indexH + 2, -1, 30.0f, f));
-        assertEquals(3.0f, f[0]);
-
-        assertEquals(1, p.breakText(chars, indexH, -1, 30.0f, f));
-        assertEquals(8.0f, f[0]);
-        assertEquals(2, p.breakText(chars, indexH, -2, 30.0f, f));
-        assertEquals(12.0f, f[0]);
-        assertEquals(3, p.breakText(chars, indexH, -3, 30.0f, f));
-        assertEquals(15.0f, f[0]);
-        assertEquals(4, p.breakText(chars, indexH, -4, 30.0f, f));
-        assertEquals(22.0f, f[0]);
-
-        assertEquals(7, p.breakText(chars, indexH, 7, 50.0f, f));
-        assertEquals(47.0f, f[0]);
-        assertEquals(6, p.breakText(chars, indexH, 7, 40.0f, f));
-        assertEquals(38.0f, f[0]);
-
-        assertEquals(7, p.breakText(chars, indexH, -7, 50.0f, null));
-        assertEquals(7, p.breakText(chars, indexH, 7, 50.0f, null));
-
-        try {
-            p.breakText(chars, 0, 8, 60.0f, null);
-            fail("Should throw an ArrayIndexOutOfboundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
-        try {
-            p.breakText(chars, -1, 7, 50.0f, null);
-            fail("Should throw an ArrayIndexOutOfboundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
+        float[] measured = new float[1];
+        for (int i = 0; i < text.length(); i++) {
+            assertBreakText(text, textChars, textSpan, i, i + 1, true, totalWidth, 1, widths[i]);
         }
 
+        // Measure empty string
+        assertBreakText(text, textChars, textSpan, 0, 0, true, totalWidth, 0, 0);
+
+        // Measure substring from front: "HIJ"
+        assertBreakText(text, textChars, textSpan, 0, 3, true, totalWidth,
+                3, widths[0] + widths[1] + widths[2]);
+
+        // Reverse measure substring from front: "HIJ"
+        assertBreakText(text, textChars, textSpan, 0, 3, false, totalWidth,
+                3, widths[0] + widths[1] + widths[2]);
+
+        // Measure substring from back: "MN"
+        assertBreakText(text, textChars, textSpan, 5, 7, false, totalWidth,
+                2, widths[5] + widths[6]);
+
+        // Reverse measure substring from back: "MN"
+        assertBreakText(text, textChars, textSpan, 5, 7, false, totalWidth,
+                2, widths[5] + widths[6]);
+
+        // Measure substring in the middle: "JKL"
+        assertBreakText(text, textChars, textSpan, 2, 5, true, totalWidth,
+                3, widths[2] + widths[3] + widths[4]);
+
+        // Reverse measure substring in the middle: "JKL"
+        assertBreakText(text, textChars, textSpan, 2, 5, false, totalWidth,
+                3, widths[2] + widths[3] + widths[4]);
+
+        // Measure substring in the middle and restrict width to the first 2 characters.
+        assertBreakText(text, textChars, textSpan, 2, 5, true, widths[2] + widths[3],
+                2, widths[2] + widths[3]);
+
+        // Reverse measure substring in the middle and restrict width to the last 2 characters.
+        assertBreakText(text, textChars, textSpan, 2, 5, false, widths[3] + widths[4],
+                2, widths[3] + widths[4]);
     }
 
-    @TestTargetNew(
-        level = TestLevel.TODO,
-        method = "breakText",
-        args = {java.lang.CharSequence.class, int.class, int.class, boolean.class, float.class,
-                float[].class}
-    )
-    @BrokenTest("unknown if hardcoded values being checked are correct")
-    public void testBreakText2() {
+    private void assertBreakText(String text, char[] textChars, SpannedString textSpan,
+            int start, int end, boolean measureForwards, float maxWidth, int expectedCount,
+            float expectedWidth) {
         Paint p = new Paint();
-        String string = "HIJKLMN";
-        float[] width = {8.0f, 4.0f, 3.0f, 7.0f, 6.0f, 10.0f, 9.0f};
-        float[] f = new float[1];
 
-        assertEquals(7, p.breakText(string, 0, 7, true, 50.0f, f));
-        assertEquals(47.0f, f[0]);
-        assertEquals(6, p.breakText(string, 0, 7, true, 40.0f, f));
-        assertEquals(38.0f, f[0]);
-        assertEquals(7, p.breakText(string, 0, 7, false, 50.0f, f));
-        assertEquals(47.0f, f[0]);
-
-        for (int i = 0; i < string.length(); i++) {
-            assertEquals(1, p.breakText(string, i, i + 1, true, 20.0f, f));
-            assertEquals(width[i], f[0]);
+        int count = end - start;
+        if (!measureForwards) {
+            count = -count;
         }
 
-        assertEquals(4, p.breakText(string, 0, 4, true, 30.0f, f));
-        assertEquals(22.0f, f[0]);
-        assertEquals(3, p.breakText(string, 0, 3, true, 30.0f, f));
-        assertEquals(15.0f, f[0]);
-        assertEquals(2, p.breakText(string, 0, 2, true, 30.0f, f));
-        assertEquals(12.0f, f[0]);
-        assertEquals(1, p.breakText(string, 0, 1, true, 30.0f, f));
-        assertEquals(8.0f, f[0]);
-        assertEquals(0, p.breakText(string, 0, 0, true, 30.0f, f));
-        assertEquals(0.0f, f[0]);
+        float[][] measured = new float[][] {
+            new float[1],
+            new float[1],
+            new float[1]
+        };
+        String textSlice = text.substring(start, end);
+        assertEquals(expectedCount, p.breakText(textSlice, measureForwards, maxWidth, measured[0]));
+        assertEquals(expectedCount, p.breakText(textChars, start, count, maxWidth, measured[1]));
+        assertEquals(expectedCount, p.breakText(textSpan, start, end, measureForwards, maxWidth,
+                measured[2]));
 
-        assertEquals(1, p.breakText(string, 2, 3, true, 30.0f, f));
-        assertEquals(3.0f, f[0]);
-        assertEquals(1, p.breakText(string, 2, 3, false, 30.0f, f));
-        assertEquals(3.0f, f[0]);
-
-        assertEquals(1, p.breakText(string, 0, 1, true, 30.0f, f));
-        assertEquals(8.0f, f[0]);
-        assertEquals(2, p.breakText(string, 0, 2, true, 30.0f, f));
-        assertEquals(12.0f, f[0]);
-        assertEquals(3, p.breakText(string, 0, 3, true, 30.0f, f));
-        assertEquals(15.0f, f[0]);
-        assertEquals(4, p.breakText(string, 0, 4, true, 30.0f, f));
-        assertEquals(22.0f, f[0]);
-
-        assertEquals(7, p.breakText(string, 0, 7, true, 50.0f, f));
-        assertEquals(47.0f, f[0]);
-        assertEquals(6, p.breakText(string, 0, 7, true, 40.0f, f));
-        assertEquals(38.0f, f[0]);
-
-        assertEquals(7, p.breakText(string, 0, 7, false, 50.0f, null));
-        assertEquals(7, p.breakText(string, 0, 7, true, 50.0f, null));
-
-        try {
-            p.breakText(string, 0, 8, true, 60.0f, null);
-            fail("Should throw an StringIndexOutOfboundsException");
-        } catch (StringIndexOutOfBoundsException e) {
-            //except here
+        for (int i = 0; i < measured.length; i++) {
+            assertEquals("i: " + i, expectedWidth, measured[i][0]);
         }
-        try {
-            p.breakText(string, -1, 7, true, 50.0f, null);
-            fail("Should throw an StringIndexOutOfboundsException");
-        } catch (StringIndexOutOfBoundsException e) {
-            //except here
-        }
-        try {
-            p.breakText(string, 1, -7, true, 50.0f, null);
-            fail("Should throw an StringIndexOutOfboundsException");
-        } catch (StringIndexOutOfBoundsException e) {
-            //except here
-        }
-        try {
-            p.breakText(string, 7, 1, true, 50.0f, null);
-            fail("Should throw an StringIndexOutOfboundsException");
-        } catch (StringIndexOutOfBoundsException e) {
-            //except here
-        }
-
-    }
-
-    @TestTargetNew(
-        level = TestLevel.TODO,
-        method = "breakText",
-        args = {java.lang.String.class, boolean.class, float.class, float[].class}
-    )
-    @BrokenTest("unknown if hardcoded values being checked are correct")
-    public void testBreakText3() {
-        Paint p = new Paint();
-        String string = "HIJKLMN";
-        float[] width = {8.0f, 4.0f, 3.0f, 7.0f, 6.0f, 10.0f, 9.0f};
-        float[] f = new float[1];
-
-        for (int i = 0; i < string.length(); i++) {
-            assertEquals(1, p.breakText(string.substring(i, i+1), true, 20.0f, f));
-            assertEquals(width[i], f[0]);
-            assertEquals(1, p.breakText(string.substring(i, i+1), false, 20.0f, f));
-            assertEquals(width[i], f[0]);
-        }
-
-        assertEquals(0, p.breakText("", false, 20.0f, f));
-        assertEquals(0.0f, f[0]);
-
-        assertEquals(7, p.breakText(string, true, 50.0f, f));
-        assertEquals(47.0f, f[0]);
-        assertEquals(7, p.breakText(string, false, 50.0f, f));
-        assertEquals(47.0f, f[0]);
-        assertEquals(6, p.breakText(string, false, 40.0f, f));
-        assertEquals(39.0f, f[0]);
-        assertEquals(5, p.breakText(string, false, 35.0f, f));
-        assertEquals(35.0f, f[0]);
-        assertEquals(4, p.breakText(string, false, 33.0f, f));
-        assertEquals(32.0f, f[0]);
-        assertEquals(3, p.breakText(string, false, 25.0f, f));
-        assertEquals(25.0f, f[0]);
-        assertEquals(2, p.breakText(string, false, 20.0f, f));
-        assertEquals(19.0f, f[0]);
-        assertEquals(1, p.breakText(string, false, 13.0f, f));
-        assertEquals(9.0f, f[0]);
-        assertEquals(0, p.breakText(string, false, 3.0f, f));
-        assertEquals(0.0f, f[0]);
-
-        assertEquals(7, p.breakText(string, true, 50.0f, f));
-        assertEquals(47.0f, f[0]);
-        assertEquals(6, p.breakText(string, true, 40.0f, f));
-        assertEquals(38.0f, f[0]);
-        assertEquals(5, p.breakText(string, true, 35.0f, f));
-        assertEquals(28.0f, f[0]);
-        assertEquals(4, p.breakText(string, true, 25.0f, f));
-        assertEquals(22.0f, f[0]);
-        assertEquals(3, p.breakText(string, true, 20.0f, f));
-        assertEquals(15.0f, f[0]);
-        assertEquals(2, p.breakText(string, true, 12.0f, f));
-        assertEquals(12.0f, f[0]);
-        assertEquals(1, p.breakText(string, true, 10.0f, f));
-        assertEquals(8.0f, f[0]);
-        assertEquals(0, p.breakText(string, true, 3.0f, f));
-        assertEquals(0.0f, f[0]);
-
-        assertEquals(7, p.breakText(string, true, 50.0f, null));
-        assertEquals(6, p.breakText(string, true, 40.0f, null));
-        assertEquals(5, p.breakText(string, true, 35.0f, null));
-        assertEquals(4, p.breakText(string, true, 25.0f, null));
-        assertEquals(3, p.breakText(string, true, 20.0f, null));
-        assertEquals(2, p.breakText(string, true, 12.0f, null));
-        assertEquals(1, p.breakText(string, true, 10.0f, null));
-        assertEquals(0, p.breakText(string, true, 3.0f, null));
     }
 
     @TestTargetNew(
@@ -831,427 +688,48 @@
 
     }
 
-    @TestTargetNew(
-        level = TestLevel.TODO,
-        method = "getTextWidths",
-        args = {char[].class, int.class, int.class, float[].class}
-    )
-    @BrokenTest("unknown if hardcoded values being checked are correct")
-    public void testGetTextWidths1() throws Exception {
-        Paint p = new Paint();
-        char[] chars = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
-        float[] width = {8.0f, 4.0f, 3.0f, 7.0f, 6.0f, 10.0f, 9.0f};
-        float[] f = new float[7];
+    public void testGetTextWidths() throws Exception {
+        String text = "HIJKLMN";
+        char[] textChars = text.toCharArray();
+        SpannedString textSpan = new SpannedString(text);
 
-        assertEquals(7, p.getTextWidths(chars, 0, 7, f));
-        for (int i = 0; i < chars.length; i++) {
-            assertEquals(width[i], f[i]);
-        }
+        // Test measuring the widths of the entire text
+        assertGetTextWidths(text, textChars, textSpan, 0, 7);
 
-        assertEquals(4, p.getTextWidths(chars, 3, 4, f));
-        for (int i = 3; i < chars.length; i++) {
-            assertEquals(width[i], f[i - 3]);
-        }
+        // Test measuring a substring of the text
+        assertGetTextWidths(text, textChars, textSpan, 1, 3);
 
-        assertEquals(1, p.getTextWidths(chars, 6, 1, f));
-        assertEquals(width[6], f[0]);
-        assertEquals(0, p.getTextWidths(chars, 6, 0, f));
+        // Test measuring a substring of zero length.
+        assertGetTextWidths(text, textChars, textSpan, 3, 3);
 
-        try {
-            p.getTextWidths(chars, -1, 6, f);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(chars, 0, -1, f);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(chars, 1, 8, f);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
-
-        float[] f2 = new float[3];
-        try {
-            p.getTextWidths(chars, 0, 6, f2);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
+        // Test measuring substrings from the front and back
+        assertGetTextWidths(text, textChars, textSpan, 0, 2);
+        assertGetTextWidths(text, textChars, textSpan, 4, 7);
     }
 
-    @TestTargetNew(
-        level = TestLevel.TODO,
-        method = "getTextWidths",
-        args = {java.lang.CharSequence.class, int.class, int.class, float[].class}
-    )
-    @BrokenTest("unknown if hardcoded values being checked are correct")
-    public void testGetTextWidths2() throws Exception {
+    /** Tests all four overloads of getTextWidths are the same. */
+    private void assertGetTextWidths(String text, char[] textChars, SpannedString textSpan,
+            int start, int end) {
         Paint p = new Paint();
+        int count = end - start;
+        float[][] widths = new float[][] {
+            new float[count],
+            new float[count],
+            new float[count],
+            new float[count]
+        };
 
-        // CharSequence of String
-        String string = "HIJKLMN";
-        float[] width = {8.0f, 4.0f, 3.0f, 7.0f, 6.0f, 10.0f, 9.0f};
-        float[] f = new float[7];
+        String textSlice = text.substring(start, end);
+        assertEquals(count, p.getTextWidths(textSlice, widths[0]));
+        assertEquals(count, p.getTextWidths(textChars, start, count, widths[1]));
+        assertEquals(count, p.getTextWidths(textSpan, start, end, widths[2]));
+        assertEquals(count, p.getTextWidths(text, start, end, widths[3]));
 
-        assertEquals(7, p.getTextWidths((CharSequence) string, 0, 7, f));
-        for (int i = 0; i < string.length(); i++) {
-            assertEquals(width[i], f[i]);
-        }
-
-        assertEquals(4, p.getTextWidths((CharSequence) string, 3, 7, f));
-        for (int i = 3; i < string.length(); i++) {
-            assertEquals(width[i], f[i - 3]);
-        }
-
-        assertEquals(1, p.getTextWidths((CharSequence) string, 6, 7, f));
-        assertEquals(width[6], f[0]);
-        assertEquals(0, p.getTextWidths((CharSequence) string, 7, 7, f));
-
-        try {
-            p.getTextWidths((CharSequence) string, -1, 6, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-        try {
-            p.getTextWidths((CharSequence) string, 0, -1, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths((CharSequence) string, 4, 3, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths((CharSequence) string, 1, 8, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        float[] f2 = new float[3];
-        try {
-            p.getTextWidths((CharSequence) string, 0, 6, f2);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
-        // CharSequence of SpannedString
-        SpannedString spannedString = new SpannedString("HIJKLMN");
-
-        assertEquals(7, p.getTextWidths(spannedString, 0, 7, f));
-        for (int i = 0; i < spannedString.length(); i++) {
-            assertEquals(width[i], f[i]);
-        }
-
-        assertEquals(4, p.getTextWidths(spannedString, 3, 7, f));
-        for (int i = 3; i < spannedString.length(); i++) {
-            assertEquals(width[i], f[i - 3]);
-        }
-
-        assertEquals(1, p.getTextWidths(spannedString, 6, 7, f));
-        assertEquals(width[6], f[0]);
-        assertEquals(0, p.getTextWidths(spannedString, 7, 7, f));
-
-        try {
-            p.getTextWidths(spannedString, -1, 6, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannedString, 0, -1, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannedString, 4, 3, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannedString, 1, 8, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannedString, 0, 6, f2);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
-
-        // CharSequence of SpannableString
-        SpannableString spannableString = new SpannableString("HIJKLMN");
-
-        assertEquals(7, p.getTextWidths(spannableString, 0, 7, f));
-        for (int i = 0; i < spannableString.length(); i++) {
-            assertEquals(width[i], f[i]);
-        }
-
-        assertEquals(4, p.getTextWidths(spannableString, 3, 7, f));
-        for (int i = 3; i < spannableString.length(); i++) {
-            assertEquals(width[i], f[i - 3]);
-        }
-
-        assertEquals(1, p.getTextWidths(spannableString, 6, 7, f));
-        assertEquals(width[6], f[0]);
-        assertEquals(0, p.getTextWidths(spannableString, 7, 7, f));
-
-        try {
-            p.getTextWidths(spannableString, -1, 6, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannableString, 0, -1, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannableString, 4, 3, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannableString, 1, 8, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannableString, 0, 6, f2);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
-
-        // CharSequence of SpannableStringBuilder (GraphicsOperations)
-        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("HIJKLMN");
-
-        assertEquals(7, p.getTextWidths(spannableStringBuilder, 0, 7, f));
-        for (int i = 0; i < spannableStringBuilder.length(); i++) {
-            assertEquals(width[i], f[i]);
-        }
-
-        assertEquals(4, p.getTextWidths(spannableStringBuilder, 3, 7, f));
-        for (int i = 3; i < spannableStringBuilder.length(); i++) {
-            assertEquals(width[i], f[i - 3]);
-        }
-
-        assertEquals(1, p.getTextWidths(spannableStringBuilder, 6, 7, f));
-        assertEquals(width[6], f[0]);
-        assertEquals(0, p.getTextWidths(spannableStringBuilder, 7, 7, f));
-
-        try {
-            p.getTextWidths(spannableStringBuilder, -1, 6, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannableStringBuilder, 0, -1, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannableStringBuilder, 4, 3, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannableStringBuilder, 1, 8, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(spannableStringBuilder, 0, 6, f2);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
-
-        // CharSequence of StringBuilder
-        StringBuilder stringBuilder = new StringBuilder("HIJKLMN");
-
-        assertEquals(7, p.getTextWidths(stringBuilder, 0, 7, f));
-        for (int i = 0; i < stringBuilder.length(); i++) {
-            assertEquals(width[i], f[i]);
-        }
-
-        assertEquals(4, p.getTextWidths(stringBuilder, 3, 7, f));
-        for (int i = 3; i < stringBuilder.length(); i++) {
-            assertEquals(width[i], f[i - 3]);
-        }
-
-        assertEquals(1, p.getTextWidths(stringBuilder, 6, 7, f));
-        assertEquals(width[6], f[0]);
-        assertEquals(0, p.getTextWidths(stringBuilder, 7, 7, f));
-
-        try {
-            p.getTextWidths(stringBuilder, -1, 6, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(stringBuilder, 0, -1, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(stringBuilder, 4, 3, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(stringBuilder, 1, 8, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(stringBuilder, 0, 6, f2);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
-
-    }
-
-    @TestTargetNew(
-        level = TestLevel.TODO,
-        method = "getTextWidths",
-        args = {java.lang.String.class, int.class, int.class, float[].class}
-    )
-    @BrokenTest("unknown if hardcoded values being checked are correct")
-    public void testGetTextWidths3() {
-        Paint p = new Paint();
-        String string = "HIJKLMN";
-        float[] width = {8.0f, 4.0f, 3.0f, 7.0f, 6.0f, 10.0f, 9.0f};
-        float[] f = new float[7];
-
-        assertEquals(7, p.getTextWidths(string, 0, 7, f));
-        for (int i = 0; i < string.length(); i++) {
-            assertEquals(width[i], f[i]);
-        }
-
-        assertEquals(4, p.getTextWidths(string, 3, 7, f));
-        for (int i = 3; i < string.length(); i++) {
-            assertEquals(width[i], f[i - 3]);
-        }
-
-        assertEquals(1, p.getTextWidths(string, 6, 7, f));
-        assertEquals(width[6], f[0]);
-        assertEquals(0, p.getTextWidths(string, 7, 7, f));
-
-        try {
-            p.getTextWidths(string, -1, 6, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(string, 0, -1, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(string, 4, 3, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextWidths(string, 1, 8, f);
-            fail("Should throw an IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-        float[] f2 = new float[3];
-        try {
-            p.getTextWidths(string, 0, 6, f2);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
-        }
-    }
-
-    @TestTargetNew(
-        level = TestLevel.TODO,
-        method = "getTextWidths",
-        args = {java.lang.String.class, float[].class}
-    )
-    @BrokenTest("unknown if hardcoded values being checked are correct")
-    public void testGetTextWidths4() throws Exception {
-        Paint p = new Paint();
-        String string = "HIJKLMN";
-        float[] width = {8.0f, 4.0f, 3.0f, 7.0f, 6.0f, 10.0f, 9.0f};
-        float[] f = new float[7];
-
-        assertEquals(7, p.getTextWidths(string, f));
-        for (int i = 0; i < string.length(); i++) {
-            assertEquals(width[i], f[i]);
-        }
-
-        assertEquals(0, p.getTextWidths("", f));
-
-        try {
-            p.getTextWidths(null, f);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        float[] f2 = new float[3];
-        try {
-            p.getTextWidths(string, f2);
-            fail("Should throw an ArrayIndexOutOfBoundsException");
-        } catch (ArrayIndexOutOfBoundsException e) {
-            //except here
+        // Check that the widths returned by the overloads are the same.
+        for (int i = 0; i < count; i++) {
+            assertEquals(widths[0][i], widths[1][i]);
+            assertEquals(widths[1][i], widths[2][i]);
+            assertEquals(widths[2][i], widths[3][i]);
         }
     }
 
@@ -1407,138 +885,6 @@
 
     @TestTargetNew(
         level = TestLevel.COMPLETE,
-        method = "getTextBounds",
-        args = {java.lang.String.class, int.class, int.class, android.graphics.Rect.class}
-    )
-    @BrokenTest("Test result will be different when run in batch mode")
-    public void testGetTextBounds1() throws Exception {
-        Paint p = new Paint();
-        Rect r = new Rect();
-        String s = "HIJKLMN";
-
-        try {
-            p.getTextBounds(s, -1, 2, r);
-        } catch (IndexOutOfBoundsException e) {
-        } catch (RuntimeException e) {
-            fail("Should not throw a RuntimeException");
-        }
-
-        try {
-            p.getTextBounds(s, 0, -2, r);
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextBounds(s, 4, 3, r);
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextBounds(s, 0, 8, r);
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextBounds(s, 0, 2, null);
-        } catch (NullPointerException e) {
-            //except here
-        }
-
-        p.getTextBounds(s, 0, 0, r);
-        assertEquals(0, r.bottom);
-        assertEquals(-1, r.left);
-        assertEquals(0, r.right);
-        assertEquals(-1, r.top);
-
-        p.getTextBounds(s, 0, 1, r);
-        assertEquals(0, r.bottom);
-        assertEquals(1, r.left);
-        assertEquals(8, r.right);
-        assertEquals(-9, r.top);
-
-        p.getTextBounds(s, 1, 2, r);
-        assertEquals(0, r.bottom);
-        assertEquals(0, r.left);
-        assertEquals(4, r.right);
-        assertEquals(-9, r.top);
-
-        p.getTextBounds(s, 0, 6, r);
-        assertEquals(3, r.bottom);
-        assertEquals(1, r.left);
-        assertEquals(38, r.right);
-        assertEquals(-9, r.top);
-    }
-
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "getTextBounds",
-        args = {char[].class, int.class, int.class, android.graphics.Rect.class}
-    )
-    @BrokenTest("Test result will be different when run in batch mode")
-    public void testGetTextBounds2() throws Exception {
-        Paint p = new Paint();
-        Rect r = new Rect();
-        char[] chars = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
-
-        try {
-            p.getTextBounds(chars, -1, 2, r);
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextBounds(chars, 0, -2, r);
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextBounds(chars, 4, 3, r);
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-
-        try {
-            p.getTextBounds(chars, 0, 8, r);
-        } catch (IndexOutOfBoundsException e) {
-            //except here
-        }
-        try {
-            p.getTextBounds(chars, 0, 2, null);
-        } catch (NullPointerException e) {
-            //except here
-        }
-
-        p.getTextBounds(chars, 0, 0, r);
-        assertEquals(0, r.bottom);
-        assertEquals(-1, r.left);
-        assertEquals(0, r.right);
-        assertEquals(0, r.top);
-
-        p.getTextBounds(chars, 0, 1, r);
-        assertEquals(0, r.bottom);
-        assertEquals(1, r.left);
-        assertEquals(8, r.right);
-        assertEquals(-9, r.top);
-
-        p.getTextBounds(chars, 1, 2, r);
-        assertEquals(3, r.bottom);
-        assertEquals(0, r.left);
-        assertEquals(7, r.right);
-        assertEquals(-9, r.top);
-
-        p.getTextBounds(chars, 0, 6, r);
-        assertEquals(3, r.bottom);
-        assertEquals(1, r.left);
-        assertEquals(38, r.right);
-        assertEquals(-9, r.top);
-    }
-
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
         method = "setShadowLayer",
         args = {float.class, float.class, float.class, int.class}
     )
@@ -1920,433 +1266,54 @@
         assertEquals(-26, fmi.top);
     }
 
-    @TestTargetNew(
-        level = TestLevel.TODO,
-        method = "measureText",
-        args = {char[].class, int.class, int.class}
-    )
-    @BrokenTest("unknown if hardcoded values being checked are correct")
-    public void testMeasureText1() {
+    public void testMeasureText() {
+        String text = "HIJKLMN";
+        char[] textChars = text.toCharArray();
+        SpannedString textSpan = new SpannedString(text);
+
         Paint p = new Paint();
-
-        // The default text size
-        assertEquals(12.0f, p.getTextSize());
-
-        char[] c = {};
-        char[] c2 = {'H'};
-        char[] c3 = {'H', 'I', 'J', 'H', 'I', 'J'};
-        assertEquals(0.0f, p.measureText(c, 0, 0));
-        assertEquals(8.0f, p.measureText(c2, 0, 1));
-        assertEquals(8.0f, p.measureText(c3, 0, 1));
-        assertEquals(15.0f, p.measureText(c3, 0, 3));
-        assertEquals(15.0f, p.measureText(c3, 3, 3));
-        assertEquals(30.0f, p.measureText(c3, 0, 6));
-
-        p.setTextSize(24.0f);
-
-        assertEquals(17.0f, p.measureText(c2, 0, 1));
-
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.MONOSPACE);
-
-        assertEquals(7.0f, p.measureText(c2, 0, 1));
-
-        try {
-            p.measureText(c3, -1, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
+        float[] widths = new float[text.length()];
+        for (int i = 0; i < widths.length; i++) {
+            widths[i] = p.measureText(text, i, i + 1);
         }
 
-        try {
-            p.measureText(c3, 4, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
+        float totalWidth = 0;
+        for (int i = 0; i < widths.length; i++) {
+            totalWidth += widths[i];
         }
 
-        try {
-            p.measureText(c3, 0, 9);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
+        // Test measuring the widths of the entire text
+        assertMeasureText(text, textChars, textSpan, 0, 7, totalWidth);
 
-        try {
-            p.measureText((char[]) null, 0, 0);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
+        // Test measuring a substring of the text
+        assertMeasureText(text, textChars, textSpan, 1, 3, widths[1] + widths[2]);
+
+        // Test measuring a substring of zero length.
+        assertMeasureText(text, textChars, textSpan, 3, 3, 0);
+
+        // Test measuring substrings from the front and back
+        assertMeasureText(text, textChars, textSpan, 0, 2, widths[0] + widths[1]);
+        assertMeasureText(text, textChars, textSpan, 4, 7, widths[4] + widths[5] + widths[6]);
     }
 
-    @TestTargetNew(
-        level = TestLevel.TODO,
-        method = "measureText",
-        args = {java.lang.String.class, int.class, int.class}
-    )
-    @BrokenTest("unknown if hardcoded values being checked are correct")
-    public void testMeasureText2() {
+    /** Tests that all four overloads of measureText are the same and match some value. */
+    private void assertMeasureText(String text, char[] textChars, SpannedString textSpan,
+            int start, int end, float expectedWidth) {
         Paint p = new Paint();
-        String string = "HIJHIJ";
+        int count = end - start;
+        float[] widths = new float[] {-1, -1, -1, -1};
 
-        // The default text size
-        assertEquals(12.0f, p.getTextSize());
+        String textSlice = text.substring(start, end);
+        widths[0] = p.measureText(textSlice);
+        widths[1] = p.measureText(textChars, start, count);
+        widths[2] = p.measureText(textSpan, start, end);
+        widths[3] = p.measureText(text, start, end);
 
-        assertEquals(0.0f, p.measureText("", 0, 0));
-        assertEquals(8.0f, p.measureText("H", 0, 1));
-        assertEquals(4.0f, p.measureText("I", 0, 1));
-        assertEquals(3.0f, p.measureText("J", 0, 1));
-        assertEquals(8.0f, p.measureText(string, 0, 1));
-        assertEquals(15.0f, p.measureText(string, 0, 3));
-        assertEquals(15.0f, p.measureText(string, 3, 6));
-        assertEquals(30.0f, p.measureText(string, 0, 6));
-
-        p.setTextSize(24.0f);
-
-        assertEquals(17.0f, p.measureText("H", 0, 1));
-        assertEquals(8.0f, p.measureText("I", 0, 1));
-        assertEquals(7.0f, p.measureText("J", 0, 1));
-
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.MONOSPACE);
-
-        assertEquals(7.0f, p.measureText("H", 0, 1));
-        assertEquals(7.0f, p.measureText("I", 0, 1));
-        assertEquals(7.0f, p.measureText("J", 0, 1));
-
-        try {
-            p.measureText(string, -1, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText(string, 4, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText(string, 0, 9);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText((String) null, 0, 0);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-    }
-
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "measureText",
-        args = {java.lang.String.class}
-    )
-    @BrokenTest("unknown if hardcoded values being checked are correct")
-    public void testMeasureText3() {
-        Paint p = new Paint();
-
-        // The default text size
-        p.setTextSize(12.0f);
-        assertEquals(12.0f, p.getTextSize());
-
-        assertEquals(0.0f, p.measureText(""));
-        assertEquals(8.0f, p.measureText("H"));
-        assertEquals(4.0f, p.measureText("I"));
-        assertEquals(3.0f, p.measureText("J"));
-        assertEquals(7.0f, p.measureText("K"));
-        assertEquals(6.0f, p.measureText("L"));
-        assertEquals(10.0f, p.measureText("M"));
-        assertEquals(9.0f, p.measureText("N"));
-        assertEquals(12.0f, p.measureText("HI"));
-        p.setTextSize(24.0f);
-
-        assertEquals(17.0f, p.measureText("H"));
-        assertEquals(8.0f, p.measureText("I"));
-        assertEquals(7.0f, p.measureText("J"));
-        assertEquals(14.0f, p.measureText("K"));
-        assertEquals(12.0f, p.measureText("L"));
-        assertEquals(21.0f, p.measureText("M"));
-        assertEquals(18.0f, p.measureText("N"));
-        assertEquals(25.0f, p.measureText("HI"));
-
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.MONOSPACE);
-
-        assertEquals(7.0f, p.measureText("H"));
-        assertEquals(7.0f, p.measureText("I"));
-        assertEquals(7.0f, p.measureText("J"));
-        assertEquals(7.0f, p.measureText("K"));
-        assertEquals(7.0f, p.measureText("L"));
-        assertEquals(7.0f, p.measureText("M"));
-        assertEquals(7.0f, p.measureText("N"));
-        assertEquals(14.0f, p.measureText("HI"));
-
-        try {
-            p.measureText(null);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-    }
-
-    @TestTargetNew(
-        level = TestLevel.TODO,
-        method = "measureText",
-        args = {java.lang.CharSequence.class, int.class, int.class}
-    )
-    @BrokenTest("unknown if hardcoded values being tested are correct")
-    public void testMeasureText4() {
-
-        Paint p = new Paint();
-        // CharSequence of String
-        String string = "HIJHIJ";
-        // The default text size
-        p.setTextSize(12.0f);
-        assertEquals(12.0f, p.getTextSize());
-
-        assertEquals(8.0f, p.measureText((CharSequence) string, 0, 1));
-        assertEquals(15.0f, p.measureText((CharSequence) string, 0, 3));
-        assertEquals(15.0f, p.measureText((CharSequence) string, 3, 6));
-        assertEquals(30.0f, p.measureText((CharSequence) string, 0, 6));
-
-        p.setTextSize(24.0f);
-
-        assertEquals(17.0f, p.measureText((CharSequence) string, 0, 1));
-        assertEquals(32.0f, p.measureText((CharSequence) string, 0, 3));
-        assertEquals(32.0f, p.measureText((CharSequence) string, 3, 6));
-        assertEquals(64.0f, p.measureText((CharSequence) string, 0, 6));
-
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.MONOSPACE);
-
-        assertEquals(7.0f, p.measureText((CharSequence) string, 0, 1));
-        assertEquals(21.0f, p.measureText((CharSequence) string, 0, 3));
-        assertEquals(21.0f, p.measureText((CharSequence) string, 3, 6));
-        assertEquals(42.0f, p.measureText((CharSequence) string, 0, 6));
-
-        try {
-            p.measureText((CharSequence) "HIJHIJ", -1, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText((CharSequence) "HIJHIJ", 4, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText((CharSequence) "HIJHIJ", 0, 9);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText((CharSequence) null, 0, 0);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        // CharSequence of SpannedString
-        SpannedString spannedString = new SpannedString("HIJHIJ");
-        // The default text size and typeface
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.DEFAULT);
-
-        assertEquals(8.0f, p.measureText(spannedString, 0, 1));
-        assertEquals(15.0f, p.measureText(spannedString, 0, 3));
-        assertEquals(15.0f, p.measureText(spannedString, 3, 6));
-        assertEquals(30.0f, p.measureText(spannedString, 0, 6));
-
-        p.setTextSize(24.0f);
-
-        assertEquals(17.0f, p.measureText(spannedString, 0, 1));
-        assertEquals(32.0f, p.measureText(spannedString, 0, 3));
-        assertEquals(32.0f, p.measureText(spannedString, 3, 6));
-        assertEquals(64.0f, p.measureText(spannedString, 0, 6));
-
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.MONOSPACE);
-
-        assertEquals(7.0f, p.measureText(spannedString, 0, 1));
-        assertEquals(21.0f, p.measureText(spannedString, 0, 3));
-        assertEquals(21.0f, p.measureText(spannedString, 3, 6));
-        assertEquals(42.0f, p.measureText(spannedString, 0, 6));
-
-        try {
-            p.measureText(spannedString, -1, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText(spannedString, 4, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText(spannedString, 0, 9);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText((SpannedString) null, 0, 0);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        // CharSequence of SpannableString
-        SpannableString spannableString = new SpannableString("HIJHIJ");
-        // The default text size and typeface
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.DEFAULT);
-
-        assertEquals(8.0f, p.measureText(spannableString, 0, 1));
-        assertEquals(15.0f, p.measureText(spannableString, 0, 3));
-        assertEquals(15.0f, p.measureText(spannableString, 3, 6));
-        assertEquals(30.0f, p.measureText(spannableString, 0, 6));
-
-        p.setTextSize(24.0f);
-
-        assertEquals(17.0f, p.measureText(spannableString, 0, 1));
-        assertEquals(32.0f, p.measureText(spannableString, 0, 3));
-        assertEquals(32.0f, p.measureText(spannableString, 3, 6));
-        assertEquals(64.0f, p.measureText(spannableString, 0, 6));
-
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.MONOSPACE);
-
-        assertEquals(7.0f, p.measureText(spannableString, 0, 1));
-        assertEquals(21.0f, p.measureText(spannableString, 0, 3));
-        assertEquals(21.0f, p.measureText(spannableString, 3, 6));
-        assertEquals(42.0f, p.measureText(spannableString, 0, 6));
-
-        try {
-            p.measureText(spannableString, -1, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText(spannableString, 4, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText(spannableString, 0, 9);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText((SpannableString) null, 0, 0);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        // CharSequence of SpannableStringBuilder (GraphicsOperations)
-        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("HIJHIJ");
-        // The default text size
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.DEFAULT);
-
-        assertEquals(8.0f, p.measureText(spannableStringBuilder, 0, 1));
-        assertEquals(15.0f, p.measureText(spannableStringBuilder, 0, 3));
-        assertEquals(15.0f, p.measureText(spannableStringBuilder, 3, 6));
-        assertEquals(30.0f, p.measureText(spannableStringBuilder, 0, 6));
-
-        p.setTextSize(24.0f);
-
-        assertEquals(17.0f, p.measureText(spannableStringBuilder, 0, 1));
-        assertEquals(32.0f, p.measureText(spannableStringBuilder, 0, 3));
-        assertEquals(32.0f, p.measureText(spannableStringBuilder, 3, 6));
-        assertEquals(64.0f, p.measureText(spannableStringBuilder, 0, 6));
-
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.MONOSPACE);
-
-        assertEquals(7.0f, p.measureText(spannableStringBuilder, 0, 1));
-        assertEquals(21.0f, p.measureText(spannableStringBuilder, 0, 3));
-        assertEquals(21.0f, p.measureText(spannableStringBuilder, 3, 6));
-        assertEquals(42.0f, p.measureText(spannableStringBuilder, 0, 6));
-
-        try {
-            p.measureText(spannableStringBuilder, -1, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText(spannableStringBuilder, 4, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText(spannableStringBuilder, 0, 9);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText((SpannableStringBuilder) null, 0, 0);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        // CharSequence of StringBuilder
-        StringBuilder stringBuilder = new StringBuilder("HIJHIJ");
-        // The default text size and typeface
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.DEFAULT);
-
-        assertEquals(8.0f, p.measureText(stringBuilder, 0, 1));
-        assertEquals(15.0f, p.measureText(stringBuilder, 0, 3));
-        assertEquals(15.0f, p.measureText(stringBuilder, 3, 6));
-        assertEquals(30.0f, p.measureText(stringBuilder, 0, 6));
-
-        p.setTextSize(24.0f);
-
-        assertEquals(17.0f, p.measureText(stringBuilder, 0, 1));
-        assertEquals(32.0f, p.measureText(stringBuilder, 0, 3));
-        assertEquals(32.0f, p.measureText(stringBuilder, 3, 6));
-        assertEquals(64.0f, p.measureText(stringBuilder, 0, 6));
-
-        p.setTextSize(12.0f);
-        p.setTypeface(Typeface.MONOSPACE);
-
-        assertEquals(7.0f, p.measureText(stringBuilder, 0, 1));
-        assertEquals(21.0f, p.measureText(stringBuilder, 0, 3));
-        assertEquals(21.0f, p.measureText(stringBuilder, 3, 6));
-        assertEquals(42.0f, p.measureText(stringBuilder, 0, 6));
-
-        try {
-            p.measureText(stringBuilder, -1, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText(stringBuilder, 4, 3);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText(stringBuilder, 0, 9);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
-        try {
-            p.measureText((StringBuilder) null, 0, 0);
-            fail("Should throw a RuntimeException");
-        } catch (RuntimeException e) {
-        }
-
+        // Check that the widths returned by the overloads are the same.
+        assertEquals(widths[0], widths[1]);
+        assertEquals(widths[1], widths[2]);
+        assertEquals(widths[2], widths[3]);
+        assertEquals(widths[3], expectedWidth);
     }
 
     @TestTargetNew(
diff --git a/tests/tests/graphics/src/android/graphics/cts/PathMeasureTest.java b/tests/tests/graphics/src/android/graphics/cts/PathMeasureTest.java
index c738d40..94e352a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PathMeasureTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PathMeasureTest.java
@@ -16,17 +16,17 @@
 
 package android.graphics.cts;
 
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
 import android.graphics.Matrix;
 import android.graphics.Path;
 import android.graphics.PathMeasure;
+import android.graphics.Path.Direction;
 import android.test.AndroidTestCase;
 
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.TestTargets;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestTargetClass;
-
 @TestTargetClass(PathMeasure.class)
 public class PathMeasureTest extends AndroidTestCase {
     private PathMeasure mPathMeasure;
@@ -119,13 +119,22 @@
         method = "isClosed",
         args = {}
     )
-    @BrokenTest("Flaky test. new PathMeasure().isClosed() does not return consistent result")
     public void testIsClosed() {
-        assertTrue(mPathMeasure.isClosed());
-        mPathMeasure = null;
-        mPathMeasure = new PathMeasure();
-        mPathMeasure.setPath(mPath, false);
-        assertFalse(mPathMeasure.isClosed());
+        Path circle = new Path();
+        circle.addCircle(0, 0, 1, Direction.CW);
+
+        PathMeasure measure = new PathMeasure(circle, false);
+        assertTrue(measure.isClosed());
+        measure.setPath(circle, true);
+        assertTrue(measure.isClosed());
+
+        Path line = new Path();
+        line.lineTo(5, 5);
+
+        measure.setPath(line, false);
+        assertFalse(measure.isClosed());
+        measure.setPath(line, true);
+        assertTrue(measure.isClosed());
     }
 
     @TestTargetNew(
@@ -148,6 +157,9 @@
         mPath.addRect(1, 2, 3, 4, Path.Direction.CW);
         mPathMeasure.setPath(mPath, true);
         assertEquals(8f, mPathMeasure.getLength());
+        Path dst = new Path();
+        assertTrue(mPathMeasure.getSegment(0, mPathMeasure.getLength(), dst, true));
+        assertFalse(mPathMeasure.getSegment(mPathMeasure.getLength(), 0, dst, true));
     }
 
     @TestTargetNew(
diff --git a/tests/tests/graphics/src/android/graphics/cts/RectTest.java b/tests/tests/graphics/src/android/graphics/cts/RectTest.java
index 2176987..5eb788f 100644
--- a/tests/tests/graphics/src/android/graphics/cts/RectTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/RectTest.java
@@ -677,6 +677,30 @@
 
     }
 
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "flattenToString",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "unflattenFromString",
+            args = {java.lang.String.class}
+        )
+    })
+    public void testFlattenToString() {
+        Rect mRect = new Rect(1, 2, 3, 4);
+        String flattenString = mRect.flattenToString();
+        assertNotNull(flattenString);
+        String unDefinedFormat = "TOPLEFT";
+        Rect rect = Rect.unflattenFromString(flattenString);
+        assertEquals(mRect, rect);
+        rect = null;
+        rect = Rect.unflattenFromString(unDefinedFormat);
+        assertNull(rect);
+    }
+
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         method = "describeContents",
diff --git a/tests/tests/graphics/src/android/graphics/cts/Shader_TileModeTest.java b/tests/tests/graphics/src/android/graphics/cts/Shader_TileModeTest.java
index 135bf36..4389032 100644
--- a/tests/tests/graphics/src/android/graphics/cts/Shader_TileModeTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/Shader_TileModeTest.java
@@ -18,6 +18,7 @@
 
 import junit.framework.TestCase;
 import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
@@ -26,18 +27,27 @@
 @TestTargetClass(Shader.TileMode.class)
 public class Shader_TileModeTest extends TestCase {
 
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.NOT_NECESSARY,
-            method = "values",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.NOT_NECESSARY,
-            method = "valueOf",
-            args = {java.lang.String.class}
-        )
-    })
-    public void testTileMode() {
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "valueOf",
+        args = {java.lang.String.class}
+    )
+    public void testValueOf() {
+        assertEquals(TileMode.CLAMP, TileMode.valueOf("CLAMP"));
+        assertEquals(TileMode.MIRROR, TileMode.valueOf("MIRROR"));
+        assertEquals(TileMode.REPEAT, TileMode.valueOf("REPEAT"));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "values",
+        args = {}
+    )
+    public void testValues() {
+        TileMode[] tileMode = TileMode.values();
+        assertEquals(3, tileMode.length);
+        assertEquals(TileMode.CLAMP, tileMode[0]);
+        assertEquals(TileMode.REPEAT, tileMode[1]);
+        assertEquals(TileMode.MIRROR, tileMode[2]);
     }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java b/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
old mode 100644
new mode 100755
index d09483d..1af26af
--- a/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
@@ -24,6 +24,10 @@
 import dalvik.annotation.TestTargetNew;
 import dalvik.annotation.TestTargets;
 
+import android.graphics.Typeface;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
@@ -31,8 +35,15 @@
 import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.graphics.Bitmap.Config;
+import android.os.ParcelFileDescriptor;
 import android.test.AndroidTestCase;
 
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
 @TestTargetClass(android.graphics.Typeface.class)
 public class TypefaceTest extends AndroidTestCase {
 
@@ -198,4 +209,107 @@
             }
         }
     }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "createFromFile",
+            args = {java.io.File.class}
+    )
+    public void testCreateFromFile1() throws IOException {
+        // input abnormal params.
+        try {
+            Typeface.createFromFile((File)null);
+            fail("Should throw a NullPointerException.");
+        } catch (NullPointerException e) {
+            // except here
+        }
+        File file = new File(obtainPath());
+        Typeface typeface = Typeface.createFromFile(file);
+        assertNotNull(typeface);
+
+        Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+        bitmap.eraseColor(Color.BLACK);
+        Canvas canvas = new Canvas(bitmap);
+        Paint p = new Paint();
+        p.setTypeface(typeface);
+        p.setColor(Color.WHITE);
+        p.setTextAlign(Paint.Align.CENTER);
+        p.setTextSize(50);
+        p.setFlags(0); // clear all flags (not sure what defaults flags are set)
+        canvas.drawText("test", bitmap.getWidth() / 2, 3 * bitmap.getHeight() / 4, p);
+
+        BitmapFactory.Options opt = new BitmapFactory.Options();
+        opt.inScaled = false;
+        Bitmap expected = BitmapFactory.decodeResource(getContext().getResources(),
+                R.drawable.typeface_test, opt);
+        assertEquals(expected.getWidth(), bitmap.getWidth());
+        assertEquals(expected.getHeight(), bitmap.getHeight());
+        for (int y = 0; y < bitmap.getHeight(); y++) {
+            for (int x = 0; x < bitmap.getWidth(); x++) {
+                assertEquals(expected.getPixel(x, y), bitmap.getPixel(x, y));
+            }
+        }
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "createFromFile",
+            args = {java.lang.String.class}
+    )
+    public void testCreateFromFile2() throws IOException {
+        // input abnormal params.
+        try {
+            Typeface.createFromFile((String)null);
+            fail("Should throw a NullPointerException.");
+        } catch (NullPointerException e) {
+            // except here
+        }
+
+        Typeface typeface = Typeface.createFromFile(obtainPath());
+        assertNotNull(typeface);
+
+        Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+        bitmap.eraseColor(Color.BLACK);
+        Canvas canvas = new Canvas(bitmap);
+        Paint p = new Paint();
+        p.setTypeface(typeface);
+        p.setColor(Color.WHITE);
+        p.setTextAlign(Paint.Align.CENTER);
+        p.setTextSize(50);
+        p.setFlags(0); // clear all flags (not sure what defaults flags are set)
+        canvas.drawText("test", bitmap.getWidth() / 2, 3 * bitmap.getHeight() / 4, p);
+
+        BitmapFactory.Options opt = new BitmapFactory.Options();
+        opt.inScaled = false;
+        Bitmap expected = BitmapFactory.decodeResource(getContext().getResources(),
+                R.drawable.typeface_test, opt);
+        assertEquals(expected.getWidth(), bitmap.getWidth());
+        assertEquals(expected.getHeight(), bitmap.getHeight());
+        for (int y = 0; y < bitmap.getHeight(); y++) {
+            for (int x = 0; x < bitmap.getWidth(); x++) {
+                assertEquals(expected.getPixel(x, y), bitmap.getPixel(x, y));
+            }
+        }
+    }
+
+    private String obtainPath() throws IOException {
+        File dir = getContext().getFilesDir();
+        dir.mkdirs();
+        File file = new File(dir, "test.jpg");
+        if (!file.createNewFile()) {
+            if (!file.exists()) {
+                fail("Failed to create new File!");
+            }
+        }
+        InputStream is = getContext().getAssets().open("samplefont.ttf");
+        FileOutputStream fOutput = new FileOutputStream(file);
+        byte[] dataBuffer = new byte[1024];
+        int readLength = 0;
+        while ((readLength = is.read(dataBuffer)) != -1) {
+            fOutput.write(dataBuffer, 0, readLength);
+        }
+        is.close();
+        fOutput.close();
+        return (file.getPath());
+    }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/YuvImageTest.java b/tests/tests/graphics/src/android/graphics/cts/YuvImageTest.java
index 6613e92..eb0b21f 100755
--- a/tests/tests/graphics/src/android/graphics/cts/YuvImageTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/YuvImageTest.java
@@ -203,6 +203,56 @@
 
     }
 
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getHeight",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getWidth",
+            args = {}
+        )
+    })
+    public void testGetHeight() {
+        generateTestBitmaps(WIDTH, HEIGHT);
+        YuvImage image = generateYuvImage(ImageFormat.YUY2, mTestBitmaps[0], 0);
+        assertEquals(mTestBitmaps[0].getHeight(), image.getHeight());
+        assertEquals(mTestBitmaps[0].getWidth(), image.getWidth());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getYuvData",
+        args = {}
+    )
+    public void testGetYuvData() {
+        generateTestBitmaps(WIDTH, HEIGHT);
+        int width = mTestBitmaps[0].getWidth();
+        int height = mTestBitmaps[0].getHeight();
+        int stride = width;
+        int[] argb = new int[stride * height];
+        mTestBitmaps[0].getPixels(argb, 0, stride, 0, 0, width, height);
+        byte[] yuv = convertArgbsToYuvs(argb, stride, height, ImageFormat.NV21);
+        int[] strides = new int[] {
+                stride, stride
+        };
+        YuvImage image = new YuvImage(yuv, ImageFormat.NV21, width, height, strides);
+        assertEquals(yuv, image.getYuvData());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getYuvFormat",
+        args = {}
+    )
+    public void testGetYuvFormat() {
+        generateTestBitmaps(WIDTH, HEIGHT);
+        YuvImage image = generateYuvImage(ImageFormat.YUY2, mTestBitmaps[0], 0);
+        assertEquals(ImageFormat.YUY2, image.getYuvFormat());
+    }
+
     private void generateTestBitmaps(int width, int height) {
         Bitmap dst = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         Canvas c = new Canvas(dst);
@@ -220,7 +270,7 @@
         int width = bitmap.getWidth();
         int height = bitmap.getHeight();
 
-        int stride = width + paddings;;
+        int stride = width + paddings;
 
         YuvImage image = null;
         int[] argb = new int [stride * height];
@@ -251,7 +301,6 @@
 
         Rect expectedRect = sameRect ? actualRect : rect1;
         expected = Bitmap.createBitmap(testBitmap, expectedRect.left, expectedRect.top, expectedRect.width(), expectedRect.height());
-        
         compareBitmaps(expected, actual, mMseMargin, sameRect);
     }
 
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
index a27aa9d..5e9e88d 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
@@ -41,6 +41,7 @@
 import android.graphics.drawable.Drawable.ConstantState;
 import android.test.InstrumentationTestCase;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.view.Gravity;
 
 import java.io.ByteArrayInputStream;
@@ -67,20 +68,40 @@
         @TestTargetNew(
             level = TestLevel.COMPLETE,
             method = "BitmapDrawable",
+            args = {android.content.res.Resources.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "BitmapDrawable",
             args = {android.graphics.Bitmap.class}
         ),
         @TestTargetNew(
             level = TestLevel.COMPLETE,
             method = "BitmapDrawable",
+            args = {android.content.res.Resources.class, android.graphics.Bitmap.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "BitmapDrawable",
             args = {java.lang.String.class}
         ),
         @TestTargetNew(
             level = TestLevel.COMPLETE,
             method = "BitmapDrawable",
+            args = {android.content.res.Resources.class, java.lang.String.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "BitmapDrawable",
             args = {java.io.InputStream.class}
         ),
         @TestTargetNew(
             level = TestLevel.COMPLETE,
+            method = "BitmapDrawable",
+            args = {android.content.res.Resources.class, java.io.InputStream.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
             method = "getPaint",
             args = {}
         ),
@@ -108,6 +129,10 @@
                 bitmapDrawable.getPaint().getFlags());
         assertEquals(bitmap, bitmapDrawable.getBitmap());
 
+        new BitmapDrawable(mContext.getResources());
+
+        new BitmapDrawable(mContext.getResources(), bitmap);
+
         new BitmapDrawable(mContext.getFilesDir().getPath());
 
         new BitmapDrawable(new ByteArrayInputStream("test constructor".getBytes()));
@@ -115,8 +140,12 @@
         // exceptional test
         new BitmapDrawable((Bitmap) null);
 
+        new BitmapDrawable(mContext.getResources(), (String) null);
+
         new BitmapDrawable((String) null);
 
+        new BitmapDrawable(mContext.getResources(), (InputStream) null);
+
         new BitmapDrawable((InputStream) null);
     }
 
@@ -416,6 +445,38 @@
         assertEquals(48, bitmapDrawable.getIntrinsicHeight());
     }
 
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setTargetDensity",
+            args = {android.graphics.Canvas.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setTargetDensity",
+            args = {android.util.DisplayMetrics.class}
+        )
+    })
+    @SuppressWarnings("deprecation")
+    public void testSetTargetDensity() {
+        BitmapDrawable bitmapDrawable = new BitmapDrawable();
+
+        Bitmap bitmap = Bitmap.createBitmap(200, 300, Config.RGB_565);
+        Canvas canvas = new Canvas(bitmap);
+        bitmapDrawable = new BitmapDrawable(bitmap);
+        bitmapDrawable.setTargetDensity(canvas.getDensity());
+        assertEquals(200, bitmapDrawable.getIntrinsicWidth());
+        assertEquals(300, bitmapDrawable.getIntrinsicHeight());
+
+        DisplayMetrics disMetrics = new DisplayMetrics();
+        disMetrics = getInstrumentation().getTargetContext().getResources().getDisplayMetrics();
+        InputStream source = mContext.getResources().openRawResource(R.drawable.size_48x48);
+        bitmapDrawable = new BitmapDrawable(source);
+        bitmapDrawable.setTargetDensity(disMetrics.densityDpi);
+        assertEquals(48, bitmapDrawable.getIntrinsicWidth());
+        assertEquals(48, bitmapDrawable.getIntrinsicHeight());
+    }
+
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         method = "inflate",
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
index 9d586d9..4d37508 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
@@ -29,6 +29,7 @@
 
 import android.content.ContentResolver;
 import android.content.res.Resources;
+import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
@@ -40,6 +41,7 @@
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
 import android.util.StateSet;
+import android.util.TypedValue;
 import android.util.Xml;
 
 import java.io.File;
@@ -236,6 +238,116 @@
 
     @TestTargetNew(
         level = TestLevel.COMPLETE,
+        method = "createFromResourceStream",
+        args = {android.content.res.Resources.class, android.util.TypedValue.class,
+                java.io.InputStream.class, java.lang.String.class}
+    )
+    public void testCreateFromResourceStream1() throws FileNotFoundException, IOException {
+        FileInputStream inputEmptyStream = null;
+        FileInputStream inputStream = null;
+        File imageFile = null;
+        OutputStream outputEmptyStream = null;
+
+        assertNull(Drawable.createFromResourceStream(null, null, inputStream, "test.bmp"));
+
+        File emptyFile = new File(mContext.getFilesDir(), "tempemptyimage.jpg");
+
+        // write some random data.
+        try {
+            outputEmptyStream = new FileOutputStream(emptyFile);
+            outputEmptyStream.write(10);
+
+            inputEmptyStream = new FileInputStream(emptyFile);
+            assertNull(Drawable.createFromResourceStream(mResources, null, inputEmptyStream,
+                    "Sample"));
+
+            imageFile = new File(mContext.getFilesDir(), "tempimage.jpg");
+
+            writeSampleImage(imageFile);
+
+            inputStream = new FileInputStream(imageFile);
+            final TypedValue value = new TypedValue();
+            assertNotNull(Drawable.createFromResourceStream(mResources, value, inputStream,
+                    "Sample"));
+        } finally {
+
+            if (null != outputEmptyStream) {
+                outputEmptyStream.close();
+            }
+            if (null != inputEmptyStream) {
+                inputEmptyStream.close();
+            }
+            if (null != inputStream) {
+                inputStream.close();
+            }
+            if (emptyFile.exists()) {
+                assertTrue(emptyFile.delete());
+            }
+            if (imageFile.exists()) {
+                assertTrue(imageFile.delete());
+            }
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "createFromResourceStream",
+        args = {android.content.res.Resources.class, android.util.TypedValue.class,
+                java.io.InputStream.class, java.lang.String.class,
+                android.graphics.BitmapFactory.Options.class}
+    )
+    public void testCreateFromResourceStream2() throws FileNotFoundException, IOException {
+        FileInputStream inputEmptyStream = null;
+        FileInputStream inputStream = null;
+        File imageFile = null;
+        OutputStream outputEmptyStream = null;
+
+        BitmapFactory.Options opt = new BitmapFactory.Options();
+        opt.inScaled = false;
+
+        assertNull(Drawable.createFromResourceStream(null, null, inputStream, "test.bmp", opt));
+
+        File emptyFile = new File(mContext.getFilesDir(), "tempemptyimage.jpg");
+
+        // write some random data.
+        try {
+            outputEmptyStream = new FileOutputStream(emptyFile);
+            outputEmptyStream.write(10);
+
+            inputEmptyStream = new FileInputStream(emptyFile);
+            assertNull(Drawable.createFromResourceStream(mResources, null, inputEmptyStream,
+                    "Sample", opt));
+
+            imageFile = new File(mContext.getFilesDir(), "tempimage.jpg");
+
+            writeSampleImage(imageFile);
+
+            inputStream = new FileInputStream(imageFile);
+            final TypedValue value = new TypedValue();
+            assertNotNull(Drawable.createFromResourceStream(mResources, value, inputStream,
+                    "Sample", opt));
+        } finally {
+
+            if (null != outputEmptyStream) {
+                outputEmptyStream.close();
+            }
+            if (null != inputEmptyStream) {
+                inputEmptyStream.close();
+            }
+            if (null != inputStream) {
+                inputStream.close();
+            }
+            if (emptyFile.exists()) {
+                assertTrue(emptyFile.delete());
+            }
+            if (imageFile.exists()) {
+                assertTrue(imageFile.delete());
+            }
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
         method = "createFromXml",
         args = {android.content.res.Resources.class, org.xmlpull.v1.XmlPullParser.class}
     )
diff --git a/tests/tests/graphics/src/android/graphics/drawable/shapes/cts/ShapeTest.java b/tests/tests/graphics/src/android/graphics/drawable/shapes/cts/ShapeTest.java
index 10c4022..99bde2f 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/shapes/cts/ShapeTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/shapes/cts/ShapeTest.java
@@ -21,14 +21,25 @@
 import dalvik.annotation.TestTargetNew;
 import dalvik.annotation.TestTargets;
 
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Bitmap.Config;
+import android.graphics.Paint.Style;
+import android.graphics.drawable.shapes.RoundRectShape;
 import android.graphics.drawable.shapes.Shape;
 
 import junit.framework.TestCase;
 
 @TestTargetClass(android.graphics.drawable.shapes.Shape.class)
 public class ShapeTest extends TestCase {
+    private static final int TEST_WIDTH  = 100;
+    private static final int TEST_HEIGHT = 200;
+
+    private static final int TEST_COLOR_1 = 0xFF00FF00;
+    private static final int TEST_COLOR_2 = 0xFFFF0000;
+
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
@@ -114,7 +125,30 @@
         args = {}
     )
     public void testHasAlpha() {
-        assertTrue(new MockShape().hasAlpha());
+        Shape shape = new MockShape();
+        assertTrue(shape.hasAlpha());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "draw",
+        args = {android.graphics.Canvas.class, android.graphics.Paint.class}
+    )
+    public void testDraw() {
+        Shape shape = new MockShape();
+        Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        Paint paint = new Paint();
+        paint.setStyle(Style.FILL);
+        paint.setColor(TEST_COLOR_1);
+        shape.resize(TEST_WIDTH, TEST_HEIGHT);
+
+        shape.draw(canvas, paint);
+        assertEquals(0, bitmap.getPixel(0, 0));
+
+        paint.setColor(TEST_COLOR_2);
+        shape.draw(canvas, paint);
+        assertEquals(0, bitmap.getPixel(0, 0));
     }
 
     private static class MockShape extends Shape {
diff --git a/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java b/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java
new file mode 100644
index 0000000..2f918db
--- /dev/null
+++ b/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.opengl.cts;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.test.ActivityInstrumentationTestCase2;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+/**
+ * Test that gets a list of EGL configurations and tries to use each one in a GLSurfaceView.
+ */
+public class EglConfigTest extends ActivityInstrumentationTestCase2<EglConfigStubActivity> {
+
+    private static final int EGL_OPENGL_ES_BIT = 0x1;
+
+    private static final int EGL_OPENGL_ES2_BIT = 0x4;
+
+    private Instrumentation mInstrumentation;
+
+    public EglConfigTest() {
+        super("com.android.cts.stub", EglConfigStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mInstrumentation = getInstrumentation();
+    }
+
+    public void testEglConfigs() throws Exception {
+        int[] configIds = getEglConfigIds(EGL_OPENGL_ES_BIT);
+        int[] configIds2 = getEglConfigIds(EGL_OPENGL_ES2_BIT);
+        assertTrue(configIds.length + configIds2.length > 0);
+        runConfigTests(configIds, 1);
+        runConfigTests(configIds2, 2);
+    }
+
+    private void runConfigTests(int[] configIds, int contextClientVersion)
+            throws InterruptedException {
+        for (int configId : configIds) {
+            Bundle extras = new Bundle();
+            extras.putInt(EglConfigStubActivity.CONFIG_ID_EXTRA, configId);
+            extras.putInt(EglConfigStubActivity.CONTEXT_CLIENT_VERSION_EXTRA, contextClientVersion);
+            EglConfigStubActivity activity = launchActivity("com.android.cts.stub",
+                    EglConfigStubActivity.class, extras);
+            activity.waitToFinishDrawing();
+            activity.finish();
+            mInstrumentation.waitForIdleSync();
+        }
+    }
+
+    private static int[] getEglConfigIds(int renderableType) {
+        EGL10 egl = (EGL10) EGLContext.getEGL();
+        EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+        int[] numConfigs = new int[1];
+
+        int[] attributeList = new int[] {
+                EGL10.EGL_RENDERABLE_TYPE, renderableType,
+
+                // Avoid configs like RGBA0008 which crash even though they have the window bit set.
+                EGL10.EGL_RED_SIZE, 1,
+                EGL10.EGL_GREEN_SIZE, 1,
+                EGL10.EGL_BLUE_SIZE, 1,
+
+                EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
+                EGL10.EGL_NONE
+        };
+
+        if (egl.eglInitialize(display, null)) {
+            try {
+                if (egl.eglChooseConfig(display, attributeList, null, 0, numConfigs)) {
+                    EGLConfig[] configs = new EGLConfig[numConfigs[0]];
+                    if (egl.eglChooseConfig(display, attributeList, configs, configs.length,
+                            numConfigs)) {
+                        int[] configIds = new int[numConfigs[0]];
+                        for (int i = 0; i < numConfigs[0]; i++) {
+                            int[] value = new int[1];
+                            if (egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_CONFIG_ID,
+                                    value)) {
+                                configIds[i] = value[0];
+                            } else {
+                              throw new IllegalStateException("Couldn't call eglGetConfigAttrib");
+                            }
+                        }
+                        return configIds;
+                    } else {
+                        throw new IllegalStateException("Couldn't call eglChooseConfig (1)");
+                    }
+                } else {
+                    throw new IllegalStateException("Couldn't call eglChooseConfig (2)");
+                }
+            } finally {
+                egl.eglTerminate(display);
+            }
+        } else {
+            throw new IllegalStateException("Couldn't initialize EGL.");
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index 864a1ab..60b8459 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -25,10 +25,10 @@
 import android.graphics.BitmapFactory;
 import android.graphics.ImageFormat;
 import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
 import android.hardware.Camera.ErrorCallback;
 import android.hardware.Camera.Parameters;
 import android.hardware.Camera.PictureCallback;
-import android.hardware.Camera.PreviewCallback;
 import android.hardware.Camera.ShutterCallback;
 import android.hardware.Camera.Size;
 import android.media.ExifInterface;
@@ -37,6 +37,7 @@
 import android.os.Environment;
 import android.os.Looper;
 import android.test.ActivityInstrumentationTestCase2;
+import android.test.MoreAsserts;
 import android.test.UiThreadTest;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
@@ -46,6 +47,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -61,7 +64,7 @@
             "/test.jpg";
     private byte[] mJpegData;
 
-    private boolean mRawPreviewCallbackResult = false;
+    private boolean mPreviewCallbackResult = false;
     private boolean mShutterCallbackResult = false;
     private boolean mRawPictureCallbackResult = false;
     private boolean mJpegPictureCallbackResult = false;
@@ -72,7 +75,7 @@
     private static final int WAIT_FOR_FOCUS_TO_COMPLETE = 3000;
     private static final int WAIT_FOR_SNAPSHOT_TO_COMPLETE = 5000;
 
-    private RawPreviewCallback mRawPreviewCallback = new RawPreviewCallback();
+    private PreviewCallback mPreviewCallback = new PreviewCallback();
     private TestShutterCallback mShutterCallback = new TestShutterCallback();
     private RawPictureCallback mRawPictureCallback = new RawPictureCallback();
     private JpegPictureCallback mJpegPictureCallback = new JpegPictureCallback();
@@ -111,7 +114,7 @@
      * Initializes the message looper so that the Camera object can
      * receive the callback messages.
      */
-    private void initializeMessageLooper() {
+    private void initializeMessageLooper(final int cameraId) {
         final ConditionVariable startDone = new ConditionVariable();
         new Thread() {
             @Override
@@ -122,7 +125,7 @@
                 // Save the looper so that we can terminate this thread
                 // after we are done with it.
                 mLooper = Looper.myLooper();
-                mCamera = Camera.open();
+                mCamera = Camera.open(cameraId);
                 Log.v(TAG, "camera is opened");
                 startDone.open();
                 Looper.loop(); // Blocks forever until Looper.quit() is called.
@@ -153,18 +156,13 @@
     }
 
     //Implement the previewCallback
-    private final class RawPreviewCallback implements PreviewCallback {
-        public void onPreviewFrame(byte [] rawData, Camera camera) {
-            if (LOGV) Log.v(TAG, "Preview callback start");
-            int rawDataLength = 0;
-            if (rawData != null) {
-                rawDataLength = rawData.length;
-            }
-            if (rawDataLength > 0) {
-                mRawPreviewCallbackResult = true;
-            } else {
-                mRawPreviewCallbackResult = false;
-            }
+    private final class PreviewCallback
+            implements android.hardware.Camera.PreviewCallback {
+        public void onPreviewFrame(byte [] data, Camera camera) {
+            assertNotNull(data);
+            Size size = camera.getParameters().getPreviewSize();
+            assertEquals(size.width * size.height * 3 / 2, data.length);
+            mPreviewCallbackResult = true;
             mCamera.stopPreview();
             if (LOGV) Log.v(TAG, "notify the preview callback");
             mPreviewDone.open();
@@ -265,10 +263,7 @@
     }
 
     private void checkPreviewCallback() throws Exception {
-        SurfaceHolder mSurfaceHolder;
-
-        mSurfaceHolder = CameraStubActivity.mSurfaceView.getHolder();
-        mCamera.setPreviewDisplay(mSurfaceHolder);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
         if (LOGV) Log.v(TAG, "check preview callback");
         mCamera.startPreview();
         waitForPreviewDone();
@@ -315,11 +310,17 @@
     })
     @UiThreadTest
     public void testTakePicture() throws Exception {
-        initializeMessageLooper();
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testTakePictureByCamera(id);
+        }
+    }
+
+    private void testTakePictureByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
         Size pictureSize = mCamera.getParameters().getPictureSize();
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = CameraStubActivity.mSurfaceView.getHolder();
-        mCamera.setPreviewDisplay(mSurfaceHolder);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
         mCamera.startPreview();
         mCamera.autoFocus(mAutoFocusCallback);
         assertTrue(waitForFocusDone());
@@ -329,16 +330,12 @@
         terminateMessageLooper();
         assertTrue(mShutterCallbackResult);
         assertTrue(mJpegPictureCallbackResult);
-        assertTrue(mJpegData != null);
+        assertNotNull(mJpegData);
         Bitmap b = BitmapFactory.decodeByteArray(mJpegData, 0, mJpegData.length);
-        assertEquals(b.getWidth(), pictureSize.width);
-        assertEquals(b.getHeight(), pictureSize.height);
+        assertEquals(pictureSize.width, b.getWidth());
+        assertEquals(pictureSize.height, b.getHeight());
     }
 
-    /*
-     * Test case 2: Set the preview and
-     * verify the RawPreviewCallback is called
-     */
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
@@ -377,13 +374,48 @@
         )
     })
     @UiThreadTest
-    public void testCheckPreview() throws Exception {
-        initializeMessageLooper();
-        mCamera.setPreviewCallback(mRawPreviewCallback);
+    public void testPreviewCallback() throws Exception {
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testPreviewCallbackByCamera(id);
+        }
+    }
+
+    private void testPreviewCallbackByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
+        mCamera.setPreviewCallback(mPreviewCallback);
         mCamera.setErrorCallback(mErrorCallback);
         checkPreviewCallback();
         terminateMessageLooper();
-        assertTrue(mRawPreviewCallbackResult);
+        assertTrue(mPreviewCallbackResult);
+
+        mPreviewCallbackResult = false;
+        initializeMessageLooper(cameraId);
+        checkPreviewCallback();
+        terminateMessageLooper();
+        assertFalse(mPreviewCallbackResult);
+
+        // Test all preview sizes.
+        initializeMessageLooper(cameraId);
+        Parameters parameters = mCamera.getParameters();
+        for (Size size: parameters.getSupportedPreviewSizes()) {
+            mPreviewCallbackResult = false;
+            mCamera.setPreviewCallback(mPreviewCallback);
+            parameters.setPreviewSize(size.width, size.height);
+            mCamera.setParameters(parameters);
+            assertEquals(size, mCamera.getParameters().getPreviewSize());
+            checkPreviewCallback();
+            assertTrue(mPreviewCallbackResult);
+            try {
+                // Wait for a while to throw away the remaining preview frames.
+                Thread.sleep(1000);
+            } catch(Exception e) {
+                // ignore
+            }
+            mPreviewDone.close();
+        }
+        terminateMessageLooper();
     }
 
     @TestTargetNew(
@@ -393,17 +425,25 @@
     )
     @UiThreadTest
     public void testSetOneShotPreviewCallback() throws Exception {
-        initializeMessageLooper();
-        mCamera.setOneShotPreviewCallback(mRawPreviewCallback);
-        checkPreviewCallback();
-        terminateMessageLooper();
-        assertTrue(mRawPreviewCallbackResult);
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testSetOneShotPreviewCallbackByCamera(id);
+        }
+    }
 
-        mRawPreviewCallbackResult = false;
-        initializeMessageLooper();
+    private void testSetOneShotPreviewCallbackByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
+        mCamera.setOneShotPreviewCallback(mPreviewCallback);
         checkPreviewCallback();
         terminateMessageLooper();
-        assertFalse(mRawPreviewCallbackResult);
+        assertTrue(mPreviewCallbackResult);
+
+        mPreviewCallbackResult = false;
+        initializeMessageLooper(cameraId);
+        checkPreviewCallback();
+        terminateMessageLooper();
+        assertFalse(mPreviewCallbackResult);
     }
 
     @TestTargetNew(
@@ -413,38 +453,45 @@
     )
     @UiThreadTest
     public void testSetPreviewDisplay() throws Exception {
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = CameraStubActivity.mSurfaceView.getHolder();
-        initializeMessageLooper();
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testSetPreviewDisplayByCamera(id);
+        }
+    }
+
+    private void testSetPreviewDisplayByCamera(int cameraId) throws Exception {
+        SurfaceHolder holder = getActivity().getSurfaceView().getHolder();
+        initializeMessageLooper(cameraId);
 
         // Check the order: startPreview->setPreviewDisplay.
-        mCamera.setOneShotPreviewCallback(mRawPreviewCallback);
+        mCamera.setOneShotPreviewCallback(mPreviewCallback);
         mCamera.startPreview();
-        mCamera.setPreviewDisplay(mSurfaceHolder);
+        mCamera.setPreviewDisplay(holder);
         waitForPreviewDone();
         terminateMessageLooper();
-        assertTrue(mRawPreviewCallbackResult);
+        assertTrue(mPreviewCallbackResult);
 
         // Check the order: setPreviewDisplay->startPreview.
-        initializeMessageLooper();
-        mRawPreviewCallbackResult = false;
-        mCamera.setOneShotPreviewCallback(mRawPreviewCallback);
-        mCamera.setPreviewDisplay(mSurfaceHolder);
+        initializeMessageLooper(cameraId);
+        mPreviewCallbackResult = false;
+        mCamera.setOneShotPreviewCallback(mPreviewCallback);
+        mCamera.setPreviewDisplay(holder);
         mCamera.startPreview();
         waitForPreviewDone();
         mCamera.stopPreview();
-        assertTrue(mRawPreviewCallbackResult);
+        assertTrue(mPreviewCallbackResult);
 
         // Check the order: setting preview display to null->startPreview->
         // setPreviewDisplay.
-        mRawPreviewCallbackResult = false;
-        mCamera.setOneShotPreviewCallback(mRawPreviewCallback);
+        mPreviewCallbackResult = false;
+        mCamera.setOneShotPreviewCallback(mPreviewCallback);
         mCamera.setPreviewDisplay(null);
         mCamera.startPreview();
-        mCamera.setPreviewDisplay(mSurfaceHolder);
+        mCamera.setPreviewDisplay(holder);
         waitForPreviewDone();
         terminateMessageLooper();
-        assertTrue(mRawPreviewCallbackResult);
+        assertTrue(mPreviewCallbackResult);
     }
 
     @TestTargetNew(
@@ -454,7 +501,15 @@
     )
     @UiThreadTest
     public void testDisplayOrientation() throws Exception {
-        initializeMessageLooper();
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testDisplayOrientationByCamera(id);
+        }
+    }
+
+    private void testDisplayOrientationByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
 
         // Check valid arguments.
         mCamera.setDisplayOrientation(0);
@@ -471,9 +526,7 @@
         }
 
         // Start preview.
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = CameraStubActivity.mSurfaceView.getHolder();
-        mCamera.setPreviewDisplay(mSurfaceHolder);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
         mCamera.startPreview();
 
         // Check setting orientation during preview is not allowed.
@@ -500,8 +553,16 @@
         )
     })
     @UiThreadTest
-    public void testAccessParameters() throws Exception {
-        initializeMessageLooper();
+    public void testParameters() throws Exception {
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testParametersByCamera(id);
+        }
+    }
+
+    private void testParametersByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
         // we can get parameters just by getxxx method due to the private constructor
         Parameters pSet = mCamera.getParameters();
         assertParameters(pSet);
@@ -531,10 +592,10 @@
         assertTrue(origPreviewFrameRate > 0);
 
         // The default preview format must be yuv420 (NV21).
-        assertTrue(origPreviewFormat == ImageFormat.NV21);
+        assertEquals(ImageFormat.NV21, origPreviewFormat);
 
         // The default picture format must be Jpeg.
-        assertTrue(origPictureFormat == ImageFormat.JPEG);
+        assertEquals(ImageFormat.JPEG, origPictureFormat);
 
         // If camera supports flash, the default flash mode must be off.
         String flashMode = parameters.getFlashMode();
@@ -551,52 +612,62 @@
         float focalLength = parameters.getFocalLength();
         float horizontalViewAngle = parameters.getHorizontalViewAngle();
         float verticalViewAngle = parameters.getVerticalViewAngle();
+        int jpegQuality = parameters.getJpegQuality();
+        int jpegThumnailQuality = parameters.getJpegThumbnailQuality();
         assertTrue(previewSizes != null && previewSizes.size() != 0);
         assertTrue(pictureSizes != null && pictureSizes.size() != 0);
         assertTrue(previewFormats != null && previewFormats.size() != 0);
         assertTrue(pictureFormats != null && pictureFormats.size() != 0);
         assertTrue(frameRates != null && frameRates.size() != 0);
         assertTrue(focusModes != null && focusModes.size() != 0);
-        assertTrue(focusMode != null);
+        assertNotNull(focusMode);
         assertTrue(focalLength > 0);
         assertTrue(horizontalViewAngle > 0 && horizontalViewAngle <= 360);
         assertTrue(verticalViewAngle > 0 && verticalViewAngle <= 360);
         Size previewSize = previewSizes.get(0);
         Size pictureSize = pictureSizes.get(0);
+        assertTrue(jpegQuality >= 1 && jpegQuality <= 100);
+        assertTrue(jpegThumnailQuality >= 1 && jpegThumnailQuality <= 100);
 
         // If a parameter is supported, both getXXX and getSupportedXXX have to
         // be non null.
         if (parameters.getWhiteBalance() != null) {
-            assertTrue(parameters.getSupportedWhiteBalance() != null);
+            assertNotNull(parameters.getSupportedWhiteBalance());
         }
         if (parameters.getSupportedWhiteBalance() != null) {
-            assertTrue(parameters.getWhiteBalance() != null);
+            assertNotNull(parameters.getWhiteBalance());
         }
         if (parameters.getColorEffect() != null) {
-            assertTrue(parameters.getSupportedColorEffects() != null);
+            assertNotNull(parameters.getSupportedColorEffects());
         }
         if (parameters.getSupportedColorEffects() != null) {
-            assertTrue(parameters.getColorEffect() != null);
+            assertNotNull(parameters.getColorEffect());
         }
         if (parameters.getAntibanding() != null) {
-            assertTrue(parameters.getSupportedAntibanding() != null);
+            assertNotNull(parameters.getSupportedAntibanding());
         }
         if (parameters.getSupportedAntibanding() != null) {
-            assertTrue(parameters.getAntibanding() != null);
+            assertNotNull(parameters.getAntibanding());
         }
         if (parameters.getSceneMode() != null) {
-            assertTrue(parameters.getSupportedSceneModes() != null);
+            assertNotNull(parameters.getSupportedSceneModes());
         }
         if (parameters.getSupportedSceneModes() != null) {
-            assertTrue(parameters.getSceneMode() != null);
+            assertNotNull(parameters.getSceneMode());
         }
         if (parameters.getFlashMode() != null) {
-            assertTrue(parameters.getSupportedFlashModes() != null);
+            assertNotNull(parameters.getSupportedFlashModes());
         }
         if (parameters.getSupportedFlashModes() != null) {
-            assertTrue(parameters.getFlashMode() != null);
+            assertNotNull(parameters.getFlashMode());
         }
 
+        // Check if the sizes value contain invalid characters.
+        assertNoLetters(parameters.get("preview-size-values"), "preview-size-values");
+        assertNoLetters(parameters.get("picture-size-values"), "picture-size-values");
+        assertNoLetters(parameters.get("jpeg-thumbnail-size-values"),
+                "jpeg-thumbnail-size-values");
+
         // Set the parameters.
         parameters.setPictureFormat(PICTURE_FORMAT);
         assertEquals(PICTURE_FORMAT, parameters.getPictureFormat());
@@ -614,20 +685,19 @@
         mCamera.setParameters(parameters);
         Parameters paramActual = mCamera.getParameters();
 
-        // camera may not accept exact parameters, but values must be in valid range
         assertTrue(isValidPixelFormat(paramActual.getPictureFormat()));
-        assertEquals(paramActual.getPictureSize().width, pictureSize.width);
-        assertEquals(paramActual.getPictureSize().height, pictureSize.height);
+        assertEquals(pictureSize.width, paramActual.getPictureSize().width);
+        assertEquals(pictureSize.height, paramActual.getPictureSize().height);
         assertTrue(isValidPixelFormat(paramActual.getPreviewFormat()));
-        assertEquals(paramActual.getPreviewSize().width, previewSize.width);
-        assertEquals(paramActual.getPreviewSize().height, previewSize.height);
+        assertEquals(previewSize.width, paramActual.getPreviewSize().width);
+        assertEquals(previewSize.height, paramActual.getPreviewSize().height);
         assertTrue(paramActual.getPreviewFrameRate() > 0);
 
         checkExposureCompensation(parameters);
     }
 
     private void checkExposureCompensation(Parameters parameters) {
-        assertEquals(parameters.getExposureCompensation(), 0);
+        assertEquals(0, parameters.getExposureCompensation());
         int max = parameters.getMaxExposureCompensation();
         int min = parameters.getMinExposureCompensation();
         float step = parameters.getExposureCompensationStep();
@@ -664,7 +734,15 @@
     })
     @UiThreadTest
     public void testJpegThumbnailSize() throws Exception {
-        initializeMessageLooper();
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testJpegThumbnailSizeByCamera(id);
+        }
+    }
+
+    private void testJpegThumbnailSizeByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
         // Thumbnail size parameters should have valid values.
         Parameters p = mCamera.getParameters();
         Size size = p.getJpegThumbnailSize();
@@ -675,57 +753,58 @@
         assertTrue(sizes.contains(mCamera.new Size(0, 0)));
 
         // Test if the thumbnail size matches the setting.
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = CameraStubActivity.mSurfaceView.getHolder();
-        mCamera.setPreviewDisplay(mSurfaceHolder);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
         mCamera.startPreview();
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
-        assertEquals(mJpegPictureCallbackResult, true);
+        assertTrue(mJpegPictureCallbackResult);
         ExifInterface exif = new ExifInterface(JPEG_PATH);
         assertTrue(exif.hasThumbnail());
         byte[] thumb = exif.getThumbnail();
         Bitmap b = BitmapFactory.decodeByteArray(thumb, 0, thumb.length);
-        assertEquals(b.getWidth(), size.width);
-        assertEquals(b.getHeight(), size.height);
+        assertEquals(size.width, b.getWidth());
+        assertEquals(size.height, b.getHeight());
 
         // Test no thumbnail case.
         p.setJpegThumbnailSize(0, 0);
         mCamera.setParameters(p);
+        Size actual = mCamera.getParameters().getJpegThumbnailSize();
+        assertEquals(0, actual.width);
+        assertEquals(0, actual.height);
         mCamera.startPreview();
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
-        assertEquals(mJpegPictureCallbackResult, true);
+        assertTrue(mJpegPictureCallbackResult);
         exif = new ExifInterface(JPEG_PATH);
-        assertTrue(!exif.hasThumbnail());
+        assertFalse(exif.hasThumbnail());
 
         terminateMessageLooper();
     }
 
     @UiThreadTest
     public void testJpegExif() throws Exception {
-        initializeMessageLooper();
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testJpegExifByCamera(id);
+        }
+    }
+
+    private void testJpegExifByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
         Camera.Parameters parameters = mCamera.getParameters();
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = CameraStubActivity.mSurfaceView.getHolder();
-        mCamera.setPreviewDisplay(mSurfaceHolder);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
         mCamera.startPreview();
         double focalLength = (double)parameters.getFocalLength();
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
         ExifInterface exif = new ExifInterface(JPEG_PATH);
-        assertTrue(exif.getAttribute(ExifInterface.TAG_MAKE) != null);
-        assertTrue(exif.getAttribute(ExifInterface.TAG_MODEL) != null);
-        assertTrue(exif.getAttribute(ExifInterface.TAG_DATETIME) != null);
+        assertNotNull(exif.getAttribute(ExifInterface.TAG_MAKE));
+        assertNotNull(exif.getAttribute(ExifInterface.TAG_MODEL));
+        assertNotNull(exif.getAttribute(ExifInterface.TAG_DATETIME));
         assertTrue(exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) != 0);
         assertTrue(exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) != 0);
-        assertEquals(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE), null);
-        assertEquals(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE), null);
-        assertEquals(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF), null);
-        assertEquals(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF), null);
-        assertEquals(exif.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP), null);
-        assertEquals(exif.getAttribute(ExifInterface.TAG_GPS_DATESTAMP), null);
-        assertEquals(exif.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD), null);
+        checkGpsDataNull(exif);
         double exifFocalLength = (double)exif.getAttributeDouble(
                 ExifInterface.TAG_FOCAL_LENGTH, -1);
         assertEquals(focalLength, exifFocalLength, 0.001);
@@ -742,17 +821,36 @@
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
         exif = new ExifInterface(JPEG_PATH);
-        assertTrue(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) != null);
-        assertTrue(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE) != null);
-        assertTrue(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF) != null);
-        assertTrue(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF) != null);
-        assertTrue(exif.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP) != null);
-        assertTrue(exif.getAttribute(ExifInterface.TAG_GPS_DATESTAMP) != null);
+        assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
+        assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
+        assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF));
+        assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF));
+        assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP));
+        assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_DATESTAMP));
         assertEquals(thirtyTwoCharacters,
                 exif.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD));
+
+        // Test gps tags do not exist after calling removeGpsData.
+        mCamera.startPreview();
+        parameters.removeGpsData();
+        mCamera.setParameters(parameters);
+        mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
+        waitForSnapshotDone();
+        exif = new ExifInterface(JPEG_PATH);
+        checkGpsDataNull(exif);
         terminateMessageLooper();
     }
 
+    private void checkGpsDataNull(ExifInterface exif) {
+        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
+        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
+        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF));
+        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF));
+        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP));
+        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_DATESTAMP));
+        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD));
+    }
+
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
@@ -767,10 +865,18 @@
     })
     @UiThreadTest
     public void testLockUnlock() throws Exception {
-        initializeMessageLooper();
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testLockUnlockByCamera(id);
+        }
+    }
+
+    private void testLockUnlockByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
         Camera.Parameters parameters = mCamera.getParameters();
         SurfaceHolder surfaceHolder;
-        surfaceHolder = CameraStubActivity.mSurfaceView.getHolder();
+        surfaceHolder = getActivity().getSurfaceView().getHolder();
         Size size = parameters.getPreviewSize();
         mCamera.setParameters(parameters);
         mCamera.setPreviewDisplay(surfaceHolder);
@@ -834,41 +940,60 @@
     })
     @UiThreadTest
     public void testPreviewCallbackWithBuffer() throws Exception {
-        initializeMessageLooper();
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testPreviewCallbackWithBufferByCamera(id);
+        }
+    }
+
+    private void testPreviewCallbackWithBufferByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
         SurfaceHolder surfaceHolder;
-        surfaceHolder = CameraStubActivity.mSurfaceView.getHolder();
+        surfaceHolder = getActivity().getSurfaceView().getHolder();
         mCamera.setPreviewDisplay(surfaceHolder);
-        Size size = mCamera.getParameters().getPreviewSize();
+        Parameters parameters = mCamera.getParameters();
         PreviewCallbackWithBuffer callback = new PreviewCallbackWithBuffer();
-        callback.mBuffer1 = new byte[size.width * size.height * 3 / 2 + 1];
-        callback.mBuffer2 = new byte[size.width * size.height * 3 / 2 + 1];
-        callback.mBuffer3 = new byte[size.width * size.height * 3 / 2 + 1];
+        // Test all preview sizes.
+        for (Size size: parameters.getSupportedPreviewSizes()) {
+            parameters.setPreviewSize(size.width, size.height);
+            mCamera.setParameters(parameters);
+            assertEquals(size, mCamera.getParameters().getPreviewSize());
+            callback.mNumCbWithBuffer1 = 0;
+            callback.mNumCbWithBuffer2 = 0;
+            callback.mNumCbWithBuffer3 = 0;
+            callback.mBuffer1 = new byte[size.width * size.height * 3 / 2];
+            callback.mBuffer2 = new byte[size.width * size.height * 3 / 2];
+            callback.mBuffer3 = new byte[size.width * size.height * 3 / 2];
 
-        // Test if we can get the preview callbacks with specified buffers.
-        mCamera.addCallbackBuffer(callback.mBuffer1);
-        mCamera.addCallbackBuffer(callback.mBuffer2);
-        mCamera.setPreviewCallbackWithBuffer(callback);
-        mCamera.startPreview();
-        waitForPreviewDone();
-        assertEquals(1, callback.mNumCbWithBuffer1);
-        assertEquals(1, callback.mNumCbWithBuffer2);
-        assertEquals(0, callback.mNumCbWithBuffer3);
+            // Test if we can get the preview callbacks with specified buffers.
+            mCamera.addCallbackBuffer(callback.mBuffer1);
+            mCamera.addCallbackBuffer(callback.mBuffer2);
+            mCamera.setPreviewCallbackWithBuffer(callback);
+            mCamera.startPreview();
+            waitForPreviewDone();
+            assertEquals(1, callback.mNumCbWithBuffer1);
+            assertEquals(1, callback.mNumCbWithBuffer2);
+            assertEquals(0, callback.mNumCbWithBuffer3);
 
-        // Test if preview callback with buffer still works during preview.
-        callback.mNumCbWithBuffer1 = callback.mNumCbWithBuffer2 = 0;
-        mCamera.addCallbackBuffer(callback.mBuffer3);
-        waitForPreviewDone();
-        assertEquals(0, callback.mNumCbWithBuffer1);
-        assertEquals(0, callback.mNumCbWithBuffer2);
-        assertEquals(1, callback.mNumCbWithBuffer3);
+            // Test if preview callback with buffer still works during preview.
+            mCamera.addCallbackBuffer(callback.mBuffer3);
+            waitForPreviewDone();
+            assertEquals(1, callback.mNumCbWithBuffer1);
+            assertEquals(1, callback.mNumCbWithBuffer2);
+            assertEquals(1, callback.mNumCbWithBuffer3);
+            mCamera.setPreviewCallbackWithBuffer(null);
+            mCamera.stopPreview();
+        }
         terminateMessageLooper();
     }
 
-    private final class PreviewCallbackWithBuffer implements PreviewCallback {
+    private final class PreviewCallbackWithBuffer
+            implements android.hardware.Camera.PreviewCallback {
         public int mNumCbWithBuffer1, mNumCbWithBuffer2, mNumCbWithBuffer3;
         public byte[] mBuffer1, mBuffer2, mBuffer3;
         public void onPreviewFrame(byte[] data, Camera camera) {
-            assert(data != null);
+            assertNotNull(data);
             if (data == mBuffer1) {
                 mNumCbWithBuffer1++;
             } else if (data == mBuffer2) {
@@ -905,55 +1030,63 @@
     })
     @UiThreadTest
     public void testZoom() throws Exception {
-        initializeMessageLooper();
-        testImmediateZoom();
-        testSmoothZoom();
-        terminateMessageLooper();
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            initializeMessageLooper(id);
+            testImmediateZoom();
+            testSmoothZoom();
+            terminateMessageLooper();
+        }
     }
 
     private void testImmediateZoom() throws Exception {
         Parameters parameters = mCamera.getParameters();
         if (!parameters.isZoomSupported()) return;
 
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
+
         // Test the zoom parameters.
-        assertEquals(parameters.getZoom(), 0);  // default zoom should be 0.
-        int maxZoom = parameters.getMaxZoom();
-        assertTrue(maxZoom >= 0);
-        if (maxZoom > 0) {
+        assertEquals(0, parameters.getZoom());  // default zoom should be 0.
+        for (Size size: parameters.getSupportedPreviewSizes()) {
+            parameters = mCamera.getParameters();
+            parameters.setPreviewSize(size.width, size.height);
+            mCamera.setParameters(parameters);
+            parameters = mCamera.getParameters();
+            int maxZoom = parameters.getMaxZoom();
+            assertTrue(maxZoom >= 0);
+
             // Zoom ratios should be sorted from small to large.
             List<Integer> ratios = parameters.getZoomRatios();
-            assertEquals(ratios.size(), maxZoom + 1);
-            assertEquals(ratios.get(0).intValue(), 100);
+            assertEquals(maxZoom + 1, ratios.size());
+            assertEquals(100, ratios.get(0).intValue());
             for (int i = 0; i < ratios.size() - 1; i++) {
                 assertTrue(ratios.get(i) < ratios.get(i + 1));
             }
-        }
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = CameraStubActivity.mSurfaceView.getHolder();
-        mCamera.setPreviewDisplay(mSurfaceHolder);
-        mCamera.startPreview();
-        waitForPreviewDone();
+            mCamera.startPreview();
+            waitForPreviewDone();
 
-        // Test each zoom step.
-        for (int i = 0; i <= maxZoom; i++) {
-            parameters.setZoom(i);
-            mCamera.setParameters(parameters);
-            assertEquals(i, parameters.getZoom());
-        }
+            // Test each zoom step.
+            for (int i = 0; i <= maxZoom; i++) {
+                parameters.setZoom(i);
+                mCamera.setParameters(parameters);
+                assertEquals(i, mCamera.getParameters().getZoom());
+            }
 
-        // It should throw exception if an invalid value is passed.
-        try {
-            parameters.setZoom(maxZoom + 1);
-            mCamera.setParameters(parameters);
-            fail("setZoom should throw exception.");
-        } catch (RuntimeException e) {
-            // expected
-        }
-        parameters = mCamera.getParameters();
-        assertEquals(maxZoom, parameters.getZoom());
+            // It should throw exception if an invalid value is passed.
+            try {
+                parameters.setZoom(maxZoom + 1);
+                mCamera.setParameters(parameters);
+                fail("setZoom should throw exception.");
+            } catch (RuntimeException e) {
+                // expected
+            }
+            assertEquals(maxZoom, mCamera.getParameters().getZoom());
 
-        mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
-        waitForSnapshotDone();
+            mCamera.takePicture(mShutterCallback, mRawPictureCallback,
+                                mJpegPictureCallback);
+            waitForSnapshotDone();
+        }
     }
 
     private void testSmoothZoom() throws Exception {
@@ -961,10 +1094,8 @@
         if (!parameters.isSmoothZoomSupported()) return;
         assertTrue(parameters.isZoomSupported());
 
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = CameraStubActivity.mSurfaceView.getHolder();
         ZoomListener zoomListener = new ZoomListener();
-        mCamera.setPreviewDisplay(mSurfaceHolder);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
         mCamera.setZoomChangeListener(zoomListener);
         mCamera.startPreview();
         waitForPreviewDone();
@@ -973,8 +1104,10 @@
         int maxZoom = parameters.getMaxZoom();
         parameters.setZoom(maxZoom);
         mCamera.setParameters(parameters);
+        assertEquals(maxZoom, mCamera.getParameters().getZoom());
         parameters.setZoom(0);
         mCamera.setParameters(parameters);
+        assertEquals(0, mCamera.getParameters().getZoom());
         assertFalse(zoomListener.mZoomDone.block(500));
 
         // Nothing will happen if zoom is not moving.
@@ -1000,7 +1133,7 @@
             Log.e(TAG, "zoomListener.mStopped = " + zoomListener.mStopped);
             zoomListener.mZoomDone.close();
             mCamera.startSmoothZoom(maxZoom / 2);
-            assertEquals(true, zoomListener.mZoomDone.block(5000));
+            assertTrue(zoomListener.mZoomDone.block(5000));
             assertEquals(maxZoom - (maxZoom / 2), zoomListener.mValues.size());
             int i = maxZoom - 1;
             for(Integer value: zoomListener.mValues) {
@@ -1023,6 +1156,7 @@
         zoomListener.mZoomDone.close();
         parameters.setZoom(0);
         mCamera.setParameters(parameters);
+        assertEquals(0, mCamera.getParameters().getZoom());
         mCamera.startSmoothZoom(maxZoom);
         mCamera.stopSmoothZoom();
         assertTrue(zoomListener.mZoomDone.block(5000));
@@ -1040,12 +1174,484 @@
 
         public void onZoomChange(int value, boolean stopped, Camera camera) {
             mValues.add(value);
-            assertEquals(value, camera.getParameters().getZoom());
-            assertEquals(false, mStopped);
+            assertFalse(mStopped);
             mStopped = stopped;
             if (stopped) {
                 mZoomDone.open();
             }
         }
     }
+
+    @UiThreadTest
+    public void testFocusDistances() throws Exception {
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testFocusDistancesByCamera(id);
+        }
+    }
+
+    private void testFocusDistancesByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
+        mCamera.startPreview();
+        waitForPreviewDone();
+        Parameters parameters = mCamera.getParameters();
+
+        // Test every supported focus mode.
+        for (String focusMode: parameters.getSupportedFocusModes()) {
+            parameters.setFocusMode(focusMode);
+            mCamera.setParameters(parameters);
+            parameters = mCamera.getParameters();
+            assertEquals(focusMode, parameters.getFocusMode());
+            checkFocusDistances(parameters);
+            if (Parameters.FOCUS_MODE_AUTO.equals(focusMode)
+                    || Parameters.FOCUS_MODE_MACRO.equals(focusMode)) {
+                mCamera.autoFocus(mAutoFocusCallback);
+                assertTrue(waitForFocusDone());
+                parameters = mCamera.getParameters();
+                checkFocusDistances(parameters);
+            }
+        }
+
+        // Test if the method throws exception if the argument is invalid.
+        try {
+            parameters.getFocusDistances(null);
+            fail("getFocusDistances should not accept null.");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            parameters.getFocusDistances(new float[2]);
+            fail("getFocusDistances should not accept a float array with two elements.");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            parameters.getFocusDistances(new float[4]);
+            fail("getFocusDistances should not accept a float array with four elements.");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        terminateMessageLooper();
+    }
+
+    private void checkFocusDistances(Parameters parameters) {
+        float[] distances = new float[3];
+        parameters.getFocusDistances(distances);
+
+        // Focus distances should be greater than 0.
+        assertTrue(distances[Parameters.FOCUS_DISTANCE_NEAR_INDEX] > 0);
+        assertTrue(distances[Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX] > 0);
+        assertTrue(distances[Parameters.FOCUS_DISTANCE_FAR_INDEX] > 0);
+
+        // Make sure far focus distance >= optimal focus distance >= near focus distance.
+        assertTrue(distances[Parameters.FOCUS_DISTANCE_FAR_INDEX] >=
+                   distances[Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX]);
+        assertTrue(distances[Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX] >=
+                   distances[Parameters.FOCUS_DISTANCE_NEAR_INDEX]);
+
+        // Far focus distance should be infinity in infinity focus mode.
+        if (Parameters.FOCUS_MODE_INFINITY.equals(parameters.getFocusMode())) {
+            assertEquals(Float.POSITIVE_INFINITY,
+                         distances[Parameters.FOCUS_DISTANCE_FAR_INDEX]);
+        }
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "cancelAutofocus",
+            args = {}
+        )
+    })
+    @UiThreadTest
+    public void testCancelAutofocus() throws Exception {
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testCancelAutofocusByCamera(id);
+        }
+    }
+
+    private void testCancelAutofocusByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
+        mCamera.startPreview();
+
+        // No op if autofocus is not in progress.
+        mCamera.cancelAutoFocus();
+
+        // Try to cancel autofocus immediately.
+        mCamera.autoFocus(mAutoFocusCallback);
+        mCamera.cancelAutoFocus();
+        checkFocusDistanceNotChanging();
+
+        // Try to cancel autofocus after it starts for some time.
+        mCamera.autoFocus(mAutoFocusCallback);
+        Thread.sleep(500);
+        mCamera.cancelAutoFocus();
+        checkFocusDistanceNotChanging();
+
+        // Try to cancel autofocus after it completes. It should be no op.
+        mCamera.autoFocus(mAutoFocusCallback);
+        assertTrue(waitForFocusDone());
+        mCamera.cancelAutoFocus();
+
+        // Test the case calling cancelAutoFocus and release in a row.
+        mCamera.autoFocus(mAutoFocusCallback);
+        mCamera.cancelAutoFocus();
+        mCamera.release();
+
+        // Ensure the camera can be opened if release is called right after AF.
+        mCamera = Camera.open(cameraId);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
+        mCamera.startPreview();
+        mCamera.autoFocus(mAutoFocusCallback);
+        mCamera.release();
+
+        terminateMessageLooper();
+    }
+
+    private void checkFocusDistanceNotChanging() throws Exception {
+        float[] distances1 = new float[3];
+        float[] distances2 = new float[3];
+        Parameters parameters = mCamera.getParameters();
+        parameters.getFocusDistances(distances1);
+        Thread.sleep(100);
+        parameters = mCamera.getParameters();
+        parameters.getFocusDistances(distances2);
+        assertEquals(distances1[Parameters.FOCUS_DISTANCE_NEAR_INDEX],
+                     distances2[Parameters.FOCUS_DISTANCE_NEAR_INDEX]);
+        assertEquals(distances1[Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX],
+                     distances2[Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX]);
+        assertEquals(distances1[Parameters.FOCUS_DISTANCE_FAR_INDEX],
+                     distances2[Parameters.FOCUS_DISTANCE_FAR_INDEX]);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getNumberOfCameras",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getCameraInfo",
+            args = {int.class, CameraInfo.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "open",
+            args = {int.class}
+        )
+    })
+    @UiThreadTest
+    public void testMultipleCameras() throws Exception {
+        int nCameras = Camera.getNumberOfCameras();
+        Log.v(TAG, "total " + nCameras + " cameras");
+        assertTrue(nCameras >= 0);
+
+        boolean backCameraExist = false;
+        CameraInfo info = new CameraInfo();
+        for (int i = 0; i < nCameras; i++) {
+            Camera.getCameraInfo(i, info);
+            if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
+                backCameraExist = true;
+                break;
+            }
+        }
+        // Make sure original open still works. It must return a back-facing
+        // camera.
+        mCamera = Camera.open();
+        if (mCamera != null) {
+            mCamera.release();
+            assertTrue(backCameraExist);
+        } else {
+            assertFalse(backCameraExist);
+        }
+
+        for (int id = -1; id <= nCameras; id++) {
+            Log.v(TAG, "testing camera #" + id);
+
+            boolean isBadId = (id < 0 || id >= nCameras);
+
+            try {
+                Camera.getCameraInfo(id, info);
+                if (isBadId) {
+                    fail("getCameraInfo should not accept bad cameraId (" + id + ")");
+                }
+            } catch (RuntimeException e) {
+                if (!isBadId) throw e;
+            }
+
+            int facing = info.facing;
+            int orientation = info.orientation;
+            assertTrue(facing == CameraInfo.CAMERA_FACING_BACK ||
+                       facing == CameraInfo.CAMERA_FACING_FRONT);
+            assertTrue(orientation == 0 || orientation == 90 ||
+                       orientation == 180 || orientation == 270);
+
+            Camera camera = null;
+            try {
+                camera = Camera.open(id);
+                if (isBadId) {
+                    fail("open() should not accept bad cameraId (" + id + ")");
+                }
+            } catch (RuntimeException e) {
+                if (!isBadId) throw e;
+            } finally {
+                if (camera != null) {
+                    camera.release();
+                }
+            }
+        }
+    }
+
+    @UiThreadTest
+    public void testPreviewPictureSizesCombination() throws Exception {
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testPreviewPictureSizesCombinationByCamera(id);
+        }
+    }
+
+    private void testPreviewPictureSizesCombinationByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
+        Parameters parameters = mCamera.getParameters();
+        PreviewCbForPreviewPictureSizesCombination callback =
+            new PreviewCbForPreviewPictureSizesCombination();
+
+        // Test all combination of preview sizes and picture sizes.
+        for (Size previewSize: parameters.getSupportedPreviewSizes()) {
+            for (Size pictureSize: parameters.getSupportedPictureSizes()) {
+                Log.v(TAG, "Test previewSize=(" + previewSize.width + "," +
+                        previewSize.height + ") pictureSize=(" +
+                        pictureSize.width + "," + pictureSize.height + ")");
+                mPreviewCallbackResult = false;
+                mCamera.setPreviewCallback(callback);
+                callback.expectedPreviewSize = previewSize;
+                parameters.setPreviewSize(previewSize.width, previewSize.height);
+                parameters.setPictureSize(pictureSize.width, pictureSize.height);
+                mCamera.setParameters(parameters);
+                assertEquals(previewSize, mCamera.getParameters().getPreviewSize());
+                assertEquals(pictureSize, mCamera.getParameters().getPictureSize());
+
+                // Check if the preview size is the same as requested.
+                mCamera.startPreview();
+                waitForPreviewDone();
+                assertTrue(mPreviewCallbackResult);
+
+                // Check if the picture size is the same as requested.
+                mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
+                waitForSnapshotDone();
+                assertTrue(mJpegPictureCallbackResult);
+                assertNotNull(mJpegData);
+                Bitmap b = BitmapFactory.decodeByteArray(mJpegData, 0, mJpegData.length);
+                assertEquals(pictureSize.width, b.getWidth());
+                assertEquals(pictureSize.height, b.getHeight());
+                b.recycle();
+                b = null;
+            }
+        }
+        terminateMessageLooper();
+    }
+
+    private final class PreviewCbForPreviewPictureSizesCombination
+            implements android.hardware.Camera.PreviewCallback {
+        public Size expectedPreviewSize;
+        public void onPreviewFrame(byte[] data, Camera camera) {
+            assertNotNull(data);
+            Size size = camera.getParameters().getPreviewSize();
+            assertEquals(expectedPreviewSize, size);
+            assertEquals(size.width * size.height * 3 / 2, data.length);
+            camera.setPreviewCallback(null);
+            mPreviewCallbackResult = true;
+            mPreviewDone.open();
+        }
+    }
+
+    public void testPreviewFpsRange() throws Exception {
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testPreviewFpsRangeByCamera(id);
+        }
+    }
+
+    private void testPreviewFpsRangeByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
+        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
+
+        // Test if the parameters exists and minimum fps <= maximum fps.
+        int[] defaultFps = new int[2];
+        Parameters parameters = mCamera.getParameters();
+        parameters.getPreviewFpsRange(defaultFps);
+        List<int[]> fpsList = parameters.getSupportedPreviewFpsRange();
+        assertTrue(fpsList.size() > 0);
+        boolean found = false;
+        for(int[] fps: fpsList) {
+            assertTrue(fps[Parameters.PREVIEW_FPS_MIN_INDEX] > 0);
+            assertTrue(fps[Parameters.PREVIEW_FPS_MIN_INDEX] <=
+                       fps[Parameters.PREVIEW_FPS_MAX_INDEX]);
+            if (!found && Arrays.equals(defaultFps, fps)) {
+                found = true;
+            }
+        }
+        assertTrue("Preview fps range must be in the supported list.", found);
+
+        // Test if the list is properly sorted.
+        for (int i = 0; i < fpsList.size() - 1; i++) {
+            int minFps1 = fpsList.get(i)[Parameters.PREVIEW_FPS_MIN_INDEX];
+            int maxFps1 = fpsList.get(i)[Parameters.PREVIEW_FPS_MAX_INDEX];
+            int minFps2 = fpsList.get(i + 1)[Parameters.PREVIEW_FPS_MIN_INDEX];
+            int maxFps2 = fpsList.get(i + 1)[Parameters.PREVIEW_FPS_MAX_INDEX];
+            assertTrue(maxFps1 < maxFps2
+                    || (maxFps1 == maxFps2 && minFps1 < minFps2));
+        }
+
+        // Test if the actual fps is within fps range.
+        Size size = parameters.getPreviewSize();
+        byte[] buffer1 = new byte[size.width * size.height * 3 / 2];
+        byte[] buffer2 = new byte[size.width * size.height * 3 / 2];
+        byte[] buffer3 = new byte[size.width * size.height * 3 / 2];
+        FpsRangePreviewCb callback = new FpsRangePreviewCb();
+        int[] readBackFps = new int[2];
+        for (int[] fps: fpsList) {
+            parameters = mCamera.getParameters();
+            parameters.setPreviewFpsRange(fps[Parameters.PREVIEW_FPS_MIN_INDEX],
+                                          fps[Parameters.PREVIEW_FPS_MAX_INDEX]);
+            callback.reset(fps[Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.0,
+                           fps[Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.0);
+            mCamera.setParameters(parameters);
+            parameters = mCamera.getParameters();
+            parameters.getPreviewFpsRange(readBackFps);
+            MoreAsserts.assertEquals(fps, readBackFps);
+            mCamera.addCallbackBuffer(buffer1);
+            mCamera.addCallbackBuffer(buffer2);
+            mCamera.addCallbackBuffer(buffer3);
+            mCamera.setPreviewCallbackWithBuffer(callback);
+            mCamera.startPreview();
+            try {
+                // Test the frame rate for a while.
+                Thread.sleep(3000);
+            } catch(Exception e) {
+                // ignore
+            }
+            mCamera.stopPreview();
+        }
+
+        // Test the invalid fps cases.
+        parameters = mCamera.getParameters();
+        parameters.setPreviewFpsRange(-1, -1);
+        try {
+            mCamera.setParameters(parameters);
+            fail("Should throw an exception if fps range is negative.");
+        } catch (RuntimeException e) {
+            // expected
+        }
+        parameters.setPreviewFpsRange(10, 5);
+        try {
+            mCamera.setParameters(parameters);
+            fail("Should throw an exception if fps range is invalid.");
+        } catch (RuntimeException e) {
+            // expected
+        }
+
+        terminateMessageLooper();
+    }
+
+    private final class FpsRangePreviewCb
+            implements android.hardware.Camera.PreviewCallback {
+        private double mMinFps, mMaxFps, mMaxFrameInterval, mMinFrameInterval;
+        // An array storing the arrival time of the frames in the last second.
+        private ArrayList<Long> mFrames = new ArrayList<Long>();
+        private long firstFrameArrivalTime;
+
+        public void reset(double minFps, double maxFps) {
+            this.mMinFps = minFps;
+            this.mMaxFps = maxFps;
+            mMaxFrameInterval = 1000.0 / mMinFps;
+            mMinFrameInterval = 1000.0 / mMaxFps;
+            Log.v(TAG, "Min fps=" + mMinFps + ". Max fps=" + mMaxFps
+                    + ". Min frame interval=" + mMinFrameInterval
+                    + ". Max frame interval=" + mMaxFrameInterval);
+            mFrames.clear();
+            firstFrameArrivalTime = 0;
+        }
+
+        // This method tests if the actual fps is between minimum and maximum.
+        // It also tests if the frame interval is too long.
+        public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
+            long arrivalTime = System.currentTimeMillis();
+            camera.addCallbackBuffer(data);
+            if (firstFrameArrivalTime == 0) firstFrameArrivalTime = arrivalTime;
+
+            // Remove the frames that arrived before the last second.
+            Iterator<Long> it = mFrames.iterator();
+            while(it.hasNext()) {
+                long time = it.next();
+                if (arrivalTime - time > 1000 && mFrames.size() > 2) {
+                    it.remove();
+                } else {
+                    break;
+                }
+            }
+
+            // Start the test after one second.
+            if (arrivalTime - firstFrameArrivalTime > 1000) {
+                assertTrue(mFrames.size() >= 2);
+
+                // Check the frame interval and fps. The interval check
+                // considers the time variance passing frames from the camera
+                // hardware to the callback. It should be a constant time, not a
+                // ratio. The fps check is more strict because individual
+                // variance is averaged out.
+
+                // Check if the frame interval is too large or too small.
+                double intervalMargin = 30;  // ms
+                long lastArrivalTime = mFrames.get(mFrames.size() - 1);
+                double interval = arrivalTime - lastArrivalTime;
+                if (LOGV) Log.v(TAG, "Frame interval=" + interval);
+                assertTrue("Frame interval (" + interval + "ms) is too large." +
+                        " mMaxFrameInterval=" + mMaxFrameInterval + "ms",
+                        interval < mMaxFrameInterval + intervalMargin);
+                assertTrue("Frame interval (" + interval + "ms) is too small." +
+                        " mMinFrameInterval=" + mMinFrameInterval + "ms",
+                        interval > mMinFrameInterval - intervalMargin);
+
+                // Check if the fps is within range.
+                double fpsMargin = 0.03;
+                double avgInterval = (double)(arrivalTime - mFrames.get(0))
+                        / mFrames.size();
+                double fps = 1000.0 / avgInterval;
+                assertTrue("Actual fps (" + fps + ") should be larger than " +
+                           "min fps (" + mMinFps + ")",
+                           fps >= mMinFps * (1 - fpsMargin));
+                assertTrue("Actual fps (" + fps + ") should be smaller than " +
+                           "max fps (" + mMaxFps + ")",
+                           fps <= mMaxFps * (1 + fpsMargin));
+            }
+            // Add the arrival time of this frame to the list.
+            mFrames.add(arrivalTime);
+        }
+    }
+
+    private void assertEquals(Size expected, Size actual) {
+        assertEquals(expected.width, actual.width);
+        assertEquals(expected.height, actual.height);
+    }
+
+    private void assertNoLetters(String value, String key) {
+        for (int i = 0; i < value.length(); i++) {
+            char c = value.charAt(i);
+            assertFalse("Parameter contains invalid characters. key,value=("
+                    + key + "," + value + ")",
+                    Character.isLetter(c) && c != 'x');
+        }
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/Camera_SizeTest.java b/tests/tests/hardware/src/android/hardware/cts/Camera_SizeTest.java
index 028b819..0b7b69f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/Camera_SizeTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/Camera_SizeTest.java
@@ -43,12 +43,18 @@
         args = {int.class, int.class}
     )
     public void testConstructor() {
-        Camera camera = Camera.open();
+        if (Camera.getNumberOfCameras() < 1) {
+            return;
+        }
+
+        Camera camera = Camera.open(0);
         Parameters parameters = camera.getParameters();
 
         checkSize(parameters, WIDTH1, HEIGHT1);
         checkSize(parameters, WIDTH2, HEIGHT2);
         checkSize(parameters, WIDTH3, HEIGHT3);
+
+        camera.release();
     }
 
     private void checkSize(Parameters parameters, int width, int height) {
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 5909a57..5ae2ee9 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -19,6 +19,7 @@
 import java.util.List;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.test.AndroidTestCase;
@@ -75,16 +76,33 @@
         List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
         assertNotNull(sensors);
         Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-        assertEquals(Sensor.TYPE_ACCELEROMETER, sensor.getType());
-        assertSensorValues(sensor);
+        boolean hasAccelerometer = getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_SENSOR_ACCELEROMETER);
+        // accelerometer sensor is optional
+        if (hasAccelerometer) {
+            assertEquals(Sensor.TYPE_ACCELEROMETER, sensor.getType());
+            assertSensorValues(sensor);
+        } else {
+            assertNull(sensor);
+        }
 
         sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
-        assertEquals(Sensor.TYPE_MAGNETIC_FIELD, sensor.getType());
-        assertSensorValues(sensor);
+        boolean hasCompass = getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_SENSOR_COMPASS);
+        // compass sensor is optional
+        if (hasCompass) {
+            assertEquals(Sensor.TYPE_MAGNETIC_FIELD, sensor.getType());
+            assertSensorValues(sensor);
+        } else {
+            assertNull(sensor);
+        }
 
         sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
-        assertEquals(Sensor.TYPE_ORIENTATION, sensor.getType());
-        assertSensorValues(sensor);
+        // orientation sensor is required if the device can physically implement it
+        if (hasCompass && hasAccelerometer) {
+            assertEquals(Sensor.TYPE_ORIENTATION, sensor.getType());
+            assertSensorValues(sensor);
+        }
 
         sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_TEMPERATURE);
         // temperature sensor is optional
diff --git a/tests/tests/jni/libjnitest/helper.h b/tests/tests/jni/libjnitest/helper.h
index 6771d2f..58a3407 100644
--- a/tests/tests/jni/libjnitest/helper.h
+++ b/tests/tests/jni/libjnitest/helper.h
@@ -47,7 +47,7 @@
  * @param ... printf-style arguments
  * @return an allocated (char *) containing the formatted result
  */
-char *failure(const char *format, ...);
+char *failure(const char *format, ...) __attribute__((format(printf, 1, 2)));
 
 /**
  * Runs a list of tests. It will run all the tests, collecting as output
diff --git a/tests/tests/location/src/android/location/cts/AddressTest.java b/tests/tests/location/src/android/location/cts/AddressTest.java
index 5b17e39..5c77d94 100644
--- a/tests/tests/location/src/android/location/cts/AddressTest.java
+++ b/tests/tests/location/src/android/location/cts/AddressTest.java
@@ -548,7 +548,10 @@
         assertEquals(address.getAdminArea(), parcel.readString());
         assertEquals(address.getSubAdminArea(), parcel.readString());
         assertEquals(address.getLocality(), parcel.readString());
+        assertEquals(address.getSubLocality(), parcel.readString());
         assertEquals(address.getThoroughfare(), parcel.readString());
+        assertEquals(address.getSubThoroughfare(), parcel.readString());
+        assertEquals(address.getPremises(), parcel.readString());
         assertEquals(address.getPostalCode(), parcel.readString());
         assertEquals(address.getCountryCode(), parcel.readString());
         assertEquals(address.getCountryName(), parcel.readString());
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index a917d1d..51623d0 100755
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.location.Criteria;
 import android.location.GpsStatus;
 import android.location.Location;
@@ -186,7 +187,7 @@
         assertTrue(providers.size() >= 2);
         assertTrue(hasTestProvider(providers));
 
-        assertTrue(hasGpsProvider(providers));
+        assertEquals(hasGpsFeature(), hasGpsProvider(providers));
 
         int oldSizeAllProviders = providers.size();
 
@@ -222,6 +223,11 @@
         return hasProvider(providers, LocationManager.GPS_PROVIDER);
     }
 
+    private boolean hasGpsFeature() {
+        return mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_LOCATION_GPS);
+    }
+
     private boolean hasProvider(List<String> providers, String providerName) {
         for (String provider : providers) {
             if (provider != null && provider.equals(providerName)) {
@@ -242,8 +248,12 @@
         assertEquals(TEST_MOCK_PROVIDER_NAME, p.getName());
 
         p = mManager.getProvider(LocationManager.GPS_PROVIDER);
-        assertNotNull(p);
-        assertEquals(LocationManager.GPS_PROVIDER, p.getName());
+        if (hasGpsFeature()) {
+            assertNotNull(p);
+            assertEquals(LocationManager.GPS_PROVIDER, p.getName());
+        } else {
+            assertNull(p);
+        }
 
         p = mManager.getProvider(UNKNOWN_PROVIDER_NAME);
         assertNull(p);
@@ -475,13 +485,6 @@
         }
 
         try {
-            mManager.requestLocationUpdates(TEST_MOCK_PROVIDER_NAME, 0, 0, listener, null);
-            fail("Should throw IllegalArgumentException if param looper is null!");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-
-        try {
             mManager.removeUpdates( (LocationListener) null );
             fail("Should throw IllegalArgumentException if listener is null!");
         } catch (IllegalArgumentException e) {
diff --git a/tests/tests/location/src/android/location/cts/LocationProviderTest.java b/tests/tests/location/src/android/location/cts/LocationProviderTest.java
index 3625684..9e53583 100644
--- a/tests/tests/location/src/android/location/cts/LocationProviderTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationProviderTest.java
@@ -28,6 +28,8 @@
 
 @TestTargetClass(LocationProvider.class)
 public class LocationProviderTest extends AndroidTestCase {
+    private static final String PROVIDER_NAME = "location_provider_test";
+
     private LocationManager mLocationManager;
 
     @Override
@@ -35,6 +37,31 @@
         super.setUp();
         mLocationManager =
             (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
+        addTestProvider(PROVIDER_NAME);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mLocationManager.removeTestProvider(PROVIDER_NAME);
+        super.tearDown();
+    }
+
+    /**
+     * Adds a test provider with the given name.
+     */
+    private void addTestProvider(String providerName) {
+        mLocationManager.addTestProvider(
+                providerName,
+                true,  // requiresNetwork,
+                false, // requiresSatellite,
+                false, // requiresCell,
+                false, // hasMonetaryCost,
+                true,  // supportsAltitude,
+                false, // supportsSpeed,
+                true,  // supportsBearing,
+                Criteria.POWER_MEDIUM,   // powerRequirement,
+                Criteria.ACCURACY_FINE); // accuracy
+        mLocationManager.setTestProviderEnabled(providerName, true);
     }
 
     @TestTargetNew(
@@ -43,9 +70,8 @@
         args = {}
     )
     public void testGetName() {
-        String name = "gps";
-        LocationProvider locationProvider = mLocationManager.getProvider(name);
-        assertEquals(name, locationProvider.getName());
+        LocationProvider locationProvider = mLocationManager.getProvider(PROVIDER_NAME);
+        assertEquals(PROVIDER_NAME, locationProvider.getName());
     }
 
     @TestTargetNew(
@@ -54,7 +80,7 @@
         args = {android.location.Criteria.class}
     )
     public void testMeetsCriteria() {
-        LocationProvider locationProvider = mLocationManager.getProvider("gps");
+        LocationProvider locationProvider = mLocationManager.getProvider(PROVIDER_NAME);
 
         Criteria criteria = new Criteria();
         criteria.setAltitudeRequired(true);
diff --git a/tests/tests/location/src/android/location/cts/LocationTest.java b/tests/tests/location/src/android/location/cts/LocationTest.java
index 531a949..2855436 100644
--- a/tests/tests/location/src/android/location/cts/LocationTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationTest.java
@@ -29,6 +29,8 @@
 import android.util.Printer;
 import android.util.StringBuilderPrinter;
 
+import java.text.DecimalFormat;
+
 @TestTargetClass(Location.class)
 public class LocationTest extends AndroidTestCase {
     private static final float DELTA = 0.1f;
@@ -141,22 +143,23 @@
         args = {double.class, int.class}
     )
     public void testConvert_CoordinateToRepresentation() {
+        DecimalFormat df = new DecimalFormat("###.#####");
         String result;
 
         result = Location.convert(-80.0, Location.FORMAT_DEGREES);
-        assertEquals("-80", result);
+        assertEquals("-" + df.format(80.0), result);
 
         result = Location.convert(-80.085, Location.FORMAT_MINUTES);
-        assertEquals("-80:5.1", result);
+        assertEquals("-80:" + df.format(5.1), result);
 
         result = Location.convert(-80, Location.FORMAT_MINUTES);
-        assertEquals("-80:0", result);
+        assertEquals("-80:" + df.format(0), result);
 
         result = Location.convert(-80.075, Location.FORMAT_MINUTES);
-        assertEquals("-80:4.5", result);
+        assertEquals("-80:" + df.format(4.5), result);
 
         result = Location.convert(-80.075, Location.FORMAT_DEGREES);
-        assertEquals("-80.075", result);
+        assertEquals("-" + df.format(80.075), result);
 
         result = Location.convert(-80.075, Location.FORMAT_SECONDS);
         assertEquals("-80:4:30", result);
diff --git a/tests/tests/media/src/android/media/cts/AudioEffectTest.java b/tests/tests/media/src/android/media/cts/AudioEffectTest.java
new file mode 100644
index 0000000..0aaf11f
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/AudioEffectTest.java
@@ -0,0 +1,1381 @@
+/*
+ * Copyright (C) 2010 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.media.cts;
+
+import com.android.cts.stub.R;
+
+import android.content.res.AssetFileDescriptor;
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.media.audiofx.PresetReverb;
+import android.media.audiofx.EnvironmentalReverb;
+import android.media.audiofx.Equalizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+import java.util.UUID;
+
+@TestTargetClass(AudioEffect.class)
+public class AudioEffectTest extends AndroidTestCase {
+
+    private String TAG = "AudioEffectTest";
+    private final static int MIN_NUMBER_EFFECTS = 5;
+    // allow +/- 5% tolerance between set and get delays
+    private final static float DELAY_TOLERANCE = 1.05f;
+    // allow +/- 5% tolerance between set and get ratios
+    private final static float RATIO_TOLERANCE = 1.05f;
+
+    private AudioEffect mEffect = null;
+    private AudioEffect mEffect2 = null;
+    private int mSession = -1;
+    private boolean mHasControl = false;
+    private boolean mIsEnabled = false;
+    private int mChangedParameter = -1;
+    private boolean mInitialized = false;
+    private Looper mLooper = null;
+    private MediaPlayer mMediaPlayer = null;
+    private int mError = 0;
+
+    private final Object mLock = new Object();
+
+
+    //-----------------------------------------------------------------
+    // AUDIOEFFECT TESTS:
+    //----------------------------------
+
+    //-----------------------------------------------------------------
+    // 0 - static methods
+    //----------------------------------
+
+    //Test case 0.0: test queryEffects() and platfrom at least provides Equalizer, Bass Boost,
+    // Virtualizer, Environmental reverb and Preset reverb effects
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "queryEffects",
+            args = {}
+        )
+    })
+    public void test0_0QueryEffects() throws Exception {
+
+        AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+
+        assertTrue("test0_0QueryEffects: number of effects < MIN_NUMBER_EFFECTS: "+desc.length,
+                (desc.length >= MIN_NUMBER_EFFECTS));
+
+        boolean hasEQ = false;
+        boolean hasBassBoost = false;
+        boolean hasVirtualizer = false;
+        boolean hasEnvReverb = false;
+        boolean hasPresetReverb = false;
+
+        for (int i = 0; i < desc.length; i++) {
+            if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
+                hasEQ = true;
+            } else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) {
+                hasBassBoost = true;
+            } else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
+                hasVirtualizer = true;
+            } else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)) {
+                hasEnvReverb = true;
+            } else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_PRESET_REVERB)) {
+                hasPresetReverb = true;
+            }
+        }
+        assertTrue("test0_0QueryEffects: equalizer not found", hasEQ);
+        assertTrue("test0_0QueryEffects: bass boost not found", hasBassBoost);
+        assertTrue("test0_0QueryEffects: virtualizer not found", hasVirtualizer);
+        assertTrue("test0_0QueryEffects: environmental reverb not found", hasEnvReverb);
+        assertTrue("test0_0QueryEffects: preset reverb not found", hasPresetReverb);
+    }
+
+    //-----------------------------------------------------------------
+    // 1 - constructor
+    //----------------------------------
+
+    //Test case 1.0: test constructor from effect type and get effect ID
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "AudioEffect",
+            args = {UUID.class, UUID.class, int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getId",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        )
+    })
+    public void test1_0ConstructorFromType() throws Exception {
+        AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+        assertTrue("no effects found", (desc.length != 0));
+        for (int i = 0; i < desc.length; i++) {
+            if (!desc[i].type.equals(AudioEffect.EFFECT_TYPE_NULL)) {
+                try {
+                    AudioEffect effect = new AudioEffect(desc[i].type,
+                            AudioEffect.EFFECT_TYPE_NULL,
+                            0,
+                            0);
+                    assertNotNull("could not create AudioEffect", effect);
+                    try {
+                        assertTrue("invalid effect ID", (effect.getId() != 0));
+                    } catch (IllegalStateException e) {
+                        fail("AudioEffect not initialized");
+                    } finally {
+                        effect.release();
+                    }
+                } catch (IllegalArgumentException e) {
+                    fail("Effect not found: "+desc[i].name);
+                } catch (UnsupportedOperationException e) {
+                    fail("Effect library not loaded");
+                }
+            }
+        }
+    }
+
+    //Test case 1.1: test constructor from effect uuid
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "AudioEffect",
+            args = {UUID.class, UUID.class, int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getId",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        )
+    })
+    public void test1_1ConstructorFromUuid() throws Exception {
+        AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+        assertTrue("no effects found", (desc.length != 0));
+        for (int i = 0; i < desc.length; i++) {
+            try {
+                AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_NULL,
+                        desc[i].uuid,
+                        0,
+                        0);
+                assertNotNull("could not create AudioEffect", effect);
+                try {
+                    assertTrue("invalid effect ID", (effect.getId() != 0));
+                } catch (IllegalStateException e) {
+                    fail("AudioEffect not initialized");
+                } finally {
+                    effect.release();
+                }
+            } catch (IllegalArgumentException e) {
+                fail("Effect not found: "+desc[i].name);
+            } catch (UnsupportedOperationException e) {
+                fail("Effect library not loaded");
+            }
+        }
+    }
+
+    //Test case 1.2: test constructor failure from unknown type
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "AudioEffect",
+            args = {UUID.class, UUID.class, int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        )
+    })
+    public void test1_2ConstructorUnknownType() throws Exception {
+
+        try {
+            AudioEffect effect = new AudioEffect(UUID.randomUUID(),
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    0,
+                    0);
+            fail("could create random AudioEffect");
+            if (effect != null) {
+                effect.release();
+            }
+        } catch (IllegalArgumentException e) {
+
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        }
+    }
+
+    //Test case 1.3: test getEnabled() failure when called on released effect
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "AudioEffect",
+            args = {UUID.class, UUID.class, int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        )
+    })
+    public void test1_3GetEnabledAfterRelease() throws Exception {
+
+        try {
+            AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    0,
+                    0);
+            assertNotNull("could not create AudioEffect", effect);
+            effect.release();
+            try {
+                effect.getEnabled();
+                fail("getEnabled() processed after release()");
+            } catch (IllegalStateException e) {
+
+            }
+        } catch (IllegalArgumentException e) {
+            fail("AudioEffect not found");
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        }
+    }
+
+    //Test case 1.4: test contructor on mediaPlayer audio session
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "MediaPlayer.getAudioSessionId",
+            args = {}
+        )
+    })
+    public void test1_4InsertOnMediaPlayer() throws Exception {
+        MediaPlayer mp = new MediaPlayer();
+        assertNotNull("could not create mediaplayer", mp);
+        AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(R.raw.testmp3);
+        mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+        afd.close();
+        getEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, mp.getAudioSessionId());
+        try {
+            mEffect.setEnabled(true);
+
+        } catch (IllegalStateException e) {
+            fail("AudioEffect not initialized");
+        } finally {
+            mp.release();
+            releaseEffect();
+        }
+    }
+
+    //Test case 1.5: test auxiliary effect attachement on MediaPlayer
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getId",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "MediaPlayer.attachAuxEffect",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "MediaPlayer.setAuxEffectSendLevel",
+            args = {}
+        )
+    })
+    public void test1_5AuxiliaryOnMediaPlayer() throws Exception {
+        createMediaPlayerLooper();
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                fail("Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);  // mMediaPlayer has been initialized?
+        mError = 0;
+
+        AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(R.raw.testmp3);
+        mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+        afd.close();
+        getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
+        try {
+            synchronized(mLock) {
+                try {
+                    mMediaPlayer.attachAuxEffect(mEffect.getId());
+                    mMediaPlayer.setAuxEffectSendLevel(1.0f);
+                    mLock.wait(200);
+                } catch(Exception e) {
+                    fail("Attach effect: wait was interrupted.");
+                }
+            }
+            assertTrue("error on attachAuxEffect", mError == 0);
+
+        } catch (IllegalStateException e) {
+            fail("attach aux effect failed");
+        } finally {
+            terminateMediaPlayerLooper();
+            releaseEffect();
+        }
+    }
+
+    //Test case 1.6: test auxiliary effect attachement failure before setDatasource
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getId",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "MediaPlayer.attachAuxEffect",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "MediaPlayer.setAuxEffectSendLevel",
+            args = {}
+        )
+    })
+    public void test1_6AuxiliaryOnMediaPlayerFailure() throws Exception {
+        createMediaPlayerLooper();
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                fail("Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);  // mMediaPlayer has been initialized?
+        mError = 0;
+
+        getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
+        try {
+            synchronized(mLock) {
+                try {
+                    mMediaPlayer.attachAuxEffect(mEffect.getId());
+                    mLock.wait(1000);
+                } catch(Exception e) {
+                    fail("Attach effect: wait was interrupted.");
+                }
+            }
+            assertTrue("no error on attachAuxEffect", mError != 0);
+
+        } catch (IllegalStateException e) {
+            fail("attach aux effect failed");
+        } finally {
+            terminateMediaPlayerLooper();
+            releaseEffect();
+        }
+    }
+
+
+    //Test case 1.7: test auxiliary effect attachement on AudioTrack
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getId",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "AudioTrack.attachAuxEffect",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "AudioTrack.setAuxEffectSendLevel",
+            args = {}
+        )
+    })
+    public void test1_7AuxiliaryOnAudioTrack() throws Exception {
+        AudioTrack track = null;
+        getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
+        try {
+            track = new AudioTrack(
+                                AudioManager.STREAM_MUSIC,
+                                44100,
+                                AudioFormat.CHANNEL_OUT_MONO,
+                                AudioFormat.ENCODING_PCM_16BIT,
+                                AudioTrack.getMinBufferSize(44100,
+                                                            AudioFormat.CHANNEL_OUT_MONO,
+                                                            AudioFormat.ENCODING_PCM_16BIT),
+                                                            AudioTrack.MODE_STREAM);
+            assertNotNull("could not create AudioTrack", track);
+
+            int status = track.attachAuxEffect(mEffect.getId());
+            if (status != AudioTrack.SUCCESS) {
+                fail("could not attach aux effect");
+            }
+            status = track.setAuxEffectSendLevel(1.0f);
+            if (status != AudioTrack.SUCCESS) {
+                fail("could not set send level");
+            }
+        } catch (IllegalStateException e) {
+            fail("could not attach aux effect");
+        } catch (IllegalArgumentException e) {
+            fail("could not create AudioTrack");
+        } finally {
+            if (track != null) {
+                track.release();
+            }
+            releaseEffect();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - enable/ disable
+    //----------------------------------
+
+
+    //Test case 2.0: test setEnabled() and getEnabled() in valid state
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        )
+    })
+    public void test2_0SetEnabledGetEnabled() throws Exception {
+
+        try {
+            AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    0,
+                    0);
+            assertNotNull("could not create AudioEffect", effect);
+            try {
+                effect.setEnabled(true);
+                assertTrue("invalid state from getEnabled", effect.getEnabled());
+                effect.setEnabled(false);
+                assertFalse("invalid state to getEnabled", effect.getEnabled());
+
+            } catch (IllegalStateException e) {
+                fail("setEnabled() in wrong state");
+            } finally {
+                effect.release();
+            }
+        } catch (IllegalArgumentException e) {
+            fail("AudioEffect not found");
+
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        }
+    }
+
+    //Test case 2.1: test setEnabled() throws exception after release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        )
+    })
+    public void test2_1SetEnabledAfterRelease() throws Exception {
+
+        try {
+            AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    0,
+                    0);
+            assertNotNull("could not create AudioEffect", effect);
+            effect.release();
+            try {
+                effect.setEnabled(true);
+                fail("setEnabled() processed after release");
+            } catch (IllegalStateException e) {
+                // test passed
+            }
+        } catch (IllegalArgumentException e) {
+            fail("AudioEffect not found");
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 3 - set/get parameters
+    //----------------------------------
+
+    //Test case 3.0: test setParameter(byte[], byte[]) / getParameter(byte[], byte[])
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {byte[].class, byte[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getParameter",
+            args = {byte[].class, byte[].class}
+        )
+    })
+    public void test3_0SetParameterByteArrayByteArray() throws Exception {
+        getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
+        try {
+            byte[] param = mEffect.intToByteArray(PresetReverb.PARAM_PRESET);
+            byte[] value = new byte[2];
+            int status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 1 failed", AudioEffect.SUCCESS, status);
+            short preset = PresetReverb.PRESET_SMALLROOM;
+            if (mEffect.byteArrayToShort(value) == preset) {
+                preset = PresetReverb.PRESET_MEDIUMROOM;
+            }
+            value = mEffect.shortToByteArray(preset);
+            status = mEffect.setParameter(param, value);
+            assertEquals("setParameter failed", AudioEffect.SUCCESS, status);
+            status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 2 failed", AudioEffect.SUCCESS, status);
+            assertEquals("get/set Parameter failed", preset,
+                    mEffect.byteArrayToShort(value));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("setParameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("setParameter() called in wrong state");
+        } finally {
+            releaseEffect();
+        }
+    }
+
+    //Test case 3.1: test setParameter(int, int) / getParameter(int, int[])
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getParameter",
+            args = {int.class, int[].class}
+        )
+    })
+    public void test3_1SetParameterIntInt() throws Exception {
+        getEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, 0);
+        try {
+            int param = EnvironmentalReverb.PARAM_DECAY_TIME;
+            int[] value = new int[1];
+            int status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 1 failed", AudioEffect.SUCCESS, status);
+            int time = 500;
+            if (value[0] == time) {
+                time = 1000;
+            }
+            status = mEffect.setParameter(param, time);
+            assertEquals("setParameter failed", AudioEffect.SUCCESS, status);
+            status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 2 failed", AudioEffect.SUCCESS, status);
+            assertTrue("got incorrect decay time",
+                    ((float)value[0] > (float)(time / DELAY_TOLERANCE)) &&
+                    ((float)value[0] < (float)(time * DELAY_TOLERANCE)));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("setParameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("setParameter() called in wrong state");
+        } finally {
+            releaseEffect();
+        }
+    }
+
+    //Test case 3.2: test setParameter(int, short) / getParameter(int, short[])
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {int.class, short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getParameter",
+            args = {int.class, short[].class}
+        )
+    })
+    public void test3_2SetParameterIntShort() throws Exception {
+        getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
+        try {
+            int param = PresetReverb.PARAM_PRESET;
+            short[] value = new short[1];
+            int status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 1 failed", AudioEffect.SUCCESS, status);
+            short preset = PresetReverb.PRESET_SMALLROOM;
+            if (value[0] == preset) {
+                preset = PresetReverb.PRESET_MEDIUMROOM;
+            }
+            status = mEffect.setParameter(param, preset);
+            assertEquals("setParameter failed", AudioEffect.SUCCESS, status);
+            status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 2 failed", AudioEffect.SUCCESS, status);
+            assertEquals("get/set Parameter failed", preset, value[0]);
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("setParameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("setParameter() called in wrong state");
+        } finally {
+            releaseEffect();
+        }
+    }
+
+    //Test case 3.3: test setParameter(int, byte[]) / getParameter(int, byte[])
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {int.class, byte[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getParameter",
+            args = {int.class, byte[].class}
+        )
+    })
+    public void test3_3SetParameterIntByteArray() throws Exception {
+        getEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, 0);
+        try {
+            int param = EnvironmentalReverb.PARAM_DECAY_TIME;
+            byte[] value = new byte[4];
+            int status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 1 failed", AudioEffect.SUCCESS, status);
+            int time = 500;
+            if (mEffect.byteArrayToInt(value) == time) {
+                time = 1000;
+            }
+            value = mEffect.intToByteArray(time);
+            status = mEffect.setParameter(param, value);
+            assertEquals("setParameter failed", AudioEffect.SUCCESS, status);
+            status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 2 failed", AudioEffect.SUCCESS, status);
+            int time2 = mEffect.byteArrayToInt(value);
+            assertTrue("got incorrect decay time",
+                    ((float)time2 > (float)(time / DELAY_TOLERANCE)) &&
+                    ((float)time2 < (float)(time * DELAY_TOLERANCE)));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("setParameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("setParameter() called in wrong state");
+        } finally {
+            releaseEffect();
+        }
+    }
+
+    //Test case 3.4: test setParameter(int[], int[]) / getParameter(int[], int[])
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {int[].class, int[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getParameter",
+            args = {int[].class, int[].class}
+        )
+    })
+    public void test3_4SetParameterIntArrayIntArray() throws Exception {
+        getEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, 0);
+        try {
+            int[] param = new int[1];
+            int[] value = new int[1];
+            param[0] = EnvironmentalReverb.PARAM_DECAY_TIME;
+            int status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 1 failed", AudioEffect.SUCCESS, status);
+            int[] time = new int[1];
+            time[0] = 500;
+            if (value[0] == time[0]) {
+                time[0] = 1000;
+            }
+            status = mEffect.setParameter(param, time);
+            assertEquals("setParameter failed", AudioEffect.SUCCESS, status);
+            status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 2 failed", AudioEffect.SUCCESS, status);
+            assertTrue("got incorrect decay time",
+                    ((float)value[0] > (float)(time[0] / DELAY_TOLERANCE)) &&
+                    ((float)value[0] < (float)(time[0] * DELAY_TOLERANCE)));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("setParameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("setParameter() called in wrong state");
+        } finally {
+            releaseEffect();
+        }
+    }
+
+    //Test case 3.5: test setParameter(int[], short[]) / getParameter(int[], short[])
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {int[].class, short[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getParameter",
+            args = {int[].class, short[].class}
+        )
+    })
+
+    public void test3_5SetParameterIntArrayShortArray() throws Exception {
+        getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
+        try {
+            int[] param = new int[1];
+            param[0] = PresetReverb.PARAM_PRESET;
+            short[] value = new short[1];
+            int status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 1 failed", AudioEffect.SUCCESS, status);
+            short[] preset = new short[1];
+            preset[0] = PresetReverb.PRESET_SMALLROOM;
+            if (value[0] == preset[0]) {
+                preset[0] = PresetReverb.PRESET_MEDIUMROOM;
+            }
+            status = mEffect.setParameter(param, preset);
+            assertEquals("setParameter failed", AudioEffect.SUCCESS, status);
+            status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 2 failed", AudioEffect.SUCCESS, status);
+            assertEquals("get/set Parameter failed", preset[0], value[0]);
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("setParameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("setParameter() called in wrong state");
+        } finally {
+            releaseEffect();
+        }
+    }
+
+    //Test case 3.6: test setParameter(int[], byte[]) / getParameter(int[], byte[])
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {int[].class, byte[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getParameter",
+            args = {int[].class, byte[].class}
+        )
+    })
+    public void test3_6SetParameterIntArrayByteArray() throws Exception {
+        getEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, 0);
+        try {
+            int[] param = new int[1];
+            param[0] = EnvironmentalReverb.PARAM_DECAY_TIME;
+            byte[] value = new byte[4];
+            int status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 1 failed", AudioEffect.SUCCESS, status);
+            int time = 500;
+            if (mEffect.byteArrayToInt(value) == time) {
+                time = 1000;
+            }
+
+            status = mEffect.setParameter(param, mEffect.intToByteArray(time));
+            assertEquals("setParameter failed", AudioEffect.SUCCESS, status);
+            status = mEffect.getParameter(param, value);
+            assertEquals("getParameter 2 failed", AudioEffect.SUCCESS, status);
+            int time2 = mEffect.byteArrayToInt(value);
+            assertTrue("got incorrect decay time",
+                    ((float)time2 > (float)(time / DELAY_TOLERANCE)) &&
+                    ((float)time2 < (float)(time * DELAY_TOLERANCE)));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("setParameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("setParameter() called in wrong state");
+        } finally {
+            releaseEffect();
+        }
+    }
+
+    //Test case 3.7: test setParameter() throws exception after release()
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {int.class, short.class}
+        )
+    })
+    public void test3_7SetParameterAfterRelease() throws Exception {
+        AudioEffect effect = null;
+        try {
+            effect = new AudioEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB,
+                                    AudioEffect.EFFECT_TYPE_NULL,
+                                    0,
+                                    0);
+            assertNotNull("could not create AudioEffect", effect);
+            effect.release();
+            effect.setParameter(PresetReverb.PARAM_PRESET, PresetReverb.PRESET_SMALLROOM);
+            fail("setParameter() processed after release");
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("setParameter() rejected");
+        } catch (IllegalStateException e) {
+            // test passed
+        } finally {
+            if (effect != null) {
+                effect.release();
+            }
+        }
+    }
+
+    //Test case 3.8: test getParameter() throws exception after release()
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {int.class, short[].class}
+        )
+    })
+    public void test3_8GetParameterAfterRelease() throws Exception {
+        AudioEffect effect = null;
+        try {
+            effect = new AudioEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB,
+                                    AudioEffect.EFFECT_TYPE_NULL,
+                                    0,
+                                    0);
+            assertNotNull("could not create AudioEffect", effect);
+            effect.release();
+            short[] value = new short[1];
+            effect.getParameter(PresetReverb.PARAM_PRESET, value);
+            fail("getParameter() processed after release");
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("getParameter() rejected");
+        } catch (IllegalStateException e) {
+            // test passed
+        } finally {
+            if (effect != null) {
+                effect.release();
+            }
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 4 priority and listeners
+    //----------------------------------
+
+    //Test case 4.0: test control passed to higher priority client
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "hasControl",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        )
+    })
+    public void test4_0setEnabledLowerPriority() throws Exception {
+        AudioEffect effect1 = null;
+        AudioEffect effect2 = null;
+        try {
+            effect1 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+                                    AudioEffect.EFFECT_TYPE_NULL,
+                                    0,
+                                    0);
+            effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    1,
+                    0);
+
+            assertNotNull("could not create AudioEffect", effect1);
+            assertNotNull("could not create AudioEffect", effect2);
+
+            assertTrue("Effect2 does not have control", effect2.hasControl());
+            assertFalse("Effect1 has control", effect1.hasControl());
+            assertTrue("Effect1 can enable",
+                    effect1.setEnabled(true) == AudioEffect.ERROR_INVALID_OPERATION);
+            assertFalse("Effect1 has enabled", effect2.getEnabled());
+
+        } catch (IllegalArgumentException e) {
+            fail("Effect not found");
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        } finally {
+            if (effect1 != null) {
+                effect1.release();
+            }
+            if (effect2 != null) {
+                effect2.release();
+            }
+        }
+    }
+
+    //Test case 4.1: test control passed to higher priority client
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {int.class, short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getParameter",
+            args = {int.class, short[].class}
+        )
+    })
+    public void test4_1setParameterLowerPriority() throws Exception {
+        AudioEffect effect1 = null;
+        AudioEffect effect2 = null;
+        try {
+            effect1 = new AudioEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB,
+                                    AudioEffect.EFFECT_TYPE_NULL,
+                                    0,
+                                    0);
+            effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB,
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    1,
+                    0);
+
+            assertNotNull("could not create AudioEffect", effect1);
+            assertNotNull("could not create AudioEffect", effect2);
+
+            int status = effect2.setParameter(PresetReverb.PARAM_PRESET,
+                    PresetReverb.PRESET_SMALLROOM);
+            assertEquals("Effect2 setParameter failed",
+                    AudioEffect.SUCCESS, status);
+
+            status = effect1.setParameter(PresetReverb.PARAM_PRESET,
+                    PresetReverb.PRESET_MEDIUMROOM);
+            assertEquals("Effect1 setParameter did not fail",
+                    AudioEffect.ERROR_INVALID_OPERATION, status);
+
+            short[] value = new short[1];
+            status = effect2.getParameter(PresetReverb.PARAM_PRESET, value);
+            assertEquals("Effect2 getParameter failed",
+                    AudioEffect.SUCCESS, status);
+            assertEquals("Effect1 changed parameter", PresetReverb.PRESET_SMALLROOM
+                    , value[0]);
+
+
+        } catch (IllegalArgumentException e) {
+            fail("Effect not found");
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        } finally {
+            if (effect1 != null) {
+                effect1.release();
+            }
+            if (effect2 != null) {
+                effect2.release();
+            }
+        }
+    }
+
+    //Test case 4.2: test control status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setControlStatusListener",
+            args = {AudioEffect.OnControlStatusChangeListener.class}
+        )
+    })
+    public void test4_2ControlStatusListener() throws Exception {
+
+        mHasControl = true;
+        createListenerLooper(true, false, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                fail("Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        synchronized(mLock) {
+            try {
+                getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                fail("Create second effect: wait was interrupted.");
+            } finally {
+                releaseEffect();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("effect control not lost by effect1", mHasControl);
+    }
+
+    //Test case 4.3: test enable status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnableStatusListener",
+            args = {AudioEffect.OnEnableStatusChangeListener.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        )
+    })
+    public void test4_3EnableStatusListener() throws Exception {
+
+        createListenerLooper(false, true, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                fail("Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        mEffect2.setEnabled(true);
+        mIsEnabled = true;
+
+        getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
+        assertTrue("effect not enabled", mEffect.getEnabled());
+        synchronized(mLock) {
+            try {
+                mEffect.setEnabled(false);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                fail("Second effect setEnabled: wait was interrupted.");
+            } finally {
+                releaseEffect();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("enable status not updated", mIsEnabled);
+    }
+
+    //Test case 4.4: test parameter changed listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameterListener",
+            args = {AudioEffect.OnParameterChangeListener.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameter",
+            args = {int.class, short.class}
+        )
+    })
+    public void test4_4ParameterChangedListener() throws Exception {
+
+        createListenerLooper(false, false, true);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                fail("Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        int status = mEffect2.setParameter(PresetReverb.PARAM_PRESET,
+                PresetReverb.PRESET_SMALLROOM);
+        assertEquals("mEffect2 setParameter failed",
+                AudioEffect.SUCCESS, status);
+        getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
+        synchronized(mLock) {
+            try {
+                mChangedParameter = -1;
+                mEffect.setParameter(PresetReverb.PARAM_PRESET,
+                        PresetReverb.PRESET_MEDIUMROOM);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                fail("set Parameter: wait was interrupted.");
+            } finally {
+                releaseEffect();
+                terminateListenerLooper();
+            }
+        }
+        assertEquals("parameter change not received",
+                PresetReverb.PARAM_PRESET, mChangedParameter);
+    }
+
+    //-----------------------------------------------------------------
+    // 5 command method
+    //----------------------------------
+
+
+    //Test case 5.0: test command method
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "command",
+            args = {int.class, byte[].class, byte[].class}
+        )
+    })
+    public void test5_0Command() throws Exception {
+        getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
+        try {
+            byte[] cmd = new byte[0];
+            byte[] reply = new byte[4];
+            // command 3 is ENABLE
+            int status = mEffect.command(3, cmd, reply);
+            assertEquals("command failed", AudioEffect.SUCCESS, status);
+            assertTrue("effect not enabled", mEffect.getEnabled());
+
+        } catch (IllegalStateException e) {
+            fail("command in illegal state");
+        } finally {
+            releaseEffect();
+        }
+    }
+
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private void getEffect(UUID type, int session) {
+         if (mEffect == null || session != mSession) {
+             if (session != mSession && mEffect != null) {
+                 mEffect.release();
+                 mEffect = null;
+             }
+             try {
+                 mEffect = new AudioEffect(type,
+                                             AudioEffect.EFFECT_TYPE_NULL,
+                                             0,
+                                             session);
+                 mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getEffect() AudioEffect not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getEffect() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mEffect", mEffect);
+    }
+
+    private void releaseEffect() {
+        if (mEffect != null) {
+            mEffect.release();
+            mEffect = null;
+        }
+    }
+
+    // Initializes the equalizer listener looper
+    class ListenerThread extends Thread {
+        boolean mControl;
+        boolean mEnable;
+        boolean mParameter;
+
+        public ListenerThread(boolean control, boolean enable, boolean parameter) {
+            super();
+            mControl = control;
+            mEnable = enable;
+            mParameter = parameter;
+        }
+    }
+
+    private void createListenerLooper(boolean control, boolean enable, boolean parameter) {
+        mInitialized = false;
+        new ListenerThread(control, enable, parameter) {
+            @Override
+            public void run() {
+                // Set up a looper
+                Looper.prepare();
+
+                // Save the looper so that we can terminate this thread
+                // after we are done with it.
+                mLooper = Looper.myLooper();
+
+                mEffect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB,
+                        AudioEffect.EFFECT_TYPE_NULL,
+                        0,
+                        0);
+                assertNotNull("could not create Equalizer2", mEffect2);
+
+                if (mControl) {
+                    mEffect2.setControlStatusListener(
+                            new AudioEffect.OnControlStatusChangeListener() {
+                        public void onControlStatusChange(
+                                AudioEffect effect, boolean controlGranted) {
+                            synchronized(mLock) {
+                                if (effect == mEffect2) {
+                                    mHasControl = controlGranted;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mEnable) {
+                    mEffect2.setEnableStatusListener(
+                            new AudioEffect.OnEnableStatusChangeListener() {
+                        public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+                            synchronized(mLock) {
+                                if (effect == mEffect2) {
+                                    mIsEnabled = enabled;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mParameter) {
+                    mEffect2.setParameterListener(new AudioEffect.OnParameterChangeListener() {
+                        public void onParameterChange(AudioEffect effect, int status, byte[] param,
+                                byte[] value)
+                        {
+                            synchronized(mLock) {
+                                if (effect == mEffect2) {
+                                    mChangedParameter = mEffect2.byteArrayToInt(param);
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+
+                synchronized(mLock) {
+                    mInitialized = true;
+                    mLock.notify();
+                }
+                Looper.loop();  // Blocks forever until Looper.quit() is called.
+            }
+        }.start();
+    }
+
+    // Terminates the listener looper thread.
+    private void terminateListenerLooper() {
+        if (mEffect2 != null) {
+            mEffect2.release();
+            mEffect2 = null;
+        }
+        if (mLooper != null) {
+            mLooper.quit();
+            mLooper = null;
+        }
+    }
+
+    /*
+     * Initializes the message looper so that the MediaPlayer object can
+     * receive the callback messages.
+     */
+    private void createMediaPlayerLooper() {
+        mInitialized = false;
+        new Thread() {
+            @Override
+            public void run() {
+                // Set up a looper to be used by mMediaPlayer.
+                Looper.prepare();
+
+                // Save the looper so that we can terminate this thread
+                // after we are done with it.
+                mLooper = Looper.myLooper();
+
+                mMediaPlayer = new MediaPlayer();
+                mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+                    public boolean onError(MediaPlayer player, int what, int extra) {
+                        synchronized(mLock) {
+                            mError = what;
+                            mLock.notify();
+                        }
+                        return true;
+                    }
+                });
+                mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+                    public void onCompletion(MediaPlayer player) {
+                        synchronized(mLock) {
+                            mLock.notify();
+                        }
+                    }
+                });
+                synchronized(mLock) {
+                    mInitialized = true;
+                    mLock.notify();
+                }
+                Looper.loop();  // Blocks forever until Looper.quit() is called.
+            }
+        }.start();
+    }
+    /*
+     * Terminates the message looper thread.
+     */
+    private void terminateMediaPlayerLooper() {
+        if (mLooper != null) {
+            mLooper.quit();
+            mLooper = null;
+        }
+        if (mMediaPlayer != null) {
+            mMediaPlayer.release();
+        }
+    }
+
+}
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index cfa9293..b4053cf 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -38,7 +38,6 @@
 
 import com.android.cts.stub.R;
 
-import dalvik.annotation.BrokenTest;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
@@ -47,7 +46,6 @@
 
 import android.app.cts.CTSResult;
 import android.content.Context;
-import android.content.Intent;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
 import android.provider.Settings;
@@ -87,32 +85,6 @@
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
-            method = "setStreamMute",
-            args = {int.class, boolean.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "setStreamSolo",
-            args = {int.class, boolean.class}
-        )
-    })
-    @BrokenTest("flaky")
-    public void testMuteSolo() throws Exception {
-        /**
-         * this test must be run on screen unlocked model
-         */
-        AudioManagerStub.setCTSResult(this);
-        Intent intent = new Intent();
-        intent.setClass(mContext, AudioManagerStub.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(intent);
-        mSync.waitForResult();
-        assertEquals(CTSResult.RESULT_OK, mResultCode);
-    }
-
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
             method = "setMicrophoneMute",
             args = {boolean.class}
         ),
diff --git a/tests/tests/media/src/android/media/cts/BassBoostTest.java b/tests/tests/media/src/android/media/cts/BassBoostTest.java
new file mode 100644
index 0000000..9cea0b0
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/BassBoostTest.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2010 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.media.cts;
+
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.audiofx.BassBoost;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+@TestTargetClass(BassBoost.class)
+public class BassBoostTest extends AndroidTestCase {
+
+    private String TAG = "BassBoostTest";
+    private final static short TEST_STRENGTH = 500;
+    private final static short TEST_STRENGTH2 = 1000;
+    private final static float STRENGTH_TOLERANCE = 1.1f;  // 10%
+
+    private BassBoost mBassBoost = null;
+    private BassBoost mBassBoost2 = null;
+    private int mSession = -1;
+    private boolean mHasControl = false;
+    private boolean mIsEnabled = false;
+    private int mChangedParameter = -1;
+    private boolean mInitialized = false;
+    private Looper mLooper = null;
+    private final Object mLock = new Object();
+
+    //-----------------------------------------------------------------
+    // BASS BOOST TESTS:
+    //----------------------------------
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "BassBoost",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getId",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        )
+    })
+    public void test0_0ConstructorAndRelease() throws Exception {
+        BassBoost eq = null;
+        try {
+            eq = new BassBoost(0, 0);
+            assertNotNull("could not create BassBoost", eq);
+            try {
+                assertTrue("invalid effect ID", (eq.getId() != 0));
+            } catch (IllegalStateException e) {
+                fail("BassBoost not initialized");
+            }
+            // test passed
+        } catch (IllegalArgumentException e) {
+            fail("BassBoost not found");
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        } finally {
+            if (eq != null) {
+                eq.release();
+            }
+        }
+    }
+
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test strength
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getStrengthSupported",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setStrength",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getRoundedStrength",
+            args = {}
+        )
+    })
+    public void test1_0Strength() throws Exception {
+        getBassBoost(0);
+        try {
+            if (mBassBoost.getStrengthSupported()) {
+                short strength = mBassBoost.getRoundedStrength();
+                strength = (strength == TEST_STRENGTH) ? TEST_STRENGTH2 : TEST_STRENGTH;
+                mBassBoost.setStrength((short)strength);
+                short strength2 = mBassBoost.getRoundedStrength();
+                // allow STRENGTH_TOLERANCE difference between set strength and rounded strength
+                assertTrue("got incorrect strength",
+                        ((float)strength2 > (float)strength / STRENGTH_TOLERANCE) &&
+                        ((float)strength2 < (float)strength * STRENGTH_TOLERANCE));
+            } else {
+                short strength = mBassBoost.getRoundedStrength();
+                assertTrue("got incorrect strength", strength >= 0 && strength <= 1000);
+            }
+            // test passed
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseBassBoost();
+        }
+    }
+
+    //Test case 1.1: test properties
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getProperties",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setProperties",
+            args = {BassBoost.Settings.class}
+        )
+    })
+    public void test1_1Properties() throws Exception {
+        getBassBoost(0);
+        try {
+            BassBoost.Settings settings = mBassBoost.getProperties();
+            String str = settings.toString();
+            settings = new BassBoost.Settings(str);
+
+            short strength = settings.strength;
+            if (mBassBoost.getStrengthSupported()) {
+                strength = (strength == TEST_STRENGTH) ? TEST_STRENGTH2 : TEST_STRENGTH;
+            }
+            settings.strength = strength;
+            mBassBoost.setProperties(settings);
+            settings = mBassBoost.getProperties();
+
+            if (mBassBoost.getStrengthSupported()) {
+                // allow STRENGTH_TOLERANCE difference between set strength and rounded strength
+                assertTrue("got incorrect strength",
+                        ((float)settings.strength > (float)strength / STRENGTH_TOLERANCE) &&
+                        ((float)settings.strength < (float)strength * STRENGTH_TOLERANCE));
+            }
+            // test passed
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseBassBoost();
+        }
+    }
+
+    //Test case 1.2: test setStrength() throws exception after release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setStrength",
+            args = {short.class}
+        )
+    })
+    public void test1_2SetStrengthAfterRelease() throws Exception {
+        getBassBoost(0);
+        mBassBoost.release();
+        try {
+            mBassBoost.setStrength(TEST_STRENGTH);
+            fail("setStrength() processed after release()");
+        } catch (IllegalStateException e) {
+            // test passed
+        } finally {
+            releaseBassBoost();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - Effect enable/disable
+    //----------------------------------
+
+    //Test case 2.0: test setEnabled() and getEnabled() in valid state
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        )
+    })
+    public void test2_0SetEnabledGetEnabled() throws Exception {
+        getBassBoost(0);
+        try {
+            mBassBoost.setEnabled(true);
+            assertTrue("invalid state from getEnabled", mBassBoost.getEnabled());
+            mBassBoost.setEnabled(false);
+            assertFalse("invalid state to getEnabled", mBassBoost.getEnabled());
+            // test passed
+        } catch (IllegalStateException e) {
+            fail("setEnabled() in wrong state");
+        } finally {
+            releaseBassBoost();
+        }
+    }
+
+    //Test case 2.1: test setEnabled() throws exception after release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        )
+    })
+    public void test2_1SetEnabledAfterRelease() throws Exception {
+        getBassBoost(0);
+        mBassBoost.release();
+        try {
+            mBassBoost.setEnabled(true);
+            fail("setEnabled() processed after release()");
+        } catch (IllegalStateException e) {
+            // test passed
+        } finally {
+            releaseBassBoost();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 3 priority and listeners
+    //----------------------------------
+
+    //Test case 3.0: test control status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setControlStatusListener",
+            args = {AudioEffect.OnControlStatusChangeListener.class}
+        )
+    })
+    public void test3_0ControlStatusListener() throws Exception {
+        mHasControl = true;
+        createListenerLooper(true, false, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        synchronized(mLock) {
+            try {
+                getBassBoost(0);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseBassBoost();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("effect control not lost by effect1", mHasControl);
+    }
+
+    //Test case 3.1: test enable status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnableStatusListener",
+            args = {AudioEffect.OnEnableStatusChangeListener.class}
+        )
+    })
+    public void test3_1EnableStatusListener() throws Exception {
+        createListenerLooper(false, true, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        mBassBoost2.setEnabled(true);
+        mIsEnabled = true;
+        getBassBoost(0);
+        synchronized(mLock) {
+            try {
+                mBassBoost.setEnabled(false);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseBassBoost();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("enable status not updated", mIsEnabled);
+    }
+
+    //Test case 3.2: test parameter changed listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameterListener",
+            args = {BassBoost.OnParameterChangeListener.class}
+        )
+    })
+    public void test3_2ParameterChangedListener() throws Exception {
+        createListenerLooper(false, false, true);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        getBassBoost(0);
+        synchronized(mLock) {
+            try {
+                mChangedParameter = -1;
+                mBassBoost.setStrength(TEST_STRENGTH);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseBassBoost();
+                terminateListenerLooper();
+            }
+        }
+        assertEquals("parameter change not received",
+                BassBoost.PARAM_STRENGTH, mChangedParameter);
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private void getBassBoost(int session) {
+         if (mBassBoost == null || session != mSession) {
+             if (session != mSession && mBassBoost != null) {
+                 mBassBoost.release();
+                 mBassBoost = null;
+             }
+             try {
+                mBassBoost = new BassBoost(0, session);
+                mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getBassBoost() BassBoost not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getBassBoost() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mBassBoost", mBassBoost);
+    }
+
+    private void releaseBassBoost() {
+        if (mBassBoost != null) {
+            mBassBoost.release();
+            mBassBoost = null;
+        }
+    }
+
+    // Initializes the bassboot listener looper
+    class ListenerThread extends Thread {
+        boolean mControl;
+        boolean mEnable;
+        boolean mParameter;
+
+        public ListenerThread(boolean control, boolean enable, boolean parameter) {
+            super();
+            mControl = control;
+            mEnable = enable;
+            mParameter = parameter;
+        }
+    }
+
+    private void createListenerLooper(boolean control, boolean enable, boolean parameter) {
+        mInitialized = false;
+        new ListenerThread(control, enable, parameter) {
+            @Override
+            public void run() {
+                // Set up a looper
+                Looper.prepare();
+
+                // Save the looper so that we can terminate this thread
+                // after we are done with it.
+                mLooper = Looper.myLooper();
+
+                mBassBoost2 = new BassBoost(0, 0);
+                assertNotNull("could not create bassboot2", mBassBoost2);
+
+                if (mControl) {
+                    mBassBoost2.setControlStatusListener(
+                            new AudioEffect.OnControlStatusChangeListener() {
+                        public void onControlStatusChange(
+                                AudioEffect effect, boolean controlGranted) {
+                            synchronized(mLock) {
+                                if (effect == mBassBoost2) {
+                                    mHasControl = controlGranted;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mEnable) {
+                    mBassBoost2.setEnableStatusListener(
+                            new AudioEffect.OnEnableStatusChangeListener() {
+                        public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+                            synchronized(mLock) {
+                                if (effect == mBassBoost2) {
+                                    mIsEnabled = enabled;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mParameter) {
+                    mBassBoost2.setParameterListener(new BassBoost.OnParameterChangeListener() {
+                        public void onParameterChange(BassBoost effect, int status,
+                                int param, short value)
+                        {
+                            synchronized(mLock) {
+                                if (effect == mBassBoost2) {
+                                    mChangedParameter = param;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+
+                synchronized(mLock) {
+                    mInitialized = true;
+                    mLock.notify();
+                }
+                Looper.loop();  // Blocks forever until Looper.quit() is called.
+            }
+        }.start();
+    }
+
+    // Terminates the listener looper thread.
+    private void terminateListenerLooper() {
+        if (mBassBoost2 != null) {
+            mBassBoost2.release();
+            mBassBoost2 = null;
+        }
+        if (mLooper != null) {
+            mLooper.quit();
+            mLooper = null;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java b/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
new file mode 100644
index 0000000..b94da11
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 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.media.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+import android.hardware.Camera;
+import android.media.CamcorderProfile;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.List;
+
+@TestTargetClass(CamcorderProfile.class)
+public class CamcorderProfileTest extends AndroidTestCase {
+
+    private static final String TAG = "CamcorderProfileTest";
+
+    private void checkProfile(CamcorderProfile profile) {
+        Log.v(TAG, String.format("profile: duration=%d, quality=%d, " +
+            "fileFormat=%d, videoCodec=%d, videoBitRate=%d, videoFrameRate=%d, " +
+            "videoFrameWidth=%d, videoFrameHeight=%d, audioCodec=%d, " +
+            "audioBitRate=%d, audioSampleRate=%d, audioChannels=%d",
+            profile.duration,
+            profile.quality,
+            profile.fileFormat,
+            profile.videoCodec,
+            profile.videoBitRate,
+            profile.videoFrameRate,
+            profile.videoFrameWidth,
+            profile.videoFrameHeight,
+            profile.audioCodec,
+            profile.audioBitRate,
+            profile.audioSampleRate,
+            profile.audioChannels));
+        assertTrue(profile.duration > 0);
+        assertTrue(profile.quality == CamcorderProfile.QUALITY_LOW ||
+                   profile.quality == CamcorderProfile.QUALITY_HIGH);
+        assertTrue(profile.videoBitRate > 0);
+        assertTrue(profile.videoFrameRate > 0);
+        assertTrue(profile.videoFrameWidth > 0);
+        assertTrue(profile.videoFrameHeight > 0);
+        assertTrue(profile.audioBitRate > 0);
+        assertTrue(profile.audioSampleRate > 0);
+        assertTrue(profile.audioChannels > 0);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "get",
+            args = {int.class}
+        )
+    })
+    public void testGet() {
+        CamcorderProfile lowProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
+        CamcorderProfile highProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
+        checkProfile(lowProfile);
+        checkProfile(highProfile);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "get",
+            args = {int.class, int.class}
+        )
+    })
+    public void testGetWithId() {
+        int nCamera = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCamera; id++) {
+            CamcorderProfile lowProfile = CamcorderProfile.get(id,
+                    CamcorderProfile.QUALITY_LOW);
+            CamcorderProfile highProfile = CamcorderProfile.get(id,
+                    CamcorderProfile.QUALITY_HIGH);
+            checkProfile(lowProfile);
+            checkProfile(highProfile);
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/CameraProfileTest.java b/tests/tests/media/src/android/media/cts/CameraProfileTest.java
new file mode 100644
index 0000000..72cd296
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/CameraProfileTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 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.media.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+import android.hardware.Camera;
+import android.media.CameraProfile;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.List;
+
+@TestTargetClass(CameraProfile.class)
+public class CameraProfileTest extends AndroidTestCase {
+
+    private static final String TAG = "CameraProfileTest";
+
+    private void checkQuality(int low, int mid, int high) {
+        Log.v(TAG, "low = " + low + ", mid = " + mid + ", high = " + high);
+        assertTrue(low >= 0 && low <= 100);
+        assertTrue(mid >= 0 && mid <= 100);
+        assertTrue(high >= 0 && high <= 100);
+        assertTrue(low <= mid && mid <= high);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getJpegEncodingQualityParameter",
+            args = {int.class}
+        )
+    })
+    public void testGetImageEncodingQualityParameter() {
+        int low = CameraProfile.getJpegEncodingQualityParameter(CameraProfile.QUALITY_LOW);
+        int mid = CameraProfile.getJpegEncodingQualityParameter(CameraProfile.QUALITY_MEDIUM);
+        int high = CameraProfile.getJpegEncodingQualityParameter(CameraProfile.QUALITY_HIGH);
+        checkQuality(low, mid, high);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getJpegEncodingQualityParameter",
+            args = {int.class, int.class}
+        )
+    })
+    public void testGetWithId() {
+        int nCamera = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCamera; id++) {
+            int low = CameraProfile.getJpegEncodingQualityParameter(id, CameraProfile.QUALITY_LOW);
+            int mid = CameraProfile.getJpegEncodingQualityParameter(id, CameraProfile.QUALITY_MEDIUM);
+            int high = CameraProfile.getJpegEncodingQualityParameter(id, CameraProfile.QUALITY_HIGH);
+            checkQuality(low, mid, high);
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/EnvReverbTest.java b/tests/tests/media/src/android/media/cts/EnvReverbTest.java
new file mode 100644
index 0000000..fe7baf9
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/EnvReverbTest.java
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) 2010 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.media.cts;
+
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.audiofx.EnvironmentalReverb;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+@TestTargetClass(EnvironmentalReverb.class)
+public class EnvReverbTest extends AndroidTestCase {
+
+    private String TAG = "EnvReverbTest";
+    private final static int MILLIBEL_TOLERANCE = 100;            // +/-1dB
+    private final static float DELAY_TOLERANCE = 1.05f;           // 5%
+    private final static float RATIO_TOLERANCE = 1.05f;           // 5%
+
+    private EnvironmentalReverb mReverb = null;
+    private EnvironmentalReverb mReverb2 = null;
+    private int mSession = -1;
+    private boolean mHasControl = false;
+    private boolean mIsEnabled = false;
+    private int mChangedParameter = -1;
+    private boolean mInitialized = false;
+    private Looper mLooper = null;
+    private final Object mLock = new Object();
+
+
+    //-----------------------------------------------------------------
+    // ENVIRONMENTAL REVERB TESTS:
+    //----------------------------------
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "EnvironmentalReverb",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getId",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        )
+    })
+    public void test0_0ConstructorAndRelease() throws Exception {
+        EnvironmentalReverb envReverb = null;
+         try {
+            envReverb = new EnvironmentalReverb(0, 0);
+            assertNotNull("could not create EnvironmentalReverb", envReverb);
+            try {
+                assertTrue("invalid effect ID", (envReverb.getId() != 0));
+            } catch (IllegalStateException e) {
+                fail("EnvironmentalReverb not initialized");
+            }
+        } catch (IllegalArgumentException e) {
+            fail("EnvironmentalReverb not found");
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        } finally {
+            if (envReverb != null) {
+                envReverb.release();
+            }
+        }
+    }
+
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test room level and room HF level
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setRoomLevel",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getRoomLevel",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setRoomHFLevel",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getRoomHFLevel",
+            args = {}
+        )
+    })
+    public void test1_0Room() throws Exception {
+        getReverb(0);
+        try {
+            short level = mReverb.getRoomLevel();
+            level = (short)((level == 0) ? -1000 : 0);
+            mReverb.setRoomLevel(level);
+            short level2 = mReverb.getRoomLevel();
+            assertTrue("got incorrect room level",
+                    (level2 > (level - MILLIBEL_TOLERANCE)) &&
+                    (level2 < (level + MILLIBEL_TOLERANCE)));
+
+            level = mReverb.getRoomHFLevel();
+            level = (short)((level == 0) ? -1000 : 0);
+            mReverb.setRoomHFLevel(level);
+            level2 = mReverb.getRoomHFLevel();
+            assertTrue("got incorrect room HF level",
+                    (level2 > (level - MILLIBEL_TOLERANCE)) &&
+                    (level2 < (level + MILLIBEL_TOLERANCE)));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //Test case 1.1: test decay time and ratio
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setDecayTime",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getDecayTime",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setDecayHFRatio",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getDecayHFRatio",
+            args = {}
+        )
+    })
+    public void test1_1Decay() throws Exception {
+        getReverb(0);
+        try {
+            int time = mReverb.getDecayTime();
+            time = (time == 500) ? 1000 : 500;
+            mReverb.setDecayTime(time);
+            int time2 = mReverb.getDecayTime();
+            assertTrue("got incorrect decay time",
+                    ((float)time2 > (float)(time / DELAY_TOLERANCE)) &&
+                    ((float)time2 < (float)(time * DELAY_TOLERANCE)));
+            short ratio = mReverb.getDecayHFRatio();
+            ratio = (short)((ratio == 500) ? 1000 : 500);
+            mReverb.setDecayHFRatio(ratio);
+            short ratio2 = mReverb.getDecayHFRatio();
+            assertTrue("got incorrect decay HF ratio",
+                    ((float)ratio2 > (float)(ratio / RATIO_TOLERANCE)) &&
+                    ((float)ratio2 < (float)(ratio * RATIO_TOLERANCE)));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+    }
+
+
+    //Test case 1.2: test reverb level and delay
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setReverbLevel",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getReverbLevel",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setReverbDelay",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getReverbDelay",
+            args = {}
+        )
+    })
+    public void test1_2Reverb() throws Exception {
+        getReverb(0);
+        try {
+            short level = mReverb.getReverbLevel();
+            level = (short)((level == 0) ? -1000 : 0);
+            mReverb.setReverbLevel(level);
+            short level2 = mReverb.getReverbLevel();
+            assertTrue("got incorrect reverb level",
+                    (level2 > (level - MILLIBEL_TOLERANCE)) &&
+                    (level2 < (level + MILLIBEL_TOLERANCE)));
+
+// FIXME:uncomment actual test when early reflections are implemented in the reverb
+//            int time = mReverb.getReverbDelay();
+//             mReverb.setReverbDelay(time);
+//            int time2 = mReverb.getReverbDelay();
+//            assertTrue("got incorrect reverb delay",
+//                    ((float)time2 > (float)(time / DELAY_TOLERANCE)) &&
+//                    ((float)time2 < (float)(time * DELAY_TOLERANCE)));
+            mReverb.setReverbDelay(0);
+            int time2 = mReverb.getReverbDelay();
+            assertEquals("got incorrect reverb delay", mReverb.getReverbDelay(), 0);
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //Test case 1.3: test early reflections level and delay
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setReflectionsLevel",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getReflectionsLevel",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setReflectionsDelay",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getReflectionsDelay",
+            args = {}
+        )
+    })
+    public void test1_3Reflections() throws Exception {
+        getReverb(0);
+        try {
+// FIXME:uncomment actual test when early reflections are implemented in the reverb
+//            short level = mReverb.getReflectionsLevel();
+//            level = (short)((level == 0) ? -1000 : 0);
+//            mReverb.setReflectionsLevel(level);
+//            short level2 = mReverb.getReflectionsLevel();
+//            assertTrue("got incorrect reflections level",
+//                    (level2 > (level - MILLIBEL_TOLERANCE)) &&
+//                    (level2 < (level + MILLIBEL_TOLERANCE)));
+//
+//            int time = mReverb.getReflectionsDelay();
+//            time = (time == 20) ? 0 : 20;
+//            mReverb.setReflectionsDelay(time);
+//            int time2 = mReverb.getReflectionsDelay();
+//            assertTrue("got incorrect reflections delay",
+//                    ((float)time2 > (float)(time / DELAY_TOLERANCE)) &&
+//                    ((float)time2 < (float)(time * DELAY_TOLERANCE)));
+            mReverb.setReflectionsLevel((short) 0);
+            assertEquals("got incorrect reverb delay",
+                    mReverb.getReflectionsLevel(), (short) 0);
+            mReverb.setReflectionsDelay(0);
+            assertEquals("got incorrect reverb delay",
+                    mReverb.getReflectionsDelay(), 0);
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //Test case 1.4: test diffusion and density
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setDiffusion",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getDiffusion",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setDensity",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getDensity",
+            args = {}
+        )
+    })
+    public void test1_4DiffusionAndDensity() throws Exception {
+        getReverb(0);
+        try {
+            short ratio = mReverb.getDiffusion();
+            ratio = (short)((ratio == 500) ? 1000 : 500);
+            mReverb.setDiffusion(ratio);
+            short ratio2 = mReverb.getDiffusion();
+            assertTrue("got incorrect diffusion",
+                    ((float)ratio2 > (float)(ratio / RATIO_TOLERANCE)) &&
+                    ((float)ratio2 < (float)(ratio * RATIO_TOLERANCE)));
+
+            ratio = mReverb.getDensity();
+            ratio = (short)((ratio == 500) ? 1000 : 500);
+            mReverb.setDensity(ratio);
+            ratio2 = mReverb.getDensity();
+            assertTrue("got incorrect density",
+                    ((float)ratio2 > (float)(ratio / RATIO_TOLERANCE)) &&
+                    ((float)ratio2 < (float)(ratio * RATIO_TOLERANCE)));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //Test case 1.5: test properties
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getProperties",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setProperties",
+            args = {EnvironmentalReverb.Settings.class}
+        )
+    })
+    public void test1_5Properties() throws Exception {
+        getReverb(0);
+        try {
+            EnvironmentalReverb.Settings settings = mReverb.getProperties();
+            String str = settings.toString();
+            settings = new EnvironmentalReverb.Settings(str);
+            short level = (short)((settings.roomLevel == 0) ? -1000 : 0);
+            settings.roomLevel = level;
+            mReverb.setProperties(settings);
+            settings = mReverb.getProperties();
+            assertTrue("setProperties failed",
+                    (settings.roomLevel >= (level - MILLIBEL_TOLERANCE)) &&
+                    (settings.roomLevel <= (level + MILLIBEL_TOLERANCE)));
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - Effect enable/disable
+    //----------------------------------
+
+    //Test case 2.0: test setEnabled() and getEnabled() in valid state
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        )
+    })
+    public void test2_0SetEnabledGetEnabled() throws Exception {
+        getReverb(0);
+        try {
+            mReverb.setEnabled(true);
+            assertTrue("invalid state from getEnabled", mReverb.getEnabled());
+            mReverb.setEnabled(false);
+            assertFalse("invalid state to getEnabled", mReverb.getEnabled());
+        } catch (IllegalStateException e) {
+            fail("setEnabled() in wrong state");
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //Test case 2.1: test setEnabled() throws exception after release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        )
+    })
+    public void test2_1SetEnabledAfterRelease() throws Exception {
+        getReverb(0);
+        mReverb.release();
+        try {
+            mReverb.setEnabled(true);
+            fail("setEnabled() processed after release()");
+        } catch (IllegalStateException e) {
+            // test passed
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 3 priority and listeners
+    //----------------------------------
+
+    //Test case 3.0: test control status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setControlStatusListener",
+            args = {AudioEffect.OnControlStatusChangeListener.class}
+        )
+    })
+    public void test3_0ControlStatusListener() throws Exception {
+        mHasControl = true;
+        createListenerLooper(true, false, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        synchronized(mLock) {
+            try {
+                getReverb(0);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseReverb();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("effect control not lost by effect1", mHasControl);
+    }
+
+    //Test case 3.1: test enable status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnableStatusListener",
+            args = {AudioEffect.OnEnableStatusChangeListener.class}
+        )
+    })
+    public void test3_1EnableStatusListener() throws Exception {
+        createListenerLooper(false, true, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        mReverb2.setEnabled(true);
+        mIsEnabled = true;
+        getReverb(0);
+        synchronized(mLock) {
+            try {
+                mReverb.setEnabled(false);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseReverb();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("enable status not updated", mIsEnabled);
+    }
+
+    //Test case 3.2: test parameter changed listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameterListener",
+            args = {EnvironmentalReverb.OnParameterChangeListener.class}
+        )
+    })
+    public void test3_2ParameterChangedListener() throws Exception {
+        createListenerLooper(false, false, true);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        getReverb(0);
+        synchronized(mLock) {
+            try {
+                mChangedParameter = -1;
+                mReverb.setRoomLevel((short)0);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseReverb();
+                terminateListenerLooper();
+            }
+        }
+        assertEquals("parameter change not received",
+                EnvironmentalReverb.PARAM_ROOM_LEVEL, mChangedParameter);
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private void getReverb(int session) {
+         if (mReverb == null || session != mSession) {
+             if (session != mSession && mReverb != null) {
+                 mReverb.release();
+                 mReverb = null;
+             }
+             try {
+                mReverb = new EnvironmentalReverb(0, session);
+                mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getReverb() EnvironmentalReverb not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getReverb() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mReverb", mReverb);
+    }
+
+    private void releaseReverb() {
+        if (mReverb != null) {
+            mReverb.release();
+            mReverb = null;
+        }
+    }
+
+    // Initializes the reverb listener looper
+    class ListenerThread extends Thread {
+        boolean mControl;
+        boolean mEnable;
+        boolean mParameter;
+
+        public ListenerThread(boolean control, boolean enable, boolean parameter) {
+            super();
+            mControl = control;
+            mEnable = enable;
+            mParameter = parameter;
+        }
+    }
+
+    private void createListenerLooper(boolean control, boolean enable, boolean parameter) {
+        mInitialized = false;
+        new ListenerThread(control, enable, parameter) {
+            @Override
+            public void run() {
+                // Set up a looper
+                Looper.prepare();
+
+                // Save the looper so that we can terminate this thread
+                // after we are done with it.
+                mLooper = Looper.myLooper();
+
+                mReverb2 = new EnvironmentalReverb(0, 0);
+                assertNotNull("could not create reverb2", mReverb2);
+
+                if (mControl) {
+                    mReverb2.setControlStatusListener(
+                            new AudioEffect.OnControlStatusChangeListener() {
+                        public void onControlStatusChange(
+                                AudioEffect effect, boolean controlGranted) {
+                            synchronized(mLock) {
+                                if (effect == mReverb2) {
+                                    mHasControl = controlGranted;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mEnable) {
+                    mReverb2.setEnableStatusListener(
+                            new AudioEffect.OnEnableStatusChangeListener() {
+                        public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+                            synchronized(mLock) {
+                                if (effect == mReverb2) {
+                                    mIsEnabled = enabled;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mParameter) {
+                    mReverb2.setParameterListener(new EnvironmentalReverb.OnParameterChangeListener() {
+                        public void onParameterChange(EnvironmentalReverb effect,
+                                int status, int param, int value)
+                        {
+                            synchronized(mLock) {
+                                if (effect == mReverb2) {
+                                    mChangedParameter = param;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+
+                synchronized(mLock) {
+                    mInitialized = true;
+                    mLock.notify();
+                }
+                Looper.loop();  // Blocks forever until Looper.quit() is called.
+            }
+        }.start();
+    }
+
+    // Terminates the listener looper thread.
+    private void terminateListenerLooper() {
+        if (mReverb2 != null) {
+            mReverb2.release();
+            mReverb2 = null;
+        }
+        if (mLooper != null) {
+            mLooper.quit();
+            mLooper = null;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/EqualizerTest.java b/tests/tests/media/src/android/media/cts/EqualizerTest.java
new file mode 100644
index 0000000..955986e
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/EqualizerTest.java
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2010 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.media.cts;
+
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.audiofx.Equalizer;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+@TestTargetClass(Equalizer.class)
+public class EqualizerTest extends AndroidTestCase {
+
+    private String TAG = "EqualizerTest";
+    private final static int MIN_NUMBER_OF_BANDS = 4;
+    private final static int MAX_LEVEL_RANGE_LOW = 0;             // 0dB
+    private final static int MIN_LEVEL_RANGE_HIGH = 0;            // 0dB
+    private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000; // 1kHz
+    private final static int MIN_NUMBER_OF_PRESETS = 0;
+    private final static float TOLERANCE = 100;                   // +/-1dB
+
+    private Equalizer mEqualizer = null;
+    private Equalizer mEqualizer2 = null;
+    private int mSession = -1;
+    private boolean mHasControl = false;
+    private boolean mIsEnabled = false;
+    private int mChangedParameter = -1;
+    private boolean mInitialized = false;
+    private Looper mLooper = null;
+    private final Object mLock = new Object();
+
+
+    //-----------------------------------------------------------------
+    // EQUALIZER TESTS:
+    //----------------------------------
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "Equalizer",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getId",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        )
+    })
+    public void test0_0ConstructorAndRelease() throws Exception {
+        Equalizer eq = null;
+        try {
+            eq = new Equalizer(0, 0);
+            assertNotNull("could not create Equalizer", eq);
+            try {
+                assertTrue("invalid effect ID", (eq.getId() != 0));
+            } catch (IllegalStateException e) {
+                fail("Equalizer not initialized");
+            }
+        } catch (IllegalArgumentException e) {
+            fail("Equalizer not found");
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        } finally {
+            if (eq != null) {
+                eq.release();
+            }
+        }
+    }
+
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test setBandLevel() and getBandLevel()
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getNumberOfBands",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getBandLevelRange",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setBandLevel",
+            args = {short.class, short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getBandLevel",
+            args = {short.class}
+        )
+    })
+    public void test1_0BandLevel() throws Exception {
+        getEqualizer(0);
+        try {
+            short numBands = mEqualizer.getNumberOfBands();
+            assertTrue("not enough bands", numBands >= MIN_NUMBER_OF_BANDS);
+
+            short[] levelRange = mEqualizer.getBandLevelRange();
+            assertTrue("min level too high", levelRange[0] <= MAX_LEVEL_RANGE_LOW);
+            assertTrue("max level too low", levelRange[1] >= MIN_LEVEL_RANGE_HIGH);
+
+            mEqualizer.setBandLevel((short)0, levelRange[1]);
+            short level = mEqualizer.getBandLevel((short)0);
+            // allow +/- TOLERANCE margin on actual level compared to requested level
+            assertTrue("setBandLevel failed",
+                    (level >= (levelRange[1] - TOLERANCE)) &&
+                    (level <= (levelRange[1] + TOLERANCE)));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseEqualizer();
+        }
+    }
+
+    //Test case 1.1: test band frequency
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getBand",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getBandFreqRange",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getCenterFreq",
+            args = {short.class}
+        )
+    })
+    public void test1_1BandFrequency() throws Exception {
+        getEqualizer(0);
+        try {
+            short band = mEqualizer.getBand(TEST_FREQUENCY_MILLIHERTZ);
+            assertTrue("getBand failed", band >= 0);
+            int[] freqRange = mEqualizer.getBandFreqRange(band);
+            assertTrue("getBandFreqRange failed",
+                    (freqRange[0] <= TEST_FREQUENCY_MILLIHERTZ) &&
+                    (freqRange[1] >= TEST_FREQUENCY_MILLIHERTZ));
+            int freq = mEqualizer.getCenterFreq(band);
+            assertTrue("getCenterFreq failed",
+                    (freqRange[0] <= freq) && (freqRange[1] >= freq));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseEqualizer();
+        }
+    }
+
+    //Test case 1.2: test presets
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getNumberOfPresets",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "usePreset",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getCurrentPreset",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getPresetName",
+            args = {short.class}
+        )
+    })
+    public void test1_2Presets() throws Exception {
+        getEqualizer(0);
+        try {
+            short numPresets = mEqualizer.getNumberOfPresets();
+            assertTrue("getNumberOfPresets failed", numPresets >= MIN_NUMBER_OF_PRESETS);
+            if (numPresets > 0) {
+                mEqualizer.usePreset((short)(numPresets - 1));
+                short preset = mEqualizer.getCurrentPreset();
+                assertEquals("usePreset failed", preset, (short)(numPresets - 1));
+                String name = mEqualizer.getPresetName(preset);
+                assertNotNull("getPresetName failed", name);
+            }
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseEqualizer();
+        }
+    }
+
+    //Test case 1.3: test properties
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getProperties",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setProperties",
+            args = {Equalizer.Settings.class}
+        )
+    })
+    public void test1_3Properties() throws Exception {
+        getEqualizer(0);
+        try {
+            Equalizer.Settings settings = mEqualizer.getProperties();
+            assertTrue("no enough bands", settings.numBands >= MIN_NUMBER_OF_BANDS);
+            short newLevel = 0;
+            if (settings.bandLevels[0] == 0) {
+                newLevel = -600;
+            }
+            String str = settings.toString();
+            settings = new Equalizer.Settings(str);
+            settings.curPreset = (short)-1;
+            settings.bandLevels[0] = newLevel;
+            mEqualizer.setProperties(settings);
+            settings = mEqualizer.getProperties();
+            assertTrue("setProperties failed",
+                    (settings.bandLevels[0] >= (newLevel - TOLERANCE)) &&
+                    (settings.bandLevels[0] <= (newLevel + TOLERANCE)));
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseEqualizer();
+        }
+    }
+
+    //Test case 1.4: test setBandLevel() throws exception after release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setBandLevel",
+            args = {short.class, short.class}
+        )
+    })
+    public void test1_4SetBandLevelAfterRelease() throws Exception {
+
+        getEqualizer(0);
+        mEqualizer.release();
+        try {
+            mEqualizer.setBandLevel((short)0, (short)0);
+        } catch (IllegalStateException e) {
+            // test passed
+        } finally {
+            releaseEqualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - Effect enable/disable
+    //----------------------------------
+
+    //Test case 2.0: test setEnabled() and getEnabled() in valid state
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        )
+    })
+    public void test2_0SetEnabledGetEnabled() throws Exception {
+        getEqualizer(0);
+        try {
+            mEqualizer.setEnabled(true);
+            assertTrue("invalid state from getEnabled", mEqualizer.getEnabled());
+            mEqualizer.setEnabled(false);
+            assertFalse("invalid state to getEnabled", mEqualizer.getEnabled());
+
+        } catch (IllegalStateException e) {
+            fail("setEnabled() in wrong state");
+        } finally {
+            releaseEqualizer();
+        }
+    }
+
+    //Test case 2.1: test setEnabled() throws exception after release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        )
+    })
+    public void test2_1SetEnabledAfterRelease() throws Exception {
+
+        getEqualizer(0);
+        mEqualizer.release();
+        try {
+            mEqualizer.setEnabled(true);
+        } catch (IllegalStateException e) {
+            // test passed
+        } finally {
+            releaseEqualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 3 priority and listeners
+    //----------------------------------
+
+    //Test case 3.0: test control status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setControlStatusListener",
+            args = {AudioEffect.OnControlStatusChangeListener.class}
+        )
+    })
+    public void test3_0ControlStatusListener() throws Exception {
+        mHasControl = true;
+        createListenerLooper(true, false, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        synchronized(mLock) {
+            try {
+                getEqualizer(0);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseEqualizer();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("effect control not lost by effect1", mHasControl);
+    }
+
+    //Test case 3.1: test enable status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnableStatusListener",
+            args = {AudioEffect.OnEnableStatusChangeListener.class}
+        )
+    })
+    public void test3_1EnableStatusListener() throws Exception {
+        createListenerLooper(false, true, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        mEqualizer2.setEnabled(true);
+        mIsEnabled = true;
+        getEqualizer(0);
+        synchronized(mLock) {
+            try {
+                mEqualizer.setEnabled(false);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseEqualizer();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("enable status not updated", mIsEnabled);
+    }
+
+    //Test case 3.2: test parameter changed listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameterListener",
+            args = {Equalizer.OnParameterChangeListener.class}
+        )
+    })
+    public void test3_2ParameterChangedListener() throws Exception {
+        createListenerLooper(false, false, true);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        getEqualizer(0);
+        synchronized(mLock) {
+            try {
+                mChangedParameter = -1;
+                mEqualizer.setBandLevel((short)0, (short)0);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseEqualizer();
+                terminateListenerLooper();
+            }
+        }
+        assertEquals("parameter change not received",
+                Equalizer.PARAM_BAND_LEVEL, mChangedParameter);
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private void getEqualizer(int session) {
+         if (mEqualizer == null || session != mSession) {
+             if (session != mSession && mEqualizer != null) {
+                 mEqualizer.release();
+                 mEqualizer = null;
+             }
+             try {
+                mEqualizer = new Equalizer(0, session);
+                mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getEqualizer() Equalizer not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getEqualizer() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mEqualizer", mEqualizer);
+    }
+
+    private void releaseEqualizer() {
+        if (mEqualizer != null) {
+            mEqualizer.release();
+            mEqualizer = null;
+        }
+    }
+
+    // Initializes the equalizer listener looper
+    class ListenerThread extends Thread {
+        boolean mControl;
+        boolean mEnable;
+        boolean mParameter;
+
+        public ListenerThread(boolean control, boolean enable, boolean parameter) {
+            super();
+            mControl = control;
+            mEnable = enable;
+            mParameter = parameter;
+        }
+    }
+
+    private void createListenerLooper(boolean control, boolean enable, boolean parameter) {
+        mInitialized = false;
+        new ListenerThread(control, enable, parameter) {
+            @Override
+            public void run() {
+                // Set up a looper
+                Looper.prepare();
+
+                // Save the looper so that we can terminate this thread
+                // after we are done with it.
+                mLooper = Looper.myLooper();
+
+                mEqualizer2 = new Equalizer(0, 0);
+                assertNotNull("could not create Equalizer2", mEqualizer2);
+
+                if (mControl) {
+                    mEqualizer2.setControlStatusListener(
+                            new AudioEffect.OnControlStatusChangeListener() {
+                        public void onControlStatusChange(
+                                AudioEffect effect, boolean controlGranted) {
+                            synchronized(mLock) {
+                                if (effect == mEqualizer2) {
+                                    mHasControl = controlGranted;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mEnable) {
+                    mEqualizer2.setEnableStatusListener(
+                            new AudioEffect.OnEnableStatusChangeListener() {
+                        public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+                            synchronized(mLock) {
+                                if (effect == mEqualizer2) {
+                                    mIsEnabled = enabled;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mParameter) {
+                    mEqualizer2.setParameterListener(new Equalizer.OnParameterChangeListener() {
+                        public void onParameterChange(Equalizer effect,
+                                int status, int param1, int param2, int value)
+                        {
+                            synchronized(mLock) {
+                                if (effect == mEqualizer2) {
+                                    mChangedParameter = param1;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+
+                synchronized(mLock) {
+                    mInitialized = true;
+                    mLock.notify();
+                }
+                Looper.loop();  // Blocks forever until Looper.quit() is called.
+            }
+        }.start();
+    }
+
+    // Terminates the listener looper thread.
+    private void terminateListenerLooper() {
+        if (mEqualizer2 != null) {
+            mEqualizer2.release();
+            mEqualizer2 = null;
+        }
+        if (mLooper != null) {
+            mLooper.quit();
+            mLooper = null;
+        }
+    }
+
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 78c8ed9..ee0b025 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -15,7 +15,6 @@
  */
 package android.media.cts;
 
-import dalvik.annotation.BrokenTest;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
@@ -27,6 +26,7 @@
 import android.media.MediaRecorder.OnInfoListener;
 import android.os.Environment;
 import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
 import android.view.Surface;
 
 import java.io.File;
@@ -39,8 +39,8 @@
     private final String OUTPUT_PATH;
     private final String OUTPUT_PATH2;
     private static final int RECORD_TIME = 3000;
-    private static final int VIDEO_WIDTH = 320;
-    private static final int VIDEO_HEIGHT = 240;
+    private static final int VIDEO_WIDTH = 176;
+    private static final int VIDEO_HEIGHT = 144;
     private static final int FRAME_RATE = 15;
     private static final long MAX_FILE_SIZE = 5000;
     private static final int MAX_DURATION_MSEC = 200;
@@ -97,6 +97,7 @@
         }
         if (mCamera != null)  {
             mCamera.release();
+            mCamera = null;
         }
         super.tearDown();
     }
@@ -167,24 +168,29 @@
         method = "setCamera",
         args = {Camera.class}
     )
-    @BrokenTest(value="No longer works in Donut. CameraService reports: " +
-            "Attempt to use locked camera from different process")
+    @UiThreadTest
     public void testSetCamera() throws Exception {
-        mCamera = Camera.open();
-        mMediaRecorder.setCamera(mCamera);
-        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
-        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
-        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
-        mMediaRecorder.setVideoFrameRate(FRAME_RATE);
-        mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
-        mMediaRecorder.setPreviewDisplay(getActivity().getSurfaceHolder().getSurface());
-        mMediaRecorder.setOutputFile(OUTPUT_PATH);
+        int nCamera = Camera.getNumberOfCameras();
+        for (int cameraId = 0; cameraId < nCamera; cameraId++) {
+            mCamera = Camera.open(cameraId);
+            mCamera.unlock();
+            mMediaRecorder.setCamera(mCamera);
+            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
+            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
+            mMediaRecorder.setVideoFrameRate(FRAME_RATE);
+            mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
+            mMediaRecorder.setPreviewDisplay(getActivity().getSurfaceHolder().getSurface());
+            mMediaRecorder.setOutputFile(OUTPUT_PATH);
 
-        mMediaRecorder.prepare();
-        mMediaRecorder.start();
-        Thread.sleep(1000);
-        mMediaRecorder.stop();
-        assertTrue(mOutFile.exists());
+            mMediaRecorder.prepare();
+            mMediaRecorder.start();
+            Thread.sleep(1000);
+            mMediaRecorder.stop();
+            assertTrue(mOutFile.exists());
+
+            mCamera.release();
+        }
     }
 
     private void checkOutputExist() {
@@ -389,6 +395,40 @@
         assertTrue(mOnInfoCalled);
     }
 
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "setMaxDuration",
+        args = {int.class}
+    )
+    public void testSetMaxDuration() throws Exception {
+        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+        mMediaRecorder.setMaxDuration(0);
+        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+        mMediaRecorder.prepare();
+        mMediaRecorder.start();
+        Thread.sleep(RECORD_TIME * 30);
+        mMediaRecorder.stop();
+        checkOutputExist();
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "setMaxFileSize",
+        args = {int.class}
+    )
+    public void testSetMaxFileSize() throws Exception {
+            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+            mMediaRecorder.setMaxFileSize(0);
+            mMediaRecorder.prepare();
+            mMediaRecorder.start();
+            Thread.sleep(RECORD_TIME * 30);
+            mMediaRecorder.stop();
+            checkOutputExist();
+    }
+
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java b/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java
new file mode 100644
index 0000000..3c201ca
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.media.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Environment;
+import android.test.AndroidTestCase;
+
+
+public class MediaScannerNotificationTest extends AndroidTestCase {
+    private static final int MEDIA_SCANNER_TIME_OUT = 2000;
+
+    private ScannerNotificationReceiver mScannerStartedReceiver;
+    private ScannerNotificationReceiver mScannerFinishedReceiver;
+    private boolean mScannerStarted;
+    private boolean mScannerFinished;
+
+    public void testMediaScannerNotification() throws InterruptedException {
+        mScannerStarted = false;
+        mScannerFinished = false;
+
+        IntentFilter scannerStartedIntentFilter = new IntentFilter(
+                Intent.ACTION_MEDIA_SCANNER_STARTED);
+        scannerStartedIntentFilter.addDataScheme("file");
+        IntentFilter scannerFinshedIntentFilter = new IntentFilter(
+                Intent.ACTION_MEDIA_SCANNER_FINISHED);
+        scannerFinshedIntentFilter.addDataScheme("file");
+
+        mScannerStartedReceiver = new ScannerNotificationReceiver(
+                Intent.ACTION_MEDIA_SCANNER_STARTED);
+        mScannerFinishedReceiver = new ScannerNotificationReceiver(
+                Intent.ACTION_MEDIA_SCANNER_FINISHED);
+
+        getContext().registerReceiver(mScannerStartedReceiver, scannerStartedIntentFilter);
+        getContext().registerReceiver(mScannerFinishedReceiver, scannerFinshedIntentFilter);
+
+        getContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
+            + Environment.getExternalStorageDirectory())));
+        mScannerStartedReceiver.waitForCalls(1, MEDIA_SCANNER_TIME_OUT);
+        mScannerFinishedReceiver.waitForCalls(1, MEDIA_SCANNER_TIME_OUT);
+
+        assertTrue(mScannerStarted);
+        assertTrue(mScannerFinished);
+    }
+
+    class ScannerNotificationReceiver extends BroadcastReceiver {
+        private int mCalls;
+        private int mExpectedCalls;
+        private String mAction;
+        private Object mLock;
+
+        ScannerNotificationReceiver(String action) {
+            mAction = action;
+            reset();
+            mLock = new Object();
+        }
+
+        void reset() {
+            mExpectedCalls = Integer.MAX_VALUE;
+            mCalls = 0;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(mAction)) {
+                if (mAction.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) {
+                    mScannerStarted = true;
+                } else if (mAction.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
+                    mScannerFinished = true;
+                }
+                synchronized (mLock) {
+                    mCalls += 1;
+                    if (mCalls >= mExpectedCalls) {
+                        mLock.notify();
+                    }
+                }
+            }
+        }
+
+        public void waitForCalls(int expectedCalls, long timeout) throws InterruptedException {
+            synchronized(mLock) {
+                mExpectedCalls = expectedCalls;
+                if (mCalls < mExpectedCalls) {
+                    mLock.wait(timeout);
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/PresetReverbTest.java b/tests/tests/media/src/android/media/cts/PresetReverbTest.java
new file mode 100644
index 0000000..a1b05bd
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/PresetReverbTest.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2010 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.media.cts;
+
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.audiofx.PresetReverb;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+@TestTargetClass(PresetReverb.class)
+public class PresetReverbTest extends AndroidTestCase {
+
+    private String TAG = "PresetReverbTest";
+    private final static short FIRST_PRESET = PresetReverb.PRESET_NONE;
+    private final static short LAST_PRESET = PresetReverb.PRESET_PLATE;
+    private PresetReverb mReverb = null;
+    private PresetReverb mReverb2 = null;
+    private int mSession = -1;
+    private boolean mHasControl = false;
+    private boolean mIsEnabled = false;
+    private int mChangedParameter = -1;
+    private boolean mInitialized = false;
+    private Looper mLooper = null;
+    private final Object mLock = new Object();
+
+
+    //-----------------------------------------------------------------
+    // PRESET REVERB TESTS:
+    //----------------------------------
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "PresetReverb",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getId",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        )
+    })
+    public void test0_0ConstructorAndRelease() throws Exception {
+        PresetReverb reverb = null;
+        try {
+            reverb = new PresetReverb(0, 0);
+            assertNotNull("could not create PresetReverb", reverb);
+            try {
+                assertTrue("invalid effect ID", (reverb.getId() != 0));
+            } catch (IllegalStateException e) {
+                fail("PresetReverb not initialized");
+            }
+        } catch (IllegalArgumentException e) {
+            fail("PresetReverb not found");
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        } finally {
+            if (reverb != null) {
+                reverb.release();
+            }
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test presets
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setPreset",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getPreset",
+            args = {}
+        )
+    })
+    public void test1_0Presets() throws Exception {
+        getReverb(0);
+        try {
+            for (short preset = FIRST_PRESET;
+                 preset <= LAST_PRESET;
+                 preset++) {
+                mReverb.setPreset(preset);
+                assertEquals("got incorrect preset", preset, mReverb.getPreset());
+            }
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //Test case 1.1: test properties
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getProperties",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setProperties",
+            args = {PresetReverb.Settings.class}
+        )
+    })
+    public void test1_1Properties() throws Exception {
+        getReverb(0);
+        try {
+            PresetReverb.Settings settings = mReverb.getProperties();
+            String str = settings.toString();
+            settings = new PresetReverb.Settings(str);
+            short preset = (settings.preset == PresetReverb.PRESET_SMALLROOM) ?
+                            PresetReverb.PRESET_MEDIUMROOM : PresetReverb.PRESET_SMALLROOM;
+            settings.preset = preset;
+            mReverb.setProperties(settings);
+            settings = mReverb.getProperties();
+            assertEquals("setProperties failed", settings.preset, preset);
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - Effect enable/disable
+    //----------------------------------
+
+    //Test case 2.0: test setEnabled() and getEnabled() in valid state
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        )
+    })
+    public void test2_0SetEnabledGetEnabled() throws Exception {
+        getReverb(0);
+        try {
+            mReverb.setEnabled(true);
+            assertTrue("invalid state from getEnabled", mReverb.getEnabled());
+            mReverb.setEnabled(false);
+            assertFalse("invalid state to getEnabled", mReverb.getEnabled());
+        } catch (IllegalStateException e) {
+            fail("setEnabled() in wrong state");
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //Test case 2.1: test setEnabled() throws exception after release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        )
+    })
+    public void test2_1SetEnabledAfterRelease() throws Exception {
+        getReverb(0);
+        mReverb.release();
+        try {
+            mReverb.setEnabled(true);
+        } catch (IllegalStateException e) {
+            // test passed
+        } finally {
+            releaseReverb();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 3 priority and listeners
+    //----------------------------------
+
+    //Test case 3.0: test control status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setControlStatusListener",
+            args = {AudioEffect.OnControlStatusChangeListener.class}
+        )
+    })
+    public void test3_0ControlStatusListener() throws Exception {
+        mHasControl = true;
+        createListenerLooper(true, false, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        synchronized(mLock) {
+            try {
+                getReverb(0);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseReverb();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("effect control not lost by effect1", mHasControl);
+    }
+
+    //Test case 3.1: test enable status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnableStatusListener",
+            args = {AudioEffect.OnEnableStatusChangeListener.class}
+        )
+    })
+    public void test3_1EnableStatusListener() throws Exception {
+        createListenerLooper(false, true, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        mReverb2.setEnabled(true);
+        mIsEnabled = true;
+        getReverb(0);
+        synchronized(mLock) {
+            try {
+                mReverb.setEnabled(false);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseReverb();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("enable status not updated", mIsEnabled);
+    }
+
+    //Test case 3.2: test parameter changed listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameterListener",
+            args = {PresetReverb.OnParameterChangeListener.class}
+        )
+    })
+    public void test3_2ParameterChangedListener() throws Exception {
+        createListenerLooper(false, false, true);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        getReverb(0);
+        synchronized(mLock) {
+            try {
+                mChangedParameter = -1;
+                mReverb.setPreset(PresetReverb.PRESET_SMALLROOM);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseReverb();
+                terminateListenerLooper();
+            }
+        }
+        assertEquals("parameter change not received",
+                PresetReverb.PARAM_PRESET, mChangedParameter);
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private void getReverb(int session) {
+         if (mReverb == null || session != mSession) {
+             if (session != mSession && mReverb != null) {
+                 mReverb.release();
+                 mReverb = null;
+             }
+             try {
+                mReverb = new PresetReverb(0, session);
+                mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getReverb() PresetReverb not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getReverb() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mReverb", mReverb);
+    }
+
+    private void releaseReverb() {
+        if (mReverb != null) {
+            mReverb.release();
+            mReverb = null;
+        }
+    }
+
+    // Initializes the reverb listener looper
+    class ListenerThread extends Thread {
+        boolean mControl;
+        boolean mEnable;
+        boolean mParameter;
+
+        public ListenerThread(boolean control, boolean enable, boolean parameter) {
+            super();
+            mControl = control;
+            mEnable = enable;
+            mParameter = parameter;
+        }
+    }
+
+    private void createListenerLooper(boolean control, boolean enable, boolean parameter) {
+        mInitialized = false;
+        new ListenerThread(control, enable, parameter) {
+            @Override
+            public void run() {
+                // Set up a looper
+                Looper.prepare();
+
+                // Save the looper so that we can terminate this thread
+                // after we are done with it.
+                mLooper = Looper.myLooper();
+
+                mReverb2 = new PresetReverb(0, 0);
+                assertNotNull("could not create Reverb2", mReverb2);
+
+                if (mControl) {
+                    mReverb2.setControlStatusListener(
+                            new AudioEffect.OnControlStatusChangeListener() {
+                        public void onControlStatusChange(
+                                AudioEffect effect, boolean controlGranted) {
+                            synchronized(mLock) {
+                                if (effect == mReverb2) {
+                                    mHasControl = controlGranted;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mEnable) {
+                    mReverb2.setEnableStatusListener(
+                            new AudioEffect.OnEnableStatusChangeListener() {
+                        public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+                            synchronized(mLock) {
+                                if (effect == mReverb2) {
+                                    mIsEnabled = enabled;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mParameter) {
+                    mReverb2.setParameterListener(new PresetReverb.OnParameterChangeListener() {
+                        public void onParameterChange(PresetReverb effect,
+                                int status, int param, short value)
+                        {
+                            synchronized(mLock) {
+                                if (effect == mReverb2) {
+                                    mChangedParameter = param;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+
+                synchronized(mLock) {
+                    mInitialized = true;
+                    mLock.notify();
+                }
+                Looper.loop();  // Blocks forever until Looper.quit() is called.
+            }
+        }.start();
+    }
+
+    // Terminates the listener looper thread.
+    private void terminateListenerLooper() {
+        if (mReverb2 != null) {
+            mReverb2.release();
+            mReverb2 = null;
+        }
+        if (mLooper != null) {
+            mLooper.quit();
+            mLooper = null;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/VideoEditorTest.java b/tests/tests/media/src/android/media/cts/VideoEditorTest.java
new file mode 100644
index 0000000..481f4d6
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/VideoEditorTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 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.media.cts;
+
+import com.android.cts.stub.R;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+public class VideoEditorTest extends ActivityInstrumentationTestCase2<MediaStubActivity> {
+
+    public VideoEditorTest() {
+        super(MediaStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        //setup for each test case.
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        //Test case clean up.
+        super.tearDown();
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/VirtualizerTest.java b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
new file mode 100644
index 0000000..0a82f09
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2010 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.media.cts;
+
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.audiofx.Virtualizer;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+@TestTargetClass(Virtualizer.class)
+public class VirtualizerTest extends AndroidTestCase {
+
+    private String TAG = "VirtualizerTest";
+    private final static short TEST_STRENGTH = 500;
+    private final static short TEST_STRENGTH2 = 1000;
+    private final static float STRENGTH_TOLERANCE = 1.1f;  // 10%
+
+    private Virtualizer mVirtualizer = null;
+    private Virtualizer mVirtualizer2 = null;
+    private int mSession = -1;
+    private boolean mHasControl = false;
+    private boolean mIsEnabled = false;
+    private int mChangedParameter = -1;
+    private boolean mInitialized = false;
+    private Looper mLooper = null;
+    private final Object mLock = new Object();
+
+    //-----------------------------------------------------------------
+    // VIRTUALIZER TESTS:
+    //----------------------------------
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "Virtualizer",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getId",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        )
+    })
+    public void test0_0ConstructorAndRelease() throws Exception {
+        Virtualizer eq = null;
+        try {
+            eq = new Virtualizer(0, 0);
+            assertNotNull(" could not create Virtualizer", eq);
+            try {
+                assertTrue(" invalid effect ID", (eq.getId() != 0));
+            } catch (IllegalStateException e) {
+                fail("Virtualizer not initialized");
+            }
+        } catch (IllegalArgumentException e) {
+            fail("Virtualizer not found");
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        } finally {
+            if (eq != null) {
+                eq.release();
+            }
+        }
+    }
+
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test strength
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getStrengthSupported",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setStrength",
+            args = {short.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getRoundedStrength",
+            args = {}
+        )
+    })
+    public void test1_0Strength() throws Exception {
+        getVirtualizer(0);
+        try {
+            if (mVirtualizer.getStrengthSupported()) {
+                short strength = mVirtualizer.getRoundedStrength();
+                strength = (strength == TEST_STRENGTH) ? TEST_STRENGTH2 : TEST_STRENGTH;
+                mVirtualizer.setStrength((short)strength);
+                short strength2 = mVirtualizer.getRoundedStrength();
+                // allow STRENGTH_TOLERANCE difference between set strength and rounded strength
+                assertTrue("got incorrect strength",
+                        ((float)strength2 > (float)strength / STRENGTH_TOLERANCE) &&
+                        ((float)strength2 < (float)strength * STRENGTH_TOLERANCE));
+            } else {
+                short strength = mVirtualizer.getRoundedStrength();
+                assertTrue("got incorrect strength", strength >= 0 && strength <= 1000);
+            }
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 1.1: test properties
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getProperties",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setProperties",
+            args = {Virtualizer.Settings.class}
+        )
+    })
+    public void test1_1Properties() throws Exception {
+        getVirtualizer(0);
+        try {
+            Virtualizer.Settings settings = mVirtualizer.getProperties();
+            String str = settings.toString();
+            settings = new Virtualizer.Settings(str);
+
+            short strength = settings.strength;
+            if (mVirtualizer.getStrengthSupported()) {
+                strength = (strength == TEST_STRENGTH) ? TEST_STRENGTH2 : TEST_STRENGTH;
+            }
+            settings.strength = strength;
+            mVirtualizer.setProperties(settings);
+            settings = mVirtualizer.getProperties();
+
+            if (mVirtualizer.getStrengthSupported()) {
+                // allow STRENGTH_TOLERANCE difference between set strength and rounded strength
+                assertTrue("got incorrect strength",
+                        ((float)settings.strength > (float)strength / STRENGTH_TOLERANCE) &&
+                        ((float)settings.strength < (float)strength * STRENGTH_TOLERANCE));
+            }
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 1.2: test setStrength() throws exception after release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setStrength",
+            args = {short.class}
+        )
+    })
+    public void test1_2SetStrengthAfterRelease() throws Exception {
+        getVirtualizer(0);
+        mVirtualizer.release();
+        try {
+            mVirtualizer.setStrength(TEST_STRENGTH);
+        } catch (IllegalStateException e) {
+            // test passed
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - Effect enable/disable
+    //----------------------------------
+
+    //Test case 2.0: test setEnabled() and getEnabled() in valid state
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        )
+    })
+    public void test2_0SetEnabledGetEnabled() throws Exception {
+        getVirtualizer(0);
+        try {
+            mVirtualizer.setEnabled(true);
+            assertTrue(" invalid state from getEnabled", mVirtualizer.getEnabled());
+            mVirtualizer.setEnabled(false);
+            assertFalse(" invalid state to getEnabled", mVirtualizer.getEnabled());
+        } catch (IllegalStateException e) {
+            fail("setEnabled() in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 2.1: test setEnabled() throws exception after release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        )
+    })
+    public void test2_1SetEnabledAfterRelease() throws Exception {
+        getVirtualizer(0);
+        mVirtualizer.release();
+        try {
+            mVirtualizer.setEnabled(true);
+        } catch (IllegalStateException e) {
+            // test passed
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 3 priority and listeners
+    //----------------------------------
+
+    //Test case 3.0: test control status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setControlStatusListener",
+            args = {AudioEffect.OnControlStatusChangeListener.class}
+        )
+    })
+    public void test3_0ControlStatusListener() throws Exception {
+        mHasControl = true;
+        createListenerLooper(true, false, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        synchronized(mLock) {
+            try {
+                getVirtualizer(0);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseVirtualizer();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("effect control not lost by effect1", mHasControl);
+    }
+
+    //Test case 3.1: test enable status listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnableStatusListener",
+            args = {AudioEffect.OnEnableStatusChangeListener.class}
+        )
+    })
+    public void test3_1EnableStatusListener() throws Exception {
+        createListenerLooper(false, true, false);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        mVirtualizer2.setEnabled(true);
+        mIsEnabled = true;
+        getVirtualizer(0);
+        synchronized(mLock) {
+            try {
+                mVirtualizer.setEnabled(false);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseVirtualizer();
+                terminateListenerLooper();
+            }
+        }
+        assertFalse("enable status not updated", mIsEnabled);
+    }
+
+    //Test case 3.2: test parameter changed listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setParameterListener",
+            args = {Virtualizer.OnParameterChangeListener.class}
+        )
+    })
+    public void test3_2ParameterChangedListener() throws Exception {
+        createListenerLooper(false, false, true);
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Looper creation: wait was interrupted.");
+            }
+        }
+        assertTrue(mInitialized);
+        getVirtualizer(0);
+        synchronized(mLock) {
+            try {
+                mChangedParameter = -1;
+                mVirtualizer.setStrength(TEST_STRENGTH);
+                mLock.wait(1000);
+            } catch(Exception e) {
+                Log.e(TAG, "Create second effect: wait was interrupted.");
+            } finally {
+                releaseVirtualizer();
+                terminateListenerLooper();
+            }
+        }
+        assertEquals("parameter change not received",
+                Virtualizer.PARAM_STRENGTH, mChangedParameter);
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private void getVirtualizer(int session) {
+         if (mVirtualizer == null || session != mSession) {
+             if (session != mSession && mVirtualizer != null) {
+                 mVirtualizer.release();
+                 mVirtualizer = null;
+             }
+             try {
+                mVirtualizer = new Virtualizer(0, session);
+                mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getVirtualizer() Virtualizer not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getVirtualizer() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mVirtualizer", mVirtualizer);
+    }
+
+    private void releaseVirtualizer() {
+        if (mVirtualizer != null) {
+            mVirtualizer.release();
+            mVirtualizer = null;
+        }
+    }
+
+    // Initializes the virtualizer listener looper
+    class ListenerThread extends Thread {
+        boolean mControl;
+        boolean mEnable;
+        boolean mParameter;
+
+        public ListenerThread(boolean control, boolean enable, boolean parameter) {
+            super();
+            mControl = control;
+            mEnable = enable;
+            mParameter = parameter;
+        }
+    }
+
+    private void createListenerLooper(boolean control, boolean enable, boolean parameter) {
+        mInitialized = false;
+        new ListenerThread(control, enable, parameter) {
+            @Override
+            public void run() {
+                // Set up a looper
+                Looper.prepare();
+
+                // Save the looper so that we can terminate this thread
+                // after we are done with it.
+                mLooper = Looper.myLooper();
+
+                mVirtualizer2 = new Virtualizer(0, 0);
+                assertNotNull("could not create virtualizer2", mVirtualizer2);
+
+                if (mControl) {
+                    mVirtualizer2.setControlStatusListener(
+                            new AudioEffect.OnControlStatusChangeListener() {
+                        public void onControlStatusChange(
+                                AudioEffect effect, boolean controlGranted) {
+                            synchronized(mLock) {
+                                if (effect == mVirtualizer2) {
+                                    mHasControl = controlGranted;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mEnable) {
+                    mVirtualizer2.setEnableStatusListener(
+                            new AudioEffect.OnEnableStatusChangeListener() {
+                        public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+                            synchronized(mLock) {
+                                if (effect == mVirtualizer2) {
+                                    mIsEnabled = enabled;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+                if (mParameter) {
+                    mVirtualizer2.setParameterListener(new Virtualizer.OnParameterChangeListener() {
+                        public void onParameterChange(Virtualizer effect, int status,
+                                int param, short value)
+                        {
+                            synchronized(mLock) {
+                                if (effect == mVirtualizer2) {
+                                    mChangedParameter = param;
+                                    mLock.notify();
+                                }
+                            }
+                        }
+                    });
+                }
+
+                synchronized(mLock) {
+                    mInitialized = true;
+                    mLock.notify();
+                }
+                Looper.loop();  // Blocks forever until Looper.quit() is called.
+            }
+        }.start();
+    }
+
+    // Terminates the listener looper thread.
+    private void terminateListenerLooper() {
+        if (mVirtualizer2 != null) {
+            mVirtualizer2.release();
+            mVirtualizer2 = null;
+        }
+        if (mLooper != null) {
+            mLooper.quit();
+            mLooper = null;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/VisualizerTest.java b/tests/tests/media/src/android/media/cts/VisualizerTest.java
new file mode 100644
index 0000000..6b29948
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/VisualizerTest.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2010 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.media.cts;
+
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.audiofx.Visualizer;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+@TestTargetClass(Visualizer.class)
+public class VisualizerTest extends AndroidTestCase {
+
+    private String TAG = "VisualizerTest";
+    private final static int MIN_CAPTURE_RATE_MAX = 10000; // 10Hz
+    private final static int MIN_CAPTURE_SIZE_MAX = 1024;
+    private final static int MAX_CAPTURE_SIZE_MIN = 512;
+
+    private Visualizer mVisualizer = null;
+    private int mSession = -1;
+    private boolean mInitialized = false;
+    private Looper mLooper = null;
+    private final Object mLock = new Object();
+    private byte[] mWaveform = null;
+    private byte[] mFft = null;
+    private boolean mCaptureWaveform = false;
+    private boolean mCaptureFft = false;
+
+    //-----------------------------------------------------------------
+    // VISUALIZER TESTS:
+    //----------------------------------
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "Visualizer",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "release",
+            args = {}
+        )
+    })
+    public void test0_0ConstructorAndRelease() throws Exception {
+        Visualizer visualizer = null;
+         try {
+            visualizer = new Visualizer(0);
+            assertNotNull("could not create Visualizer", visualizer);
+        } catch (IllegalArgumentException e) {
+            fail("Visualizer not found");
+        } catch (UnsupportedOperationException e) {
+            fail("Effect library not loaded");
+        } finally {
+            if (visualizer != null) {
+                visualizer.release();
+            }
+        }
+    }
+
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: capture rates
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getMaxCaptureRate",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getSamplingRate",
+            args = {}
+        )
+    })
+    public void test1_0CaptureRates() throws Exception {
+        getVisualizer(0);
+        try {
+            int captureRate = mVisualizer.getMaxCaptureRate();
+            assertTrue("insufficient max capture rate",
+                    captureRate >= MIN_CAPTURE_RATE_MAX);
+            int samplingRate = mVisualizer.getSamplingRate();
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseVisualizer();
+        }
+    }
+
+    //Test case 1.1: test capture size
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getCaptureSizeRange",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setCaptureSize",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getCaptureSize",
+            args = {}
+        )
+    })
+    public void test1_1CaptureSize() throws Exception {
+        getVisualizer(0);
+        try {
+            int[] range = mVisualizer.getCaptureSizeRange();
+            assertTrue("insufficient min capture size",
+                    range[0] <= MAX_CAPTURE_SIZE_MIN);
+            assertTrue("insufficient min capture size",
+                    range[1] >= MIN_CAPTURE_SIZE_MAX);
+            mVisualizer.setCaptureSize(range[0]);
+            assertEquals("insufficient min capture size",
+                    range[0], mVisualizer.getCaptureSize());
+            mVisualizer.setCaptureSize(range[1]);
+            assertEquals("insufficient min capture size",
+                    range[1], mVisualizer.getCaptureSize());
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } finally {
+            releaseVisualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - check capture
+    //----------------------------------
+
+    //Test case 2.0: test cature in polling mode
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getWaveForm",
+            args = {byte[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getFft",
+            args = {byte[].class}
+        )
+    })
+    public void test2_0PollingCapture() throws Exception {
+        try {
+            getVisualizer(0);
+            mVisualizer.setEnabled(true);
+            assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+            Thread.sleep(100);
+            // check capture on silence
+            byte[] data = new byte[mVisualizer.getCaptureSize()];
+            mVisualizer.getWaveForm(data);
+            int energy = computeEnergy(data, true);
+            assertEquals("getWaveForm reports energy for silence",
+                    0, energy);
+            mVisualizer.getFft(data);
+            energy = computeEnergy(data, false);
+            assertEquals("getFft reports energy for silence",
+                    0, energy);
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } catch (InterruptedException e) {
+            fail("sleep() interrupted");
+        }
+        finally {
+            releaseVisualizer();
+        }
+    }
+
+    //Test case 2.1: test capture with listener
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setEnabled",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getEnabled",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setDataCaptureListener",
+            args = {Visualizer.OnDataCaptureListener.class,
+                    int.class, boolean.class, boolean.class}
+        )
+    })
+    public void test2_1ListenerCapture() throws Exception {
+        try {
+            getVisualizer(0);
+            createListenerLooper();
+            synchronized(mLock) {
+                try {
+                    mLock.wait(1000);
+                } catch(Exception e) {
+                    Log.e(TAG, "Looper creation: wait was interrupted.");
+                }
+            }
+            assertTrue(mInitialized);
+
+            mVisualizer.setEnabled(true);
+            assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+
+            Thread.sleep(100);
+            // check capture on silence
+            synchronized(mLock) {
+                try {
+                    mCaptureWaveform = true;
+                    mLock.wait(1000);
+                    mCaptureWaveform = false;
+                } catch(Exception e) {
+                    Log.e(TAG, "Capture waveform: wait was interrupted.");
+                }
+            }
+            assertNotNull("waveform capture failed", mWaveform);
+            int energy = computeEnergy(mWaveform, true);
+            assertEquals("getWaveForm reports energy for silence",
+                    0, energy);
+
+            synchronized(mLock) {
+                try {
+                    mCaptureFft = true;
+                    mLock.wait(1000);
+                    mCaptureFft = false;
+                } catch(Exception e) {
+                    Log.e(TAG, "Capture FFT: wait was interrupted.");
+                }
+            }
+            assertNotNull("FFT capture failed", mFft);
+            energy = computeEnergy(mFft, false);
+            assertEquals("getFft reports energy for silence",
+                    0, energy);
+
+        } catch (IllegalArgumentException e) {
+            fail("Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("get parameter() rejected");
+        } catch (IllegalStateException e) {
+            fail("get parameter() called in wrong state");
+        } catch (InterruptedException e) {
+            fail("sleep() interrupted");
+        } finally {
+            terminateListenerLooper();
+            releaseVisualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private int computeEnergy(byte[] data, boolean pcm) {
+        int energy = 0;
+        if (data.length != 0) {
+            if (pcm) {
+                for (int i = 0; i < data.length; i++) {
+                    int tmp = ((int)data[i] & 0xFF) - 128;
+                    energy += tmp*tmp;
+                }
+            } else {
+                energy = (int)data[0] * (int)data[0];
+                for (int i = 2; i < data.length; i += 2) {
+                    int real = (int)data[i];
+                    int img = (int)data[i + 1];
+                    energy += real * real + img * img;
+                }
+            }
+        }
+        return energy;
+    }
+
+    private void getVisualizer(int session) {
+         if (mVisualizer == null || session != mSession) {
+             if (session != mSession && mVisualizer != null) {
+                 mVisualizer.release();
+                 mVisualizer = null;
+             }
+             try {
+                mVisualizer = new Visualizer(session);
+                mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getVisualizer() Visualizer not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getVisualizer() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mVisualizer", mVisualizer);
+    }
+
+    private void releaseVisualizer() {
+        if (mVisualizer != null) {
+            mVisualizer.release();
+            mVisualizer = null;
+        }
+   }
+
+    private void createListenerLooper() {
+
+        new Thread() {
+            @Override
+            public void run() {
+                // Set up a looper to be used by mEffect.
+                Looper.prepare();
+
+                // Save the looper so that we can terminate this thread
+                // after we are done with it.
+                mLooper = Looper.myLooper();
+
+                if (mVisualizer != null) {
+                    mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
+                        public void onWaveFormDataCapture(
+                                Visualizer visualizer, byte[] waveform, int samplingRate) {
+                            synchronized(mLock) {
+                                if (visualizer == mVisualizer) {
+                                    if (mCaptureWaveform) {
+                                        mWaveform = waveform;
+                                        mLock.notify();
+                                    }
+                                }
+                            }
+                        }
+
+                        public void onFftDataCapture(
+                                Visualizer visualizer, byte[] fft, int samplingRate) {
+                            synchronized(mLock) {
+                                if (visualizer == mVisualizer) {
+                                    if (mCaptureFft) {
+                                        mFft = fft;
+                                        mLock.notify();
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    10000,
+                    true,
+                    true);
+                }
+
+                synchronized(mLock) {
+                    mInitialized = true;
+                    mLock.notify();
+                }
+                Looper.loop();  // Blocks forever until Looper.quit() is called.
+            }
+        }.start();
+    }
+    /*
+     * Terminates the listener looper thread.
+     */
+    private void terminateListenerLooper() {
+        if (mLooper != null) {
+            mLooper.quit();
+            mLooper = null;
+        }
+    }
+}
diff --git a/apps/CtsVerifier/tests/Android.mk b/tests/tests/ndef/Android.mk
similarity index 77%
copy from apps/CtsVerifier/tests/Android.mk
copy to tests/tests/ndef/Android.mk
index b9572fb..4cb0b78 100644
--- a/apps/CtsVerifier/tests/Android.mk
+++ b/tests/tests/ndef/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
+# 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.
@@ -12,23 +11,24 @@
 # 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_PACKAGE_NAME := CtsNdefTestCases
+
+# 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)
 
+# All tests should include android.test.runner.
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CtsVerifierTests
-
-LOCAL_INSTRUMENTATION_FOR := CtsVerifier
-
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_PACKAGE)
diff --git a/apps/CtsVerifier/tests/AndroidManifest.xml b/tests/tests/ndef/AndroidManifest.xml
similarity index 67%
rename from apps/CtsVerifier/tests/AndroidManifest.xml
rename to tests/tests/ndef/AndroidManifest.xml
index 7b642d9..4a65818 100644
--- a/apps/CtsVerifier/tests/AndroidManifest.xml
+++ b/tests/tests/ndef/AndroidManifest.xml
@@ -1,13 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
-
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- 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.
@@ -16,14 +15,16 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-      package="com.android.cts.verifier.test"
-      android:versionCode="1"
-      android:versionName="1.0">
-    
+    package="com.android.cts.ndef">
+
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:targetPackage="com.android.cts.verifier" android:name="android.test.InstrumentationTestRunner" />
-    
-</manifest> 
\ No newline at end of file
+    <!-- This is a self-instrumenting test package. -->
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="com.android.cts.ndef"
+                     android:label="CTS tests of NDEF data classes"/>
+
+</manifest>
+
diff --git a/tests/tests/ndef/src/android/ndef/cts/BasicNdefTest.java b/tests/tests/ndef/src/android/ndef/cts/BasicNdefTest.java
new file mode 100644
index 0000000..6e2ac3c
--- /dev/null
+++ b/tests/tests/ndef/src/android/ndef/cts/BasicNdefTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.ndef.cts;
+
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.FormatException;
+
+import junit.framework.TestCase;
+
+public class BasicNdefTest extends TestCase {
+    /**
+     * A Smart Poster containing a URL and no text.
+     */
+    public static final byte[] SMART_POSTER_URL_NO_TEXT = new byte[] {
+            (byte) 0xd1, (byte) 0x02, (byte) 0x0f, (byte) 0x53, (byte) 0x70, (byte) 0xd1,
+            (byte) 0x01, (byte) 0x0b, (byte) 0x55, (byte) 0x01, (byte) 0x67, (byte) 0x6f,
+            (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, (byte) 0x2e, (byte) 0x63,
+            (byte) 0x6f, (byte) 0x6d
+    };
+
+    public void test_parseSmartPoster() throws FormatException {
+        NdefMessage msg = new NdefMessage(SMART_POSTER_URL_NO_TEXT);
+        NdefRecord[] records = msg.getRecords();
+
+        assertEquals(1, records.length);
+
+        assertEquals(0, records[0].getId().length);
+
+        assertEquals(NdefRecord.TNF_WELL_KNOWN, records[0].getTnf());
+
+        assertByteArrayEquals(NdefRecord.RTD_SMART_POSTER, records[0].getType());
+
+        assertByteArrayEquals(new byte[] {
+                (byte) 0xd1, (byte) 0x01, (byte) 0x0b, (byte) 0x55, (byte) 0x01,
+                (byte) 0x67, (byte) 0x6f, (byte) 0x6f, (byte) 0x67, (byte) 0x6c,
+                (byte) 0x65, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d},
+                records[0].getPayload());
+    }
+
+    private static void assertByteArrayEquals(byte[] b1, byte[] b2) {
+        assertEquals(b1.length, b2.length);
+        for (int i = 0; i < b1.length; i++) {
+            assertEquals(b1[i], b2[i]);
+        }
+    }
+}
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index 3b85e9f..96a935b 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -51,8 +51,9 @@
     private ConnectivityManager mCm;
     private WifiManager mWifiManager;
     private PackageManager mPackageManager;
-    // must include both mobile data + wifi
-    private static final int MIN_NUM_NETWORK_TYPES = 2;
+
+    // device could have only one interface: data, wifi.
+    private static final int MIN_NUM_NETWORK_TYPES = 1;
 
     @Override
     protected void setUp() throws Exception {
@@ -68,28 +69,27 @@
         args = {int.class}
     )
     public void testGetNetworkInfo() {
-
-        // this test assumes that there are at least two network types.
         assertTrue(mCm.getAllNetworkInfo().length >= MIN_NUM_NETWORK_TYPES);
-        NetworkInfo ni = mCm.getNetworkInfo(1);
-        State state = ni.getState();
-        assertTrue(State.UNKNOWN.ordinal() >= state.ordinal()
-                && state.ordinal() >= State.CONNECTING.ordinal());
-        DetailedState ds = ni.getDetailedState();
-        assertTrue(DetailedState.FAILED.ordinal() >= ds.ordinal()
-                && ds.ordinal() >= DetailedState.IDLE.ordinal());
-
-        ni = mCm.getNetworkInfo(0);
-        state = ni.getState();
-        assertTrue(State.UNKNOWN.ordinal() >= state.ordinal()
-                && state.ordinal() >= State.CONNECTING.ordinal());
-        ds = ni.getDetailedState();
-        assertTrue(DetailedState.FAILED.ordinal() >= ds.ordinal()
-                && ds.ordinal() >= DetailedState.IDLE.ordinal());
-
+        NetworkInfo ni = mCm.getNetworkInfo(TYPE_WIFI);
+        if (ni != null) {
+            State state = ni.getState();
+            assertTrue(State.UNKNOWN.ordinal() >= state.ordinal()
+                       && state.ordinal() >= State.CONNECTING.ordinal());
+            DetailedState ds = ni.getDetailedState();
+            assertTrue(DetailedState.FAILED.ordinal() >= ds.ordinal()
+                       && ds.ordinal() >= DetailedState.IDLE.ordinal());
+        }
+        ni = mCm.getNetworkInfo(TYPE_MOBILE);
+        if (ni != null) {
+            State state = ni.getState();
+            assertTrue(State.UNKNOWN.ordinal() >= state.ordinal()
+                    && state.ordinal() >= State.CONNECTING.ordinal());
+            DetailedState ds = ni.getDetailedState();
+            assertTrue(DetailedState.FAILED.ordinal() >= ds.ordinal()
+                    && ds.ordinal() >= DetailedState.IDLE.ordinal());
+        }
         ni = mCm.getNetworkInfo(-1);
         assertNull(ni);
-
     }
 
     @TestTargets({
@@ -114,67 +114,6 @@
         assertFalse(ConnectivityManager.isNetworkTypeValid(-1));
     }
 
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getNetworkPreference",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.SUFFICIENT,
-            method = "setNetworkPreference",
-            args = {int.class}
-        )
-    })
-    public void testAccessNetworkPreference() {
-        int initialSetting = mCm.getNetworkPreference();
-
-        // Changing the network preference requires android.permission.WRITE_SECURE_SETTINGS,
-        // which is only available to signed or system applications.
-
-        // Setting the same preference that is already set is a no-op and does not throw
-        // a SecurityException.
-        mCm.setNetworkPreference(initialSetting);
-        assertEquals(initialSetting, mCm.getNetworkPreference());
-
-        // find a valid setting that is different from the initial setting
-        int validSetting = -1;
-        NetworkInfo[] ni = mCm.getAllNetworkInfo();
-        for (NetworkInfo n : ni) {
-            int type = n.getType();
-            if (type != initialSetting) {
-                validSetting = type;
-                break;
-            }
-        }
-        if (validSetting >= 0) {
-            try {
-                mCm.setNetworkPreference(validSetting);
-                fail("Trying to change the network preference should throw SecurityException");
-            } catch (SecurityException expected) {
-                // expected
-            }
-        }
-
-        // find an invalid setting
-        int invalidSetting = -1;
-        for (int i = 0; i < 10; i++) {
-            if (!ConnectivityManager.isNetworkTypeValid(i)) {
-                invalidSetting = i;
-                break;
-            }
-        }
-        if (invalidSetting >= 0) {
-            // illegal setting should be ignored
-            mCm.setNetworkPreference(invalidSetting);
-            assertEquals(initialSetting, mCm.getNetworkPreference());
-        }
-
-        // illegal setting should be ignored
-        mCm.setNetworkPreference(-1);
-        assertEquals(initialSetting, mCm.getNetworkPreference());
-    }
-
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         notes = "Test getAllNetworkInfo().",
@@ -203,9 +142,21 @@
         final String invalidateFeature = "invalidateFeature";
         final String mmsFeature = "enableMMS";
         final int failureCode = -1;
+        final int wifiOnlyStartFailureCode = 3;
+        final int wifiOnlyStopFailureCode = 1;
 
-        assertEquals(failureCode, mCm.startUsingNetworkFeature(TYPE_MOBILE, invalidateFeature));
-        assertEquals(failureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE, invalidateFeature));
+        NetworkInfo ni = mCm.getNetworkInfo(TYPE_MOBILE);
+        if (ni != null) {
+            assertEquals(failureCode, mCm.startUsingNetworkFeature(TYPE_MOBILE,
+                    invalidateFeature));
+            assertEquals(failureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE,
+                    invalidateFeature));
+        } else {
+            assertEquals(wifiOnlyStartFailureCode, mCm.startUsingNetworkFeature(TYPE_MOBILE,
+                    invalidateFeature));
+            assertEquals(wifiOnlyStopFailureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE,
+                    invalidateFeature));
+        }
 
         // Should return failure(-1) because MMS is not supported on WIFI.
         assertEquals(failureCode, mCm.startUsingNetworkFeature(TYPE_WIFI, mmsFeature));
diff --git a/tests/tests/net/src/android/net/cts/ListeningPortsTest.java b/tests/tests/net/src/android/net/cts/ListeningPortsTest.java
index ff6b4e9..b6e6efb 100644
--- a/tests/tests/net/src/android/net/cts/ListeningPortsTest.java
+++ b/tests/tests/net/src/android/net/cts/ListeningPortsTest.java
@@ -23,6 +23,7 @@
 import java.util.Scanner;
 import java.util.regex.Pattern;
 
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 public class ListeningPortsTest extends TestCase {
@@ -50,12 +51,40 @@
         EXCEPTION_PATTERNS.add("[0]{16}[F]{4}[0]{4}[0-9A-F]{6}7F:[0-9A-F]{4}"); // IPv4-6 Conversion
     }
 
-    public static void testNoListeningPorts() {
-        final boolean isTcp = true;
-        assertNoListeningPorts("/proc/net/tcp", isTcp);
-        assertNoListeningPorts("/proc/net/tcp6", isTcp);
-        assertNoListeningPorts("/proc/net/udp", !isTcp);
-        assertNoListeningPorts("/proc/net/udp6", !isTcp);
+    public void testNoListeningTcpPorts() {
+        assertNoListeningPorts("/proc/net/tcp", true);
+    }
+
+    public void testNoListeningTcp6Ports() {
+        assertNoListeningPorts("/proc/net/tcp6", true);
+    }
+
+    public void testNoListeningUdpPorts() throws Exception {
+        assertNoListeningUdpPorts("/proc/net/udp");
+    }
+
+    public void testNoListeningUdp6Ports() throws Exception {
+        assertNoListeningUdpPorts("/proc/net/udp6");
+    }
+
+    private static final int RETRIES_MAX = 6;
+
+    /**
+     * UDP tests can be flaky due to DNS lookups.  Compensate.
+     */
+    private static void assertNoListeningUdpPorts(String procFilePath) throws Exception {
+        for (int i = 0; i < RETRIES_MAX; i++) {
+            try {
+                assertNoListeningPorts(procFilePath, false);
+                return;
+            } catch (ListeningPortsAssertionError e) {
+                if (i == RETRIES_MAX - 1) {
+                    throw e;
+                }
+                Thread.sleep(2 * 1000 * i);
+            }
+        }
+        throw new IllegalStateException("unreachable");
     }
 
     private static void assertNoListeningPorts(String procFilePath, boolean isTcp) {
@@ -94,7 +123,8 @@
                         isAddress(localAddress));
 
                 if (!isException(localAddress) && isPortListening(state, isTcp)) {
-                    fail("Found port listening on " + localAddress + " in " + procFilePath);
+                    throw new ListeningPortsAssertionError(
+                            "Found port listening on " + localAddress + " in " + procFilePath);
                 }
             }
         } catch (FileNotFoundException notFound) {
@@ -128,4 +158,10 @@
         String listeningState = isTcp ? "0A" : "07";
         return listeningState.equals(state);
     }
+
+    private static class ListeningPortsAssertionError extends AssertionFailedError {
+        private ListeningPortsAssertionError(String msg) {
+            super(msg);
+        }
+    }
 }
diff --git a/tests/tests/net/src/android/net/cts/NetworkInfoTest.java b/tests/tests/net/src/android/net/cts/NetworkInfoTest.java
index 99e8e15..6800c43 100644
--- a/tests/tests/net/src/android/net/cts/NetworkInfoTest.java
+++ b/tests/tests/net/src/android/net/cts/NetworkInfoTest.java
@@ -16,16 +16,14 @@
 
 package android.net.cts;
 
+import dalvik.annotation.TestTargetClass;
+
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
 import android.test.AndroidTestCase;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestTargets;
 
 @TestTargetClass(NetworkInfo.class)
 public class NetworkInfoTest extends AndroidTestCase {
@@ -35,135 +33,41 @@
     public static final String MOBILE_TYPE_NAME = "mobile";
     public static final String WIFI_TYPE_NAME = "WIFI";
 
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "isConnectedOrConnecting",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "setFailover",
-            args = {boolean.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "isFailover",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "isRoaming",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getType",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getSubtype",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getTypeName",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getSubtypeName",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "setIsAvailable",
-            args = {boolean.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "isAvailable",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "isConnected",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getDetailedState",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getState",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getReason",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getExtraInfo",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "toString",
-            args = {}
-        )
-    })
     public void testAccessNetworkInfoProperties() {
         ConnectivityManager cm = (ConnectivityManager) getContext().getSystemService(
                 Context.CONNECTIVITY_SERVICE);
-
         NetworkInfo[] ni = cm.getAllNetworkInfo();
-        assertTrue(ni.length >= 2);
+        assertTrue(ni.length >= 1);
 
-        assertFalse(ni[TYPE_MOBILE].isFailover());
-        assertFalse(ni[TYPE_WIFI].isFailover());
-
-        // test environment:connect as TYPE_MOBILE, and connect to internet.
-        assertEquals(TYPE_MOBILE, ni[TYPE_MOBILE].getType());
-        assertEquals(TYPE_WIFI, ni[TYPE_WIFI].getType());
-
-        // don't know the return value
-        ni[TYPE_MOBILE].getSubtype();
-        ni[TYPE_WIFI].getSubtype();
-
-        assertEquals(MOBILE_TYPE_NAME, ni[TYPE_MOBILE].getTypeName());
-        assertEquals(WIFI_TYPE_NAME, ni[TYPE_WIFI].getTypeName());
-
-        // don't know the return value
-        ni[TYPE_MOBILE].getSubtypeName();
-        ni[TYPE_WIFI].getSubtypeName();
-
-        if(ni[TYPE_MOBILE].isConnectedOrConnecting()) {
-            assertTrue(ni[TYPE_MOBILE].isAvailable());
-            assertTrue(ni[TYPE_MOBILE].isConnected());
-            assertEquals(State.CONNECTED, ni[TYPE_MOBILE].getState());
-            assertEquals(DetailedState.CONNECTED, ni[TYPE_MOBILE].getDetailedState());
-            ni[TYPE_MOBILE].getReason();
-            ni[TYPE_MOBILE].getExtraInfo();
+        for (NetworkInfo netInfo: ni) {
+            switch (netInfo.getType()) {
+                case TYPE_MOBILE:
+                    assertNetworkInfo(netInfo, MOBILE_TYPE_NAME);
+                    break;
+                case TYPE_WIFI:
+                    assertNetworkInfo(netInfo, WIFI_TYPE_NAME);
+                    break;
+                 // TODO: Add BLUETOOTH_TETHER testing
+                 default:
+                     break;
+            }
         }
+    }
 
-        if(ni[TYPE_WIFI].isConnectedOrConnecting()) {
-            assertTrue(ni[TYPE_WIFI].isAvailable());
-            assertTrue(ni[TYPE_WIFI].isConnected());
-            assertEquals(State.CONNECTED, ni[TYPE_WIFI].getState());
-            assertEquals(DetailedState.CONNECTED, ni[TYPE_WIFI].getDetailedState());
-            ni[TYPE_WIFI].getReason();
-            ni[TYPE_WIFI].getExtraInfo();
+    private void assertNetworkInfo(NetworkInfo netInfo, String expectedTypeName) {
+        assertEquals(expectedTypeName, netInfo.getTypeName());
+        if(netInfo.isConnectedOrConnecting()) {
+            assertTrue(netInfo.isAvailable());
+            if (State.CONNECTED == netInfo.getState()) {
+                assertTrue(netInfo.isConnected());
+            }
+            assertTrue(State.CONNECTING == netInfo.getState()
+                    || State.CONNECTED == netInfo.getState());
+            assertTrue(DetailedState.SCANNING == netInfo.getDetailedState()
+                    || DetailedState.CONNECTING == netInfo.getDetailedState()
+                    || DetailedState.AUTHENTICATING == netInfo.getDetailedState()
+                    || DetailedState.CONNECTED == netInfo.getDetailedState());
         }
-
-        assertFalse(ni[TYPE_MOBILE].isRoaming());
-        assertFalse(ni[TYPE_WIFI].isRoaming());
-
-        assertNotNull(ni[TYPE_MOBILE].toString());
-        assertNotNull(ni[TYPE_WIFI].toString());
+        assertNotNull(netInfo.toString());
     }
 }
diff --git a/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java b/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
index 6cd5d6f..f125550 100644
--- a/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
+++ b/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
@@ -21,6 +21,7 @@
 import java.net.Socket;
 
 import javax.net.SocketFactory;
+import javax.net.ssl.SSLPeerUnverifiedException;
 
 import android.net.SSLCertificateSocketFactory;
 import android.test.AndroidTestCase;
@@ -101,11 +102,10 @@
             args = {int.class}
         )
     })
-    @BrokenTest("flaky")
     public void testCreateSocket() throws Exception {
         new SSLCertificateSocketFactory(100);
         int port = 443;
-        String host = "www.fortify.net";
+        String host = "www.google.com";
         InetAddress inetAddress = null;
         inetAddress = InetAddress.getLocalHost();
         try {
@@ -141,4 +141,73 @@
         // The socket level is invalid.
     }
 
+    // a host and port that are expected to be available but have
+    // a cert with a different CN, in this case CN=mtalk.google.com
+    private static String TEST_CREATE_SOCKET_HOST = "mobile-gtalk.l.google.com";
+    private static int TEST_CREATE_SOCKET_PORT = 5228;
+
+    /**
+     * b/2807618 Make sure that hostname verifcation in cases were it
+     * is documented to be included by various
+     * SSLCertificateSocketFactory.createSocket messages.
+     *
+     * NOTE: Test will fail if external server is not available.
+     */
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "createSocket",
+        args = {String.class, int.class}
+    )
+    public void test_createSocket_simple() throws Exception {
+        try {
+            mFactory.createSocket(TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT);
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // expected
+        }
+    }
+
+    /**
+     * b/2807618 Make sure that hostname verifcation in cases were it
+     * is documented to be included by various
+     * SSLCertificateSocketFactory.createSocket messages.
+     *
+     * NOTE: Test will fail if external server is not available.
+     */
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "createSocket",
+        args = {Socket.class, String.class, int.class, boolean.class}
+    )
+    public void test_createSocket_wrapping() throws Exception {
+        try {
+            Socket underlying = new Socket(TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT);
+            mFactory.createSocket(
+                    underlying, TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT, true);
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // expected
+        }
+    }
+
+    /**
+     * b/2807618 Make sure that hostname verifcation in cases were it
+     * is documented to be included by various
+     * SSLCertificateSocketFactory.createSocket messages.
+     *
+     * NOTE: Test will fail if external server is not available.
+     */
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "createSocket",
+        args = {String.class, int.class, InetAddress.class, int.class}
+    )
+    public void test_createSocket_bind() throws Exception {
+        try {
+            mFactory.createSocket(TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT, null, 0);
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // expected
+        }
+    }
 }
diff --git a/tests/tests/net/src/android/net/cts/TrafficStatsTest.java b/tests/tests/net/src/android/net/cts/TrafficStatsTest.java
index 9d23a87..183f891 100644
--- a/tests/tests/net/src/android/net/cts/TrafficStatsTest.java
+++ b/tests/tests/net/src/android/net/cts/TrafficStatsTest.java
@@ -16,24 +16,20 @@
 
 package android.net.cts;
 
-import android.os.Process;
-import android.net.TrafficStats;
-import android.test.AndroidTestCase;
-
-import dalvik.annotation.TestTargets;
 import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetNew;
 import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+import android.net.TrafficStats;
+import android.os.Process;
+import android.test.AndroidTestCase;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.net.UnknownHostException;
-import java.util.Random;
 
 @TestTargetClass(TrafficStats.class)
 public class TrafficStatsTest extends AndroidTestCase {
@@ -58,63 +54,6 @@
     }
 
     @TestTargets({
-        @TestTargetNew(level = TestLevel.PARTIAL_COMPLETE, method = "getTotalTxPackets"),
-        @TestTargetNew(level = TestLevel.PARTIAL_COMPLETE, method = "getTotalRxPackets"),
-        @TestTargetNew(level = TestLevel.PARTIAL_COMPLETE, method = "getTotalTxBytes"),
-        @TestTargetNew(level = TestLevel.PARTIAL_COMPLETE, method = "getTotalRxBytes"),
-        @TestTargetNew(level = TestLevel.PARTIAL_COMPLETE, method = "getUidTxBytes"),
-        @TestTargetNew(level = TestLevel.PARTIAL_COMPLETE, method = "getUidRxBytes")
-    })
-    public void testTrafficStatsWithHostLookup() {
-        long txPacketsBefore = TrafficStats.getTotalTxPackets();
-        long rxPacketsBefore = TrafficStats.getTotalRxPackets();
-        long txBytesBefore = TrafficStats.getTotalTxBytes();
-        long rxBytesBefore = TrafficStats.getTotalRxBytes();
-        long uidTxBytesBefore = TrafficStats.getUidTxBytes(Process.myUid());
-        long uidRxBytesBefore = TrafficStats.getUidRxBytes(Process.myUid());
-
-        // Look up random hostnames in a wildcard domain owned by Google.
-        // This will require a DNS request, which should generate traffic.
-
-        int found = 0;
-        Random r = new Random();
-        for (int i = 0; i < 10; i++) {
-            try {
-                String host = "test" + r.nextInt(100000) + ".clients.google.com";
-                InetAddress[] addr = InetAddress.getAllByName(host);
-                if (addr.length > 0) found++;
-            } catch (UnknownHostException e) {
-                // Ignore -- our purpose is not to test network connectivity,
-                // and we'd rather have false positives than a flaky test.
-            }
-        }
-
-        long txPacketsAfter = TrafficStats.getTotalTxPackets();
-        long rxPacketsAfter = TrafficStats.getTotalRxPackets();
-        long txBytesAfter = TrafficStats.getTotalTxBytes();
-        long rxBytesAfter = TrafficStats.getTotalRxBytes();
-        long uidTxBytesAfter = TrafficStats.getUidTxBytes(Process.myUid());
-        long uidRxBytesAfter = TrafficStats.getUidRxBytes(Process.myUid());
-
-        // Make some conservative assertions about the data used:
-        // each successful resolution should exchange at least one packet,
-        // and at least 20 bytes in each direction.
-
-        assertTrue("txp: " + txPacketsBefore + " [" + found + "] " + txPacketsAfter,
-                   txPacketsAfter >= txPacketsBefore + found);
-        assertTrue("rxp: " + rxPacketsBefore + " [" + found + "] " + rxPacketsAfter,
-                   rxPacketsAfter >= rxPacketsBefore + found);
-        assertTrue("txb: " + txBytesBefore + " [" + found + "] " + txBytesAfter,
-                   txBytesAfter >= txBytesBefore + found * 20);
-        assertTrue("rxb: " + rxBytesBefore + " [" + found + "] " + rxBytesAfter,
-                   rxBytesAfter >= rxBytesBefore + found * 20);
-        assertTrue("uidtxb: " + uidTxBytesBefore + " [" + found + "] " + uidTxBytesAfter,
-                   uidTxBytesAfter >= uidTxBytesBefore + found * 20);
-        assertTrue("uidrxb: " + uidRxBytesBefore + " [" + found + "] " + uidRxBytesAfter,
-                   uidRxBytesAfter >= uidRxBytesBefore + found * 20);
-    }
-
-    @TestTargets({
         @TestTargetNew(level = TestLevel.PARTIAL_COMPLETE, method = "getMobileTxPackets"),
         @TestTargetNew(level = TestLevel.PARTIAL_COMPLETE, method = "getMobileRxPackets"),
         @TestTargetNew(level = TestLevel.PARTIAL_COMPLETE, method = "getMobileTxBytes"),
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/net/src/android/net/wifi/cts/WifiInfoTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java
index d6d7d8e..44189cd 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java
@@ -54,7 +54,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
-            if (action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
+            if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
                 synchronized (mMySync) {
                     mMySync.expectedState = STATE_WIFI_CHANGED;
                     mMySync.notify();
@@ -68,14 +68,7 @@
         super.setUp();
         mMySync = new MySync();
         mIntentFilter = new IntentFilter();
-        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
-        mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
-        mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
         mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
-        mIntentFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
-        mIntentFilter.addAction(WifiManager.ACTION_PICK_WIFI_NETWORK);
 
         mContext.registerReceiver(mReceiver, mIntentFilter);
         mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
@@ -184,6 +177,8 @@
         wifiInfo.getMacAddress();
         setWifiEnabled(false);
         Thread.sleep(DURATION);
+        wifiInfo = mWifiManager.getConnectionInfo();
+        assertEquals(-1, wifiInfo.getNetworkId());
         assertEquals(WifiManager.WIFI_STATE_DISABLED, mWifiManager.getWifiState());
     }
 
diff --git a/tests/tests/os/src/android/os/cts/BuildTest.java b/tests/tests/os/src/android/os/cts/BuildTest.java
index 62abc5a..ec3f49a 100644
--- a/tests/tests/os/src/android/os/cts/BuildTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildTest.java
@@ -106,11 +106,56 @@
         }
     }
 
+    private static final Pattern BOARD_PATTERN =
+        Pattern.compile("^([0-9A-Za-z._-]+)$");
+    private static final Pattern BRAND_PATTERN =
+        Pattern.compile("^([0-9A-Za-z._-]+)$");
     private static final Pattern DEVICE_PATTERN =
-        Pattern.compile("^([0-9A-Za-z_-]+)$");
+        Pattern.compile("^([0-9A-Za-z._-]+)$");
+    private static final Pattern ID_PATTERN =
+        Pattern.compile("^([0-9A-Za-z._-]+)$");
+    private static final Pattern PRODUCT_PATTERN =
+        Pattern.compile("^([0-9A-Za-z._-]+)$");
+    private static final Pattern SERIAL_NUMBER_PATTERN =
+        Pattern.compile("^([0-9A-Za-z]{0,20})$");
+    private static final Pattern TAGS_PATTERN =
+        Pattern.compile("^([0-9A-Za-z.,_-]+)$");
+    private static final Pattern TYPE_PATTERN =
+        Pattern.compile("^([0-9A-Za-z._-]+)$");
 
     /** Tests that check for valid values of constants in Build. */
     public void testBuildConstants() {
+        // Build.VERSION.* constants tested by BuildVersionTest
+
+        assertTrue(BOARD_PATTERN.matcher(Build.BOARD).matches());
+
+        assertTrue(BRAND_PATTERN.matcher(Build.BRAND).matches());
+
         assertTrue(DEVICE_PATTERN.matcher(Build.DEVICE).matches());
+
+        // Build.FINGERPRINT tested by BuildVersionTest
+
+        assertNotEmpty(Build.HOST);
+
+        assertTrue(ID_PATTERN.matcher(Build.ID).matches());
+
+        assertNotEmpty(Build.MODEL);
+
+        assertTrue(PRODUCT_PATTERN.matcher(Build.PRODUCT).matches());
+
+        assertTrue(SERIAL_NUMBER_PATTERN.matcher(Build.SERIAL).matches());
+
+        assertTrue(TAGS_PATTERN.matcher(Build.TAGS).matches());
+
+        // No format requirements stated in CDD for Build.TIME
+
+        assertTrue(TYPE_PATTERN.matcher(Build.TYPE).matches());
+
+        assertNotEmpty(Build.USER);
+    }
+
+    private void assertNotEmpty(String value) {
+        assertNotNull(value);
+        assertFalse(value.isEmpty());
     }
 }
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index bbea5b7..74a2d8e 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -32,8 +32,8 @@
 
     private static final String LOG_TAG = "BuildVersionTest";
     private static final Set<String> EXPECTED_RELEASES =
-        new HashSet<String>(Arrays.asList("2.2", "2.2.1", "2.2.2"));
-    private static final int EXPECTED_SDK = 8;
+        new HashSet<String>(Arrays.asList("2.3.3", "2.3.4", "2.3.5"));
+    private static final int EXPECTED_SDK = 10;
 
     public void testReleaseVersion() {
         // Applications may rely on the exact release version
@@ -45,11 +45,15 @@
         assertEquals(EXPECTED_SDK, Build.VERSION.SDK_INT);
     }
 
+    public void testIncremental() {
+        assertNotEmpty(Build.VERSION.INCREMENTAL);
+    }
+
     /**
      * Verifies {@link Build.FINGERPRINT} follows expected format:
      * <p/>
      * <code>
-     * (BRAND)/(PRODUCT)/(DEVICE)/(BOARD):(VERSION.RELEASE)/(BUILD_ID)/
+     * (BRAND)/(PRODUCT)/(DEVICE):(VERSION.RELEASE)/(BUILD_ID)/
      * (BUILD_NUMBER):(BUILD_VARIANT)/(TAGS)
      * </code>
      */
@@ -60,18 +64,24 @@
         assertEquals("Build fingerprint must not include whitespace", -1,
                 fingerprint.indexOf(' '));
         final String[] fingerprintSegs = fingerprint.split("/");
-        assertEquals("Build fingerprint does not match expected format", 7, fingerprintSegs.length);
+        assertEquals("Build fingerprint does not match expected format", 6, fingerprintSegs.length);
         assertEquals(Build.BRAND, fingerprintSegs[0]);
         assertEquals(Build.PRODUCT, fingerprintSegs[1]);
-        assertEquals(Build.DEVICE, fingerprintSegs[2]);
-        // parse BOARD:VERSION_RELEASE
-        String[] bootloaderPlat = fingerprintSegs[3].split(":");
-        assertEquals(Build.BOARD, bootloaderPlat[0]);
-        assertEquals(Build.VERSION.RELEASE, bootloaderPlat[1]);
-        assertEquals(Build.ID, fingerprintSegs[4]);
+
+        String[] devicePlatform = fingerprintSegs[2].split(":");
+        assertEquals(2, devicePlatform.length);
+        assertEquals(Build.DEVICE, devicePlatform[0]);
+        assertEquals(Build.VERSION.RELEASE, devicePlatform[1]);
+
+        assertEquals(Build.ID, fingerprintSegs[3]);
         // no requirements for BUILD_NUMBER and BUILD_VARIANT
-        assertTrue(fingerprintSegs[5].contains(":"));
+        assertTrue(fingerprintSegs[4].contains(":"));
         // no strict requirement for TAGS
-        //assertEquals(Build.TAGS, fingerprintSegs[6]);
+        //assertEquals(Build.TAGS, fingerprintSegs[5]);
+    }
+
+    private void assertNotEmpty(String value) {
+        assertNotNull(value);
+        assertFalse(value.isEmpty());
     }
 }
diff --git a/tests/tests/os/src/android/os/cts/FileAccessPermissionTest.java b/tests/tests/os/src/android/os/cts/FileAccessPermissionTest.java
index c5418bc..cd89e3d 100755
--- a/tests/tests/os/src/android/os/cts/FileAccessPermissionTest.java
+++ b/tests/tests/os/src/android/os/cts/FileAccessPermissionTest.java
@@ -41,6 +41,8 @@
  *
  * mksdcard <size> <file>
  * emulator -sdcard <filepath>
+ *
+ * TODO: Combine this file with {@link android.permission.cts.FileSystemPermissionTest}
  */
 public class FileAccessPermissionTest extends AndroidTestCase {
 
diff --git a/tests/tests/os/src/android/os/cts/MessageQueueTest.java b/tests/tests/os/src/android/os/cts/MessageQueueTest.java
index 95441ae..c7b549a 100644
--- a/tests/tests/os/src/android/os/cts/MessageQueueTest.java
+++ b/tests/tests/os/src/android/os/cts/MessageQueueTest.java
@@ -16,10 +16,7 @@
 
 package android.os.cts;
 
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargetNew;
 
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -30,77 +27,92 @@
 import android.os.MessageQueue.IdleHandler;
 import android.test.AndroidTestCase;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @TestTargetClass(MessageQueue.class)
 public class MessageQueueTest extends AndroidTestCase {
 
-    private boolean mResult;
-    // Action flag: true means addIdleHanlder, false means removeIdleHanlder
-    private boolean mActionFlag;
     private static final long TIMEOUT = 1000;
-    private static final long INTERVAL = 50;
-    private IdleHandler mIdleHandler = new IdleHandler() {
-        public boolean queueIdle() {
-            MessageQueueTest.this.mResult = true;
-            return true;
-        }
-    };
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mResult = false;
-    }
+    public void testAddIdleHandler() throws InterruptedException {
+        TestLooperThread looperThread = new TestLooperThread(Test.ADD_IDLE_HANDLER);
+        looperThread.start();
 
-    /**
-     * After calling addIdleHandler (called by MessageQueueTestHelper#doTest), the size of
-     * idleHanlder list is not 0 (before calling addIdleHandler, there is no idleHanlder in
-     * the test looper we started, that means no idleHanlder with flag mResult), and in doTest,
-     * we start a looper, which will queueIdle (Looper.loop()) if idleHanlder list has element,
-     * then mResult will be set true. It can make sure addIdleHandler works. If no idleHanlder
-     * with flag mResult, mResult will be false.
-     */
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "addIdleHandler",
-        args = {android.os.MessageQueue.IdleHandler.class}
-    )
-    @BrokenTest("needs investigation")
-    public void testAddIdleHandler() throws RuntimeException, InterruptedException {
         try {
-            Looper.myQueue().addIdleHandler(null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            if (!looperThread.hasIdleHandlerBeenCalled()) {
+                fail("IdleHandler#queueIdle was NOT called: " + looperThread.getTestProgress());
+            }
+        } finally {
+            assertTrue("The looper should have been running.", looperThread.quit());
         }
-        // If mActionFlag is true, doTest will call addIdleHandler
-        mActionFlag = true;
-        mResult = false;
-        MessageQueueTestHelper tester = new MessageQueueTestHelper();
-        tester.doTest(TIMEOUT, INTERVAL);
-
-        tester.quit();
-        assertTrue(mResult);
     }
 
-    /**
-     * In this test method, at the beginning of the LooperThread, we call addIdleHandler then
-     * removeIdleHandler, there should be no element in idleHanlder list. So the Looper.loop()
-     * will not call queueIdle(), mResult will not be set true.
-     */
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "removeIdleHandler",
-        args = {android.os.MessageQueue.IdleHandler.class}
-    )
-    @BrokenTest("needs investigation")
-    public void testRemoveIdleHandler() throws RuntimeException, InterruptedException {
-        mActionFlag = false;
-        mResult = false;
-        MessageQueueTestHelper tester = new MessageQueueTestHelper();
-        tester.doTest(TIMEOUT, INTERVAL);
+    public void testRemoveIdleHandler() throws InterruptedException {
+        TestLooperThread looperThread = new TestLooperThread(Test.REMOVE_IDLE_HANDLER);
+        looperThread.start();
 
-        tester.quit();
-        assertFalse(mResult);
+        try {
+            if (looperThread.hasIdleHandlerBeenCalled()) {
+                fail("IdleHandler#queueIdle was called: " + looperThread.getTestProgress());
+            }
+        } finally {
+            assertTrue("The looper should have been running.", looperThread.quit());
+        }
+    }
+
+    private enum Test {ADD_IDLE_HANDLER, REMOVE_IDLE_HANDLER};
+
+    /**
+     * {@link HandlerThread} that adds or removes an idle handler depending on the {@link Test}
+     * given. It uses a {@link CountDownLatch} with an initial count of 2. The first count down
+     * occurs right before the looper's run thread had started running. The final count down
+     * occurs when the idle handler was executed. Tests can call {@link #hasIdleHandlerBeenCalled()}
+     * to see if the countdown reached to 0 or not.
+     */
+    private static class TestLooperThread extends HandlerThread {
+
+        private final Test mTestMode;
+
+        private final CountDownLatch mIdleLatch = new CountDownLatch(2);
+
+        TestLooperThread(Test testMode) {
+            super("TestLooperThread");
+            mTestMode = testMode;
+        }
+
+        @Override
+        protected void onLooperPrepared() {
+            super.onLooperPrepared();
+
+            IdleHandler idleHandler = new IdleHandler() {
+                public boolean queueIdle() {
+                    mIdleLatch.countDown();
+                    return false;
+                }
+            };
+
+            if (mTestMode == Test.ADD_IDLE_HANDLER) {
+                Looper.myQueue().addIdleHandler(idleHandler);
+            } else {
+                Looper.myQueue().addIdleHandler(idleHandler);
+                Looper.myQueue().removeIdleHandler(idleHandler);
+            }
+        }
+
+        @Override
+        public void run() {
+            mIdleLatch.countDown();
+            super.run();
+        }
+
+        public boolean hasIdleHandlerBeenCalled() throws InterruptedException {
+            return mIdleLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        }
+
+        public long getTestProgress() {
+            return mIdleLatch.getCount();
+        }
     }
 
     /**
@@ -155,68 +167,6 @@
     }
 
     /**
-     * Helper class used to test addIdleHandler, removeIdleHandler
-     */
-    private class MessageQueueTestHelper {
-
-        private boolean mDone;
-        private Looper mLooper;
-
-        public void doTest(long timeout, long interval) throws InterruptedException {
-            (new LooperThread()).start();
-            synchronized (this) {
-                long now = System.currentTimeMillis();
-                long endTime = now + timeout;
-                // Wait and frequently check if mDone is set.
-                while (!mDone && now < endTime) {
-                    wait(interval);
-                    now = System.currentTimeMillis();
-                }
-            }
-            mLooper.quit();
-            if (!mDone) {
-                throw new RuntimeException("test timed out");
-            }
-        }
-
-        private class LooperThread extends HandlerThread {
-            public LooperThread() {
-                super("MessengeQueueLooperThread");
-            }
-
-            public void onLooperPrepared() {
-                mLooper = getLooper();
-                if (mActionFlag) {
-                    // If mActionFlag is true, just addIdleHandler, and
-                    // Looper.loop() will set mResult true.
-                    Looper.myQueue().addIdleHandler(mIdleHandler);
-                } else {
-                    // If mActionFlag is false, addIdleHandler and remove it, then Looper.loop()
-                    // will not set mResult true because the idleHandler list is empty.
-                    Looper.myQueue().addIdleHandler(mIdleHandler);
-                    Looper.myQueue().removeIdleHandler(mIdleHandler);
-                }
-            }
-
-            @Override
-            public void run() {
-                super.run();
-                synchronized (MessageQueueTestHelper.this) {
-                    mDone = true;
-                    MessageQueueTestHelper.this.notifyAll();
-                }
-            }
-        }
-
-        public void quit() {
-            synchronized (this) {
-                mDone = true;
-                notifyAll();
-            }
-        }
-    }
-
-    /**
      * Helper class used to test sending message to message queue.
      */
     private class OrderTestHelper {
diff --git a/tests/tests/os/src/android/os/cts/NoExecutePermissionTest.java b/tests/tests/os/src/android/os/cts/NoExecutePermissionTest.java
new file mode 100644
index 0000000..6450e58
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/NoExecutePermissionTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.os.cts;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Scanner;
+
+import junit.framework.TestCase;
+
+/**
+ * {@link TestCase} that checks that the NX (No Execute) feature is enabled. This feature makes it
+ * harder to perform attacks against Android by marking certain data blocks as non-executable.
+ */
+public class NoExecutePermissionTest extends TestCase {
+
+    public void testNoExecutePermission() throws FileNotFoundException {
+        if (!cpuHasNxSupport()) {
+            return;
+        }
+
+        String heapPermissions = null;
+        String stackPermissions = null;
+
+        Scanner scanner = null;
+        try {
+            scanner = new Scanner(new File("/proc/self/maps"));
+            while (scanner.hasNextLine()) {
+                String line = scanner.nextLine().trim();
+                String[] fields = line.split("\\s+");
+
+                // Sample line:
+                // 0001d000-00024000 rw-p 00000000 00:00 0          [heap]
+                if (fields != null && fields.length >= 1) {
+                    String permissions = fields[1];
+                    if (fields.length >= 6) {
+                        String tag = fields[5];
+                        if ("[heap]".equals(tag)) {
+                            heapPermissions = permissions;
+                        } else if ("[stack]".equals(tag)) {
+                            stackPermissions = permissions;
+                        }
+                    }
+                }
+            }
+        } finally {
+            if (scanner != null) {
+                scanner.close();
+            }
+        }
+
+        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/os/src/android/os/storage/cts/StorageManagerTest.java b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
new file mode 100644
index 0000000..e23c0b7
--- /dev/null
+++ b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
@@ -0,0 +1,251 @@
+/*
+ * 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.os.storage.cts;
+
+import com.android.cts.stub.R;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.os.cts.FileUtils;
+import android.os.storage.OnObbStateChangeListener;
+import android.os.storage.StorageManager;
+import android.test.AndroidTestCase;
+import android.test.ComparisonFailure;
+import android.util.Log;
+
+import java.io.File;
+import java.io.InputStream;
+
+public class StorageManagerTest extends AndroidTestCase {
+
+    private static final String TAG = StorageManager.class.getSimpleName();
+
+    private static final long MAX_WAIT_TIME = 25*1000;
+    private static final long WAIT_TIME_INCR = 5*1000;
+
+    private static final String OBB_MOUNT_PREFIX = "/mnt/obb/";
+
+    private StorageManager mStorageManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
+    }
+
+    public void testMountAndUnmountObbNormal() {
+        final File outFile = getFilePath("test1.obb");
+
+        final String canonPath = mountObb(R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED);
+
+        mountObb(R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
+
+        final String mountPath = checkMountedPath(canonPath);
+        final File mountDir = new File(mountPath);
+
+        assertTrue("OBB mounted path should be a directory", mountDir.isDirectory());
+
+        unmountObb(outFile, OnObbStateChangeListener.UNMOUNTED);
+    }
+
+    public void testAttemptMountNonObb() {
+        final File outFile = getFilePath("test1_nosig.obb");
+
+        mountObb(R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL);
+
+        assertFalse("OBB should not be mounted",
+                mStorageManager.isObbMounted(outFile.getPath()));
+
+        assertNull("OBB's mounted path should be null",
+                mStorageManager.getMountedObbPath(outFile.getPath()));
+    }
+
+    public void testAttemptMountObbWrongPackage() {
+        final File outFile = getFilePath("test1_wrongpackage.obb");
+
+        mountObb(R.raw.test1_wrongpackage, outFile,
+                OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+
+        assertFalse("OBB should not be mounted",
+                mStorageManager.isObbMounted(outFile.getPath()));
+
+        assertNull("OBB's mounted path should be null",
+                mStorageManager.getMountedObbPath(outFile.getPath()));
+    }
+
+    public void testMountAndUnmountTwoObbs() {
+        final File file1 = getFilePath("test1.obb");
+        final File file2 = getFilePath("test2.obb");
+
+        ObbObserver oo1 = mountObbWithoutWait(R.raw.test1, file1);
+        ObbObserver oo2 = mountObbWithoutWait(R.raw.test1, file2);
+
+        Log.d(TAG, "Waiting for OBB #1 to complete mount");
+        waitForObbActionCompletion(file1, oo1, OnObbStateChangeListener.MOUNTED);
+        Log.d(TAG, "Waiting for OBB #2 to complete mount");
+        waitForObbActionCompletion(file2, oo2, OnObbStateChangeListener.MOUNTED);
+
+        final String mountPath1 = checkMountedPath(oo1.getPath());
+        final File mountDir1 = new File(mountPath1);
+        assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory());
+
+        final String mountPath2 = checkMountedPath(oo2.getPath());
+        final File mountDir2 = new File(mountPath2);
+        assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory());
+
+        unmountObb(file1, OnObbStateChangeListener.UNMOUNTED);
+        unmountObb(file2, OnObbStateChangeListener.UNMOUNTED);
+    }
+
+    private static void assertStartsWith(String message, String prefix, String actual) {
+        if (!actual.startsWith(prefix)) {
+            throw new ComparisonFailure(message, prefix, actual);
+        }
+    }
+
+    private static class ObbObserver extends OnObbStateChangeListener {
+        private String path;
+
+        public int state = -1;
+        boolean done = false;
+
+        @Override
+        public void onObbStateChange(String path, int state) {
+            Log.d(TAG, "Received message.  path=" + path + ", state=" + state);
+            synchronized (this) {
+                this.path = path;
+                this.state = state;
+                done = true;
+                notifyAll();
+            }
+        }
+
+        public String getPath() {
+            assertTrue("Expected ObbObserver to have received a state change.", done);
+            return path;
+        }
+
+        public int getState() {
+            assertTrue("Expected ObbObserver to have received a state change.", done);
+            return state;
+        }
+
+        public boolean isDone() {
+            return done;
+        }
+
+        public boolean waitForCompletion() {
+            long waitTime = 0;
+            synchronized (this) {
+                while (!isDone() && waitTime < MAX_WAIT_TIME) {
+                    try {
+                        wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    } catch (InterruptedException e) {
+                        Log.i(TAG, "Interrupted during sleep", e);
+                    }
+                }
+            }
+
+            return isDone();
+        }
+    }
+
+    private File getFilePath(String name) {
+        final File filesDir = mContext.getFilesDir();
+        final File outFile = new File(filesDir, name);
+        return outFile;
+    }
+
+    private void copyRawToFile(int rawResId, File outFile) {
+        Resources res = mContext.getResources();
+        InputStream is = null;
+        try {
+            is = res.openRawResource(rawResId);
+        } catch (NotFoundException e) {
+            fail("Failed to load resource with id: " + rawResId);
+        }
+        FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                | FileUtils.S_IRWXO);
+        assertTrue(FileUtils.copyToFile(is, outFile));
+        FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                | FileUtils.S_IRWXO);
+    }
+
+    private String mountObb(final int resource, final File file, int expectedState) {
+        copyRawToFile(resource, file);
+
+        final ObbObserver observer = new ObbObserver();
+        assertTrue("mountObb call on " + file.getPath() + " should succeed",
+                mStorageManager.mountObb(file.getPath(), null, observer));
+
+        assertTrue("Mount should have completed",
+                observer.waitForCompletion());
+
+        if (expectedState == OnObbStateChangeListener.MOUNTED) {
+            assertTrue("OBB should be mounted", mStorageManager.isObbMounted(observer.getPath()));
+        }
+
+        assertEquals(expectedState, observer.getState());
+
+        return observer.getPath();
+    }
+
+    private ObbObserver mountObbWithoutWait(final int resource, final File file) {
+        copyRawToFile(resource, file);
+
+        final ObbObserver observer = new ObbObserver();
+        assertTrue("mountObb call on " + file.getPath() + " should succeed",
+                mStorageManager.mountObb(file.getPath(), null, observer));
+
+        return observer;
+    }
+
+    private void waitForObbActionCompletion(final File file, final ObbObserver observer,
+            int expectedState) {
+        assertTrue("Mount should have completed", observer.waitForCompletion());
+
+        assertTrue("OBB should be mounted", mStorageManager.isObbMounted(observer.getPath()));
+
+        assertEquals(expectedState, observer.getState());
+    }
+
+    private String checkMountedPath(final String path) {
+        final String mountPath = mStorageManager.getMountedObbPath(path);
+        assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX,
+                OBB_MOUNT_PREFIX,
+                mountPath);
+        return mountPath;
+    }
+
+    private void unmountObb(final File file, int expectedState) {
+        final ObbObserver observer = new ObbObserver();
+
+        assertTrue("unmountObb call on test1.obb should succeed",
+                mStorageManager.unmountObb(file.getPath(), false, observer));
+
+        assertTrue("Unmount should have completed",
+                observer.waitForCompletion());
+
+        assertEquals(expectedState, observer.getState());
+
+        if (expectedState == OnObbStateChangeListener.UNMOUNTED) {
+            assertFalse("OBB should not be mounted", mStorageManager.isObbMounted(file.getPath()));
+        }
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/DebuggableTest.java b/tests/tests/permission/src/android/permission/cts/DebuggableTest.java
index 58751a5..fe4ed57 100644
--- a/tests/tests/permission/src/android/permission/cts/DebuggableTest.java
+++ b/tests/tests/permission/src/android/permission/cts/DebuggableTest.java
@@ -20,10 +20,7 @@
 import android.content.pm.PackageManager;
 import android.test.AndroidTestCase;
 
-import java.util.Arrays;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Verify that pre-installed packages don't have the debuggable
@@ -32,21 +29,14 @@
  */
 public class DebuggableTest extends AndroidTestCase {
 
-    // Remove whitelist in future release.
-    private static final Set<String> WHITELISTED_APPS = new HashSet<String>(Arrays.asList(
-            "com.google.android.apps.uploader"
-            ));
-
     public void testNoDebuggable() {
         List<ApplicationInfo> apps = getContext()
                 .getPackageManager()
                 .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
         for (ApplicationInfo app : apps) {
             String appName = app.packageName;
-            if (!WHITELISTED_APPS.contains(appName)) {
-                assertTrue("Package " + appName + " is marked as debuggable.",
-                        (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
-            }
+            assertTrue("Package " + appName + " is marked as debuggable.",
+                    (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
         }
     }
 }
diff --git a/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
index 3fccfaf..c5f8ea5 100644
--- a/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
@@ -48,21 +48,4 @@
             // expected
         }
     }
-
-    /**
-     * Verify that generating user activity requires Permission.
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#DEVICE_POWER}.
-     *
-     * TODO: add back SmallTest annotation when test has been fixed
-     */
-    @KnownFailure("will be fixed in future release")
-    public void testUserActivity() {
-        try {
-            mPowerManager.userActivity(0, false);
-            fail("Was able to call PowerManager.userActivity without DEVICE_POWER Permission.");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
 }
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 3dfcad4..3cbb362 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -23,12 +23,18 @@
 import android.test.suitebuilder.annotation.MediumTest;
 
 import java.io.File;
+import java.io.FileFilter;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Verify certain permissions on the filesystem
+ *
+ * TODO: Combine this file with {@link android.os.cts.FileAccessPermissionTest}
  */
 @MediumTest
 public class FileSystemPermissionTest extends AndroidTestCase {
@@ -80,7 +86,7 @@
         String myAppDirectory = getContext().getApplicationInfo().dataDir;
         for (ApplicationInfo app : apps) {
             if (!myAppDirectory.equals(app.dataDir)) {
-                assertDirectoryNotWritable(new File(app.dataDir));
+                assertDirectoryAndSubdirectoriesNotWritable(new File(app.dataDir));
             }
         }
     }
@@ -115,6 +121,27 @@
         assertDirectoryNotWritable(new File("/proc"));
     }
 
+    public void testDevMemSane() throws Exception {
+        File f = new File("/dev/mem");
+        assertFalse(f.canRead());
+        assertFalse(f.canWrite());
+        assertFalse(f.canExecute());
+    }
+
+    public void testDevkmemSane() throws Exception {
+        File f = new File("/dev/kmem");
+        assertFalse(f.canRead());
+        assertFalse(f.canWrite());
+        assertFalse(f.canExecute());
+    }
+
+    public void testDevPortSane() throws Exception {
+        File f = new File("/dev/port");
+        assertFalse(f.canRead());
+        assertFalse(f.canWrite());
+        assertFalse(f.canExecute());
+    }
+
     private static void assertDirectoryNotWritable(File directory) throws Exception {
         File toCreate = new File(directory, "hello");
         try {
@@ -127,4 +154,161 @@
             toCreate.delete();
         }
     }
+
+    /**
+     * Verify that any publicly readable directories reachable from
+     * the root directory are not writable.
+     *
+     * Note: Because not all directories are readable, this is a best-effort
+     * test only.  Writable directories within unreadable subdirectories
+     * will NOT be detected by this code.
+     */
+    public void testAllOtherDirectoriesNotWritable() throws Exception {
+        File start = new File("/");
+        assertDirectoryAndSubdirectoriesNotWritable(start);
+    }
+
+    private static final Set<String> OTHER_RANDOM_DIRECTORIES = new HashSet<String>(
+            Arrays.asList(
+                    "/data/backup",
+                    "/data/secure",
+                    "/data/system",
+                    "/data/dalvik-cache",
+                    "/data/property",
+                    "/data/app",
+                    "/data/app-private",
+                    "/data/local",
+                    "/data/misc",
+                    "/data/dontpanic",
+                    "/data/lost+found",
+                    "/data/drm",
+                    "/data/drm/rights",
+                    "/data/data/.drm",
+                    "/data/data/.drm/.wmdrm"
+            )
+    );
+
+    /**
+     * Because /data and /data/data are not readable, we blindly try to
+     * poke around in there looking for bad directories.  There has to be
+     * a better way...
+     */
+    public void testOtherRandomDirectoriesNotWritable() throws Exception {
+        for (String dir : OTHER_RANDOM_DIRECTORIES) {
+            File start = new File(dir);
+            assertDirectoryAndSubdirectoriesNotWritable(start);
+        }
+    }
+
+    public void testAllFilesInSysAreNotWritable() throws Exception {
+        assertAllFilesInDirAndSubDirAreNotWritable(new File("/sys"));
+    }
+
+    private static void
+    assertAllFilesInDirAndSubDirAreNotWritable(File dir) throws Exception {
+        assertTrue(dir.isDirectory());
+
+        if (isSymbolicLink(dir)) {
+            // don't examine symbolic links.
+            return;
+        }
+
+        File[] subDirectories = dir.listFiles(new FileFilter() {
+            @Override public boolean accept(File pathname) {
+                return pathname.isDirectory();
+            }
+        });
+
+
+        /* recurse into subdirectories */
+        if (subDirectories != null) {
+            for (File f : subDirectories) {
+                assertAllFilesInDirAndSubDirAreNotWritable(f);
+            }
+        }
+
+        File[] filesInThisDirectory = dir.listFiles(new FileFilter() {
+            @Override public boolean accept(File pathname) {
+                return pathname.isFile();
+            }
+        });
+        if (filesInThisDirectory == null) {
+            return;
+        }
+
+        for (File f: filesInThisDirectory) {
+            assertFalse(f.getCanonicalPath(), f.canWrite());
+        }
+    }
+
+    public void testAllBlockDevicesAreNotReadableWritable() throws Exception {
+        assertBlockDevicesInDirAndSubDirAreNotWritable(new File("/dev"));
+    }
+
+    private static void
+    assertBlockDevicesInDirAndSubDirAreNotWritable(File dir) throws Exception {
+        assertTrue(dir.isDirectory());
+        File[] subDirectories = dir.listFiles(new FileFilter() {
+            @Override public boolean accept(File pathname) {
+                return pathname.isDirectory();
+            }
+        });
+
+
+        /* recurse into subdirectories */
+        if (subDirectories != null) {
+            for (File f : subDirectories) {
+                assertBlockDevicesInDirAndSubDirAreNotWritable(f);
+            }
+        }
+
+        File[] filesInThisDirectory = dir.listFiles();
+        if (filesInThisDirectory == null) {
+            return;
+        }
+
+        for (File f: filesInThisDirectory) {
+            FileUtils.FileStatus status = new FileUtils.FileStatus();
+            FileUtils.getFileStatus(f.getAbsolutePath(), status, false);
+            if (status.hasModeFlag(FileUtils.S_IFBLK)) {
+                assertFalse(f.getCanonicalPath(), f.canRead());
+                assertFalse(f.getCanonicalPath(), f.canWrite());
+                assertFalse(f.getCanonicalPath(), f.canExecute());
+            }
+        }
+    }
+
+    private void assertDirectoryAndSubdirectoriesNotWritable(File dir) throws Exception {
+        if (!dir.isDirectory()) {
+            return;
+        }
+
+        if (isSymbolicLink(dir)) {
+            // don't examine symbolic links.
+            return;
+        }
+
+        String myHome = getContext().getApplicationInfo().dataDir;
+        String thisDir = dir.getCanonicalPath();
+        if (thisDir.startsWith(myHome)) {
+            // Don't examine directories within our home directory.
+            // We expect these directories to be writable.
+            return;
+        }
+
+        assertDirectoryNotWritable(dir);
+
+        File[] subFiles = dir.listFiles();
+        if (subFiles == null) {
+            return;
+        }
+
+        for (File f : subFiles) {
+            assertDirectoryAndSubdirectoriesNotWritable(f);
+        }
+    }
+
+    private static boolean isSymbolicLink(File f) throws IOException {
+        return !f.getAbsolutePath().equals(f.getCanonicalPath());
+    }
 }
diff --git a/tests/tests/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java b/tests/tests/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java
index 2d11883..a645424 100644
--- a/tests/tests/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java
@@ -22,7 +22,6 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
 import android.test.suitebuilder.annotation.MediumTest;
 
 /**
@@ -45,22 +44,6 @@
     }
 
     /**
-     * Verify that setting Activity's persistent attribute requires permissions.
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#PERSISTENT_ACTIVITY}.
-     */
-    @UiThreadTest
-    @MediumTest
-    public void testSetPersistent() {
-        try {
-            mActivity.setPersistent(true);
-            fail("Activity.setPersistent() did not throw SecurityException as expected");
-        } catch (SecurityException e) {
-            // Expected
-        }
-    }
-
-    /**
      * Verify that get task requires permissions.
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#GET_TASKS}
diff --git a/tests/tests/permission/src/android/permission/cts/NoCallPermissionTest.java b/tests/tests/permission/src/android/permission/cts/NoCallPermissionTest.java
deleted file mode 100644
index 88d5f1c..0000000
--- a/tests/tests/permission/src/android/permission/cts/NoCallPermissionTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2009 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.permission.cts;
-
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-/**
- * Verify Phone calling related methods without specific Phone/Call permissions.
- */
-public class NoCallPermissionTest extends AndroidTestCase {
-
-    /**
-     * Verify that Intent.ACTION_CALL requires permissions.
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#CALL_PHONE}.
-     */
-    @SmallTest
-    public void testActionCall() {
-        PackageManager packageManager = getContext().getPackageManager();
-        if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
-            Uri uri = Uri.parse("tel:123456");
-            Intent intent = new Intent(Intent.ACTION_CALL, uri);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            try {
-                mContext.startActivity(intent);
-                fail("startActivity(Intent.ACTION_CALL) did not throw SecurityException as expected");
-            } catch (SecurityException e) {
-                // expected
-            }
-        }
-    }
-
-    /**
-     * Verify that Intent.ACTION_CALL_PRIVILEGED requires permissions.
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#CALL_PRIVILEGED}.
-     */
-    @SmallTest
-    public void testCallVoicemail() {
-        PackageManager packageManager = getContext().getPackageManager();
-        if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
-            try {
-                //Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
-                Intent intent = new Intent("android.intent.action.CALL_PRIVILEGED",
-                        Uri.fromParts("voicemail", "", null));
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                mContext.startActivity(intent);
-                fail("startActivity(Intent.ACTION_CALL_PRIVILEGED) did not throw SecurityException as expected");
-            } catch (SecurityException e) {
-                // expected
-            }
-        }
-     }
-
-    /**
-     * Verify that Intent.ACTION_CALL_PRIVILEGED requires permissions.
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#CALL_PRIVILEGED}.
-     */
-    @SmallTest
-    public void testCall911() {
-        PackageManager packageManager = getContext().getPackageManager();
-        if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
-            //Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.parse("tel:911"));
-            Intent intent = new Intent("android.intent.action.CALL_PRIVILEGED",
-                    Uri.parse("tel:911"));
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            try {
-                mContext.startActivity(intent);
-                fail("startActivity(Intent.ACTION_CALL_PRIVILEGED) did not throw " +
-                        "SecurityException as expected");
-            } catch (SecurityException e) {
-               // expected
-           }
-        }
-    }
-
-}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsTest.java
index 2b0786c..fa1e431 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsTest.java
@@ -16,18 +16,14 @@
 
 package android.provider.cts;
 
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.KnownFailure;
+
 import dalvik.annotation.TestTargetClass;
 
 import android.content.ContentResolver;
-import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.IContentProvider;
 import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.net.Uri;
 import android.os.RemoteException;
@@ -48,7 +44,6 @@
 import android.telephony.PhoneNumberUtils;
 import android.test.InstrumentationTestCase;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Date;
@@ -153,29 +148,17 @@
      * Test case for the behavior of the ContactsProvider's groups table
      * It does not test any APIs in android.provider.Contacts.java
      */
-    @BrokenTest("Throws NPE in call to update(). uri parameter null?")
     public void testGroupsTable() {
         final String[] GROUPS_PROJECTION = new String[] {
-                Groups._ID, Groups.NAME, Groups.NOTES, Groups.SHOULD_SYNC,
-                Groups.SYSTEM_ID, Groups._SYNC_ACCOUNT, Groups._SYNC_ACCOUNT_TYPE, Groups._SYNC_ID,
-                Groups._SYNC_TIME, Groups._SYNC_VERSION, Groups._SYNC_LOCAL_ID,
-                Groups._SYNC_DIRTY};
+                Groups._ID, Groups.NAME, Groups.NOTES,
+                Groups.SYSTEM_ID};
         final int ID_INDEX = 0;
         final int NAME_INDEX = 1;
         final int NOTES_INDEX = 2;
-        final int SHOULD_SYNC_INDEX = 3;
-        final int SYSTEM_ID_INDEX = 4;
-        final int SYNC_ACCOUNT_NAME_INDEX = 5;
-        final int SYNC_ACCOUNT_TYPE_INDEX = 6;
-        final int SYNC_ID_INDEX = 7;
-        final int SYNC_TIME_INDEX = 8;
-        final int SYNC_VERSION_INDEX = 9;
-        final int SYNC_LOCAL_ID_INDEX = 10;
-        final int SYNC_DIRTY_INDEX = 11;
+        final int SYSTEM_ID_INDEX = 3;
 
         String insertGroupsName = "name_insert";
         String insertGroupsNotes = "notes_insert";
-        String updateGroupsName = "name_update";
         String updateGroupsNotes = "notes_update";
         String updateGroupsSystemId = "system_id_update";
 
@@ -188,40 +171,30 @@
 
             Uri uri = mProvider.insert(Groups.CONTENT_URI, value);
             Cursor cursor = mProvider.query(Groups.CONTENT_URI,
-                    GROUPS_PROJECTION, GroupsColumns.NAME + " = ?",
-                    new String[] {insertGroupsName}, null);
+                    GROUPS_PROJECTION, Groups._ID + " = ?",
+                    new String[] {uri.getPathSegments().get(1)}, null);
             assertTrue(cursor.moveToNext());
             assertEquals(insertGroupsName, cursor.getString(NAME_INDEX));
             assertEquals(insertGroupsNotes, cursor.getString(NOTES_INDEX));
-            assertEquals(0, cursor.getInt(SHOULD_SYNC_INDEX));
             assertEquals(Groups.GROUP_MY_CONTACTS, cursor.getString(SYSTEM_ID_INDEX));
-            // TODO: Figure out what can be tested for the SYNC_* columns
             int id = cursor.getInt(ID_INDEX);
             cursor.close();
 
             // Test: update
             value.clear();
-            value.put(GroupsColumns.NAME, updateGroupsName);
             value.put(GroupsColumns.NOTES, updateGroupsNotes);
             value.put(GroupsColumns.SYSTEM_ID, updateGroupsSystemId);
-            value.put(GroupsColumns.SHOULD_SYNC, 1);
 
-            mProvider.update(uri, value, null, null);
+            assertEquals(1, mProvider.update(uri, value, null, null));
             cursor = mProvider.query(Groups.CONTENT_URI, GROUPS_PROJECTION,
                     Groups._ID + " = " + id, null, null);
             assertTrue(cursor.moveToNext());
-            assertEquals(updateGroupsName, cursor.getString(NAME_INDEX));
             assertEquals(updateGroupsNotes, cursor.getString(NOTES_INDEX));
-            assertEquals(1, cursor.getInt(SHOULD_SYNC_INDEX));
             assertEquals(updateGroupsSystemId, cursor.getString(SYSTEM_ID_INDEX));
-            // TODO: Figure out what can be tested for the SYNC_* columns
             cursor.close();
 
             // Test: delete
-            mProvider.delete(uri, null, null);
-            cursor = mProvider.query(Groups.CONTENT_URI, GROUPS_PROJECTION,
-                    Groups._ID + " = " + id, null, null);
-            assertEquals(0, cursor.getCount());
+            assertEquals(1, mProvider.delete(uri, null, null));
         } catch (RemoteException e) {
             fail("Unexpected RemoteException");
         }
@@ -231,27 +204,17 @@
      * Test case for the behavior of the ContactsProvider's photos table
      * It does not test any APIs in android.provider.Contacts.java
      */
-    @BrokenTest("Should not test EXISTS_ON_SERVER_INDEX?")
     public void testPhotosTable() {
         final String[] PHOTOS_PROJECTION = new String[] {
                 Photos._ID, Photos.EXISTS_ON_SERVER, Photos.PERSON_ID,
-                Photos.LOCAL_VERSION, Photos.DATA, Photos._SYNC_ACCOUNT, Photos._SYNC_ACCOUNT_TYPE,
-                Photos._SYNC_ID, Photos._SYNC_TIME, Photos._SYNC_VERSION,
-                Photos._SYNC_LOCAL_ID, Photos._SYNC_DIRTY,
+                Photos.LOCAL_VERSION, Photos.DATA,
                 Photos.SYNC_ERROR};
         final int ID_INDEX = 0;
         final int EXISTS_ON_SERVER_INDEX = 1;
         final int PERSON_ID_INDEX = 2;
         final int LOCAL_VERSION_INDEX = 3;
         final int DATA_INDEX = 4;
-        final int SYNC_ACCOUNT_NAME_INDEX = 5;
-        final int SYNC_ACCOUNT_TYPE_INDEX = 6;
-        final int SYNC_ID_INDEX = 7;
-        final int SYNC_TIME_INDEX = 8;
-        final int SYNC_VERSION_INDEX = 9;
-        final int SYNC_LOCAL_ID_INDEX = 10;
-        final int SYNC_DIRTY_INDEX = 11;
-        final int SYNC_ERROR_INDEX = 12;
+        final int SYNC_ERROR_INDEX = 5;
 
         String updatePhotosLocalVersion = "local_version1";
 
@@ -275,54 +238,6 @@
             } catch (UnsupportedOperationException e) {
                 // Don't support direct insert operation to photos URI.
             }
-
-            // Insert a people to insert a row in photos table.
-            value.clear();
-            value.put(PeopleColumns.NAME, "name_photos_test_stub");
-            Uri peopleUri = mProvider.insert(People.CONTENT_URI, value);
-            int peopleId = Integer.parseInt(peopleUri.getPathSegments().get(1));
-
-            Cursor cursor = mProvider.query(Photos.CONTENT_URI,
-                    PHOTOS_PROJECTION, Photos.PERSON_ID + " = " + peopleId,
-                    null, null);
-            assertTrue(cursor.moveToNext());
-            assertEquals(0, cursor.getInt(EXISTS_ON_SERVER_INDEX));
-            assertEquals(peopleId, cursor.getInt(PERSON_ID_INDEX));
-            assertNull(cursor.getString(LOCAL_VERSION_INDEX));
-            assertNull(cursor.getString(DATA_INDEX));
-            // TODO: Figure out what can be tested for the SYNC_* columns
-            int id = cursor.getInt(ID_INDEX);
-            cursor.close();
-
-            // Test: update
-            value.clear();
-            value.put(Photos.LOCAL_VERSION, updatePhotosLocalVersion);
-            value.put(Photos.DATA, data);
-            value.put(Photos.EXISTS_ON_SERVER, 1);
-
-            Uri uri = ContentUris.withAppendedId(Photos.CONTENT_URI, id);
-            mProvider.update(uri, value, null, null);
-            cursor = mProvider.query(Photos.CONTENT_URI, PHOTOS_PROJECTION,
-                    Photos._ID + " = " + id, null, null);
-            assertTrue(cursor.moveToNext());
-            assertEquals(1, cursor.getInt(EXISTS_ON_SERVER_INDEX));
-            assertEquals(peopleId, cursor.getInt(PERSON_ID_INDEX));
-            assertEquals(updatePhotosLocalVersion, cursor.getString(LOCAL_VERSION_INDEX));
-            byte resultData[] = cursor.getBlob(DATA_INDEX);
-            InputStream resultInputStream = new ByteArrayInputStream(resultData);
-            Bitmap bitmap = BitmapFactory.decodeStream(resultInputStream, null, null);
-            assertEquals(sourceDrawable.getIntrinsicWidth(), bitmap.getWidth());
-            assertEquals(sourceDrawable.getIntrinsicHeight(), bitmap.getHeight());
-            // TODO: Figure out what can be tested for the SYNC_* columns
-            cursor.close();
-
-            // Test: delete
-            mProvider.delete(peopleUri, null, null);
-            cursor = mProvider.query(Photos.CONTENT_URI, PHOTOS_PROJECTION,
-                    Groups._ID + " = " + id, null, null);
-            assertEquals(0, cursor.getCount());
-
-            mProvider.delete(peopleUri, null, null);
         } catch (RemoteException e) {
             fail("Unexpected RemoteException");
         } catch (IOException e) {
diff --git a/tests/tests/provider/src/android/provider/cts/Contacts_PeopleTest.java b/tests/tests/provider/src/android/provider/cts/Contacts_PeopleTest.java
index 230a541..d8d6baa 100644
--- a/tests/tests/provider/src/android/provider/cts/Contacts_PeopleTest.java
+++ b/tests/tests/provider/src/android/provider/cts/Contacts_PeopleTest.java
@@ -16,6 +16,11 @@
 
 package android.provider.cts;
 
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -28,15 +33,10 @@
 import android.os.RemoteException;
 import android.provider.Contacts;
 import android.provider.Contacts.Groups;
+import android.provider.Contacts.GroupsColumns;
 import android.provider.Contacts.People;
 import android.test.InstrumentationTestCase;
 
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.TestTargets;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -58,7 +58,7 @@
     private static final int PEOPLE_LAST_CONTACTED_INDEX = 1;
 
     private static final int MEMBERSHIP_PERSON_ID_INDEX = 1;
-    private static final int MEMBERSHIP_GROUP_ID_INDEX = 7;
+    private static final int MEMBERSHIP_GROUP_ID_INDEX = 5;
 
     private static final String[] GROUPS_PROJECTION = new String[] {
         Groups._ID,
@@ -146,10 +146,14 @@
             args = {android.content.ContentResolver.class, android.content.ContentValues.class}
         )
     })
-    @BrokenTest("GROUP_MY_CONTACTS does not exist")
     public void testAddToGroup() {
         Cursor cursor;
         try {
+            // Add the My Contacts group, since it is no longer automatically created.
+            ContentValues testValues = new ContentValues();
+            testValues.put(GroupsColumns.SYSTEM_ID, Groups.GROUP_MY_CONTACTS);
+            mProvider.insert(Groups.CONTENT_URI, testValues);
+
             // People: test_people_0, Group: Groups.GROUP_MY_CONTACTS
             cursor = mProvider.query(mPeopleRowsAdded.get(0), PEOPLE_PROJECTION,
                     null, null, null);
@@ -158,9 +162,8 @@
             cursor.close();
             mRowsAdded.add(People.addToMyContactsGroup(mContentResolver, personId));
             cursor = mProvider.query(Groups.CONTENT_URI, GROUPS_PROJECTION,
-                    Groups.NAME + "='" + Groups.GROUP_MY_CONTACTS + "'", null, null);
+                    Groups.SYSTEM_ID + "='" + Groups.GROUP_MY_CONTACTS + "'", null, null);
             cursor.moveToFirst();
-            // TODO: this throws an exception because no rows were found
             int groupId = cursor.getInt(GROUPS_ID_INDEX);
             cursor.close();
             cursor = People.queryGroups(mContentResolver, personId);
@@ -183,7 +186,7 @@
             mRowsAdded.add(ContentUris.withAppendedId(People.CONTENT_URI, personId));
             cursor.close();
             cursor = mProvider.query(Groups.CONTENT_URI, GROUPS_PROJECTION,
-                    Groups.NAME + "='" + Groups.GROUP_MY_CONTACTS + "'", null, null);
+                    Groups.SYSTEM_ID + "='" + Groups.GROUP_MY_CONTACTS + "'", null, null);
             cursor.moveToFirst();
             groupId = cursor.getInt(GROUPS_ID_INDEX);
             cursor.close();
@@ -281,7 +284,7 @@
             level = TestLevel.COMPLETE,
             notes = "Test methods access the photo data of person",
             method = "loadContactPhoto",
-            args = {android.content.Context.class, android.net.Uri.class, int.class, 
+            args = {android.content.Context.class, android.net.Uri.class, int.class,
                     android.graphics.BitmapFactory.Options.class}
         ),
         @TestTargetNew(
@@ -291,7 +294,6 @@
             args = {android.content.ContentResolver.class, android.net.Uri.class}
         )
     })
-    @BrokenTest("photoStream is null after setting photo data")
     public void testAccessPhotoData() {
         Context context = getInstrumentation().getTargetContext();
         try {
@@ -308,10 +310,6 @@
             Bitmap bitmap = BitmapFactory.decodeStream(photoStream, null, null);
             assertEquals(212, bitmap.getWidth());
             assertEquals(142, bitmap.getHeight());
-            // NOTE: this data we added can't be deleted, will be garbage data.
-//            Uri photoUri = Uri.withAppendedPath(mPeopleRowsAdded.get(0),
-//                    Contacts.Photos.CONTENT_DIRECTORY);
-//            mRowsAdded.add(photoUri);
 
             photoStream = People.openContactPhotoInputStream(mContentResolver,
                     mPeopleRowsAdded.get(1));
@@ -324,8 +322,7 @@
 
             bitmap = People.loadContactPhoto(context, null,
                     com.android.cts.stub.R.drawable.size_48x48, null);
-            assertEquals(48, bitmap.getWidth());
-            assertEquals(48, bitmap.getHeight());
+            assertNotNull(bitmap);
         } catch (IOException e) {
             fail("Unexpected IOException");
         }
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
index 6b15bb9..12d080e 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
@@ -16,16 +16,13 @@
 
 package android.provider.cts;
 
-import dalvik.annotation.BrokenTest;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.ToBeFixed;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.database.Cursor;
-import android.database.SQLException;
 import android.net.Uri;
 import android.provider.MediaStore.Audio.Media;
 import android.provider.MediaStore.Audio.Playlists;
@@ -127,42 +124,13 @@
       method = "getContentUri",
       args = {String.class, long.class}
     )
-    @BrokenTest("brittle test")
     public void testGetContentUri() {
-        // this verification seems brittle - will break if there happens to be a playlist with Id 1
-        // present in external volume
-        // setUp should create a playlist which this method should verify can be queried
-        Cursor c = mContentResolver.query(
-                Members.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME, 1),
-                mMembersProjection, null, null, Members.DEFAULT_SORT_ORDER);
-        assertEquals(0, c.getCount());
-        c.close();
-
-        // test querying media provider with null projection, should return all columns
-        c = mContentResolver.query(
-                Members.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME, 1), null,
-                Members.ALBUM + "=?", new String[] { Audio1.ALBUM },
-                Members.DEFAULT_SORT_ORDER);
-        assertEquals(0, c.getCount());
-        // TODO: need a way to verify all expected columns are returned. Purely testing for number
-        // of columns returned is brittle
-        assertEquals(31, c.getColumnCount());
-        c.close();
-
-        try {
-            mContentResolver.query(
-                    Members.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME, 1),
-                    mMembersProjection, null, null, Members.DEFAULT_SORT_ORDER);
-            fail("Should throw SQLException as the internal datatbase has no playlist");
-        } catch (SQLException e) {
-            // expected
-        }
-
-        String volume = "fakeVolume";
-        assertNull(mContentResolver.query(Members.getContentUri(volume, 1), null, null, null,
-                null));
+        assertEquals("content://media/external/audio/playlists/1337/members",
+                Members.getContentUri("external", 1337).toString());
+        assertEquals("content://media/internal/audio/playlists/3007/members",
+                Members.getContentUri("internal", 3007).toString());
     }
-    @BrokenTest("needs investigation")
+
     public void testStoreAudioPlaylistsMembersExternal() {
         ContentValues values = new ContentValues();
         values.put(Playlists.NAME, "My favourites");
diff --git a/tests/tests/provider/src/android/provider/cts/SettingsTest.java b/tests/tests/provider/src/android/provider/cts/SettingsTest.java
index 3a789d1..06fb4b4 100644
--- a/tests/tests/provider/src/android/provider/cts/SettingsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/SettingsTest.java
@@ -27,10 +27,10 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
 @TestTargetClass(android.provider.Settings.class)
 public class SettingsTest extends AndroidTestCase {
-
     public void testSystemTable() throws RemoteException {
         final String[] SYSTEM_PROJECTION = new String[] {
                 Settings.System._ID, Settings.System.NAME, Settings.System.VALUE
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java b/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
index 6ce4157..718b9d2 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
+++ b/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
@@ -23,6 +23,7 @@
 
 import android.content.ContentResolver;
 import android.net.Uri;
+import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.provider.Settings.SettingNotFoundException;
 import android.test.AndroidTestCase;
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/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
new file mode 100644
index 0000000..22cf3e7
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -0,0 +1,157 @@
+/*
+ * 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;
+
+/**
+ * Run "./cts/tools/utils/java-cert-list-generator.sh >
+ * cts/tests/tests/security/src/android/security/cts/CertificateData.java"
+ * to generate this file.
+ */
+class CertificateData {
+  static final String[] CERTIFICATE_DATA = {
+      "91:C6:D6:EE:3E:8A:C8:63:84:E5:48:C2:99:29:5C:75:6C:81:7B:81",
+      "A4:34:89:15:9A:52:0F:0D:93:D0:32:CC:AF:37:E7:FE:20:A8:B4:19",
+      "E7:B4:F6:9D:61:EC:90:69:DB:7E:90:A7:40:1A:3C:F4:7D:4F:E8:EE",
+      "DD:E1:D2:A9:01:80:2E:1D:87:5E:84:B3:80:7E:4B:B1:FD:99:41:34",
+      "92:5A:8F:8D:2C:6D:04:E0:66:5F:59:6A:FF:22:D8:63:E8:25:6F:3F",
+      "75:E0:AB:B6:13:85:12:27:1C:04:F8:5F:DD:DE:38:E4:B7:24:2E:FE",
+      "A9:62:8F:4B:98:A9:1B:48:35:BA:D2:C1:46:32:86:BB:66:64:6A:8C",
+      "40:9D:4B:D9:17:B5:5C:27:B6:9B:64:CB:98:22:44:0D:CD:09:B8:89",
+      "87:81:C2:5A:96:BD:C2:FB:4C:65:06:4F:F9:39:0B:26:04:8A:0E:01",
+      "DA:C9:02:4F:54:D8:F6:DF:94:93:5F:B1:73:26:38:CA:6A:D7:7C:13",
+      "74:20:74:41:72:9C:DD:92:EC:79:31:D8:23:10:8D:C2:81:92:E2:BB",
+      "3C:71:D7:0E:35:A5:DA:A8:B2:E3:81:2D:C3:67:74:17:F5:99:0D:F3",
+      "40:54:DA:6F:1C:3F:40:74:AC:ED:0F:EC:CD:DB:79:D1:53:FB:90:1D",
+      "43:F9:B1:10:D5:BA:FD:48:22:52:31:B0:D0:08:2B:37:2F:EF:9A:54",
+      "F4:8B:11:BF:DE:AB:BE:94:54:20:71:E6:41:DE:6B:BE:88:2B:40:B9",
+      "96:56:CD:7B:57:96:98:95:D0:E1:41:46:68:06:FB:B8:C6:11:06:87",
+      "D6:9B:56:11:48:F0:1C:77:C5:45:78:C1:09:26:DF:5B:85:69:76:AD",
+      "78:6A:74:AC:76:AB:14:7F:9C:6A:30:50:BA:9E:A8:7E:FE:9A:CE:3C",
+      "27:96:BA:E6:3F:18:01:E2:77:26:1B:A0:D7:77:70:02:8F:20:EE:E4",
+      "AD:7E:1C:28:B0:64:EF:8F:60:03:40:20:14:C3:D0:E3:37:0E:B5:8A",
+      "AE:50:83:ED:7C:F4:5C:BC:8F:61:C6:21:FE:68:5D:79:42:21:15:6E",
+      "5F:4E:1F:CF:31:B7:91:3B:85:0B:54:F6:E5:FF:50:1A:2B:6F:C6:CF",
+      "74:F8:A3:C3:EF:E7:B3:90:06:4B:83:90:3C:21:64:60:20:E5:DF:CE",
+      "85:B5:FF:67:9B:0C:79:96:1F:C8:6E:44:22:00:46:13:DB:17:92:84",
+      "3E:2B:F7:F2:03:1B:96:F3:8C:E6:C4:D8:A8:5D:3E:2D:58:47:6A:0F",
+      "5F:43:E5:B1:BF:F8:78:8C:AC:1C:C7:CA:4A:9A:C6:22:2B:CC:34:C6",
+      "A8:98:5D:3A:65:E5:E5:C4:B2:D7:D6:6D:40:C6:DD:2F:B1:9C:54:36",
+      "59:22:A1:E1:5A:EA:16:35:21:F8:98:39:6A:46:46:B0:44:1B:0F:A9",
+      "D4:DE:20:D0:5E:66:FC:53:FE:1A:50:88:2C:78:DB:28:52:CA:E4:74",
+      "02:FA:F3:E2:91:43:54:68:60:78:57:69:4D:F5:E4:5B:68:85:18:68",
+      "D8:C5:38:8A:B7:30:1B:1B:6E:D4:7A:E6:45:25:3A:6F:9F:1A:27:61",
+      "59:AF:82:79:91:86:C7:B4:75:07:CB:CF:03:57:46:EB:04:DD:B7:16",
+      "50:30:06:09:1D:97:D4:F5:AE:39:F7:CB:E7:92:7D:7D:65:2D:34:31",
+      "1B:4B:39:61:26:27:6B:64:91:A2:68:6D:D7:02:43:21:2D:1F:1D:96",
+      "8C:F4:27:FD:79:0C:3A:D1:66:06:8D:E8:1E:57:EF:BB:93:22:72:D4",
+      "56:E0:FA:C0:3B:8F:18:23:55:18:E5:D3:11:CA:E8:C2:43:31:AB:66",
+      "02:72:68:29:3E:5F:5D:17:AA:A4:B3:C3:E6:36:1E:1F:92:57:5E:AA",
+      "97:81:79:50:D8:1C:96:70:CC:34:D8:09:CF:79:44:31:36:7E:F4:74",
+      "85:A4:08:C0:9C:19:3E:5D:51:58:7D:CD:D6:13:30:FD:8C:DE:37:BF",
+      "58:11:9F:0E:12:82:87:EA:50:FD:D9:87:45:6F:4F:78:DC:FA:D6:D4",
+      "6B:2F:34:AD:89:58:BE:62:FD:B0:6B:5C:CE:BB:9D:D9:4F:4E:39:F3",
+      "9B:AA:E5:9F:56:EE:21:CB:43:5A:BE:25:93:DF:A7:F0:40:D1:1D:CB",
+      "36:79:CA:35:66:87:72:30:4D:30:A5:FB:87:3B:0F:A7:7B:B7:0D:54",
+      "B4:35:D4:E1:11:9D:1C:66:90:A7:49:EB:B3:94:BD:63:7B:A7:82:B7",
+      "A9:E9:78:08:14:37:58:88:F2:05:19:B0:6D:2B:0D:2B:60:16:90:7D",
+      "60:D6:89:74:B5:C2:65:9E:8A:0F:C1:88:7C:88:D2:46:69:1B:18:2C",
+      "D2:32:09:AD:23:D3:14:23:21:74:E4:0D:7F:9D:62:13:97:86:63:3A",
+      "66:31:BF:9E:F7:4F:9E:B6:C9:D5:A6:0C:BA:6A:BE:D1:F7:BD:EF:7B",
+      "87:9F:4B:EE:05:DF:98:58:3B:E3:60:D6:33:E7:0D:3F:FE:98:71:AF",
+      "DE:3F:40:BD:50:93:D3:9B:6C:60:F6:DA:BC:07:62:01:00:89:76:C9",
+      "06:08:3F:59:3F:15:A1:04:A0:69:A4:6B:A9:03:D0:06:B7:97:09:91",
+      "E3:92:51:2F:0A:CF:F5:05:DF:F6:DE:06:7F:75:37:E1:65:EA:57:4B",
+      "F1:8B:53:8D:1B:E9:03:B6:A6:F0:56:43:5B:17:15:89:CA:F3:6B:F2",
+      "05:63:B8:63:0D:62:D7:5A:BB:C8:AB:1E:4B:DF:B5:A8:99:B2:4D:43",
+      "93:E6:AB:22:03:03:B5:23:28:DC:DA:56:9E:BA:E4:D1:D1:CC:FB:65",
+      "62:52:DC:40:F7:11:43:A2:2F:DE:9E:F7:34:8E:06:42:51:B1:81:18",
+      "70:17:9B:86:8C:00:A4:FA:60:91:52:22:3F:9F:3E:32:BD:E0:05:62",
+      "A0:A1:AB:90:C9:FC:84:7B:3B:12:61:E8:97:7D:5F:D3:22:61:D3:CC",
+      "85:37:1C:A6:E5:50:14:3D:CE:28:03:47:1B:DE:3A:09:E8:F8:77:0F",
+      "7E:78:4A:10:1C:82:65:CC:2D:E1:F1:6D:47:B4:40:CA:D9:0A:19:45",
+      "D1:EB:23:A4:6D:17:D6:8F:D9:25:64:C2:F1:F1:60:17:64:D8:E3:49",
+      "74:2C:31:92:E6:07:E4:24:EB:45:49:54:2B:E1:BB:C5:3E:61:74:E2",
+      "B8:01:86:D1:EB:9C:86:A5:41:04:CF:30:54:F3:4C:52:B7:E5:58:C6",
+      "DE:28:F4:A4:FF:E5:B9:2F:A3:C5:03:D1:A3:49:A7:F9:96:2A:82:12",
+      "80:25:EF:F4:6E:70:C8:D4:72:24:65:84:FE:40:3B:8A:8D:6A:DB:F5",
+      "CA:3A:FB:CF:12:40:36:4B:44:B2:16:20:88:80:48:39:19:93:7C:F7",
+      "69:BD:8C:F4:9C:D3:00:FB:59:2E:17:93:CA:55:6A:F3:EC:AA:35:FB",
+      "13:2D:0D:45:53:4B:69:97:CD:B2:D5:C3:39:E2:55:76:60:9B:5C:C6",
+      "5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25",
+      "25:01:90:19:CF:FB:D9:99:1C:B7:68:25:74:8D:94:5F:30:93:95:42",
+      "79:98:A3:08:E1:4D:65:85:E6:C2:1E:15:3A:71:9F:BA:5A:D3:4A:D9",
+      "B5:1C:06:7C:EE:2B:0C:3D:F8:55:AB:2D:92:F4:FE:39:D4:E7:0F:0E",
+      "29:36:21:02:8B:20:ED:02:F5:66:C5:32:D1:D6:ED:90:9F:45:00:2F",
+      "37:9A:19:7B:41:85:45:35:0C:A6:03:69:F3:3C:2E:AF:47:4F:20:79",
+      "FA:B7:EE:36:97:26:62:FB:2D:B0:2A:F6:BF:03:FD:E8:7C:4B:2F:9B",
+      "8B:AF:4C:9B:1D:F0:2A:92:F7:DA:12:8E:B9:1B:AC:F4:98:60:4B:6F",
+      "39:4F:F6:85:0B:06:BE:52:E5:18:56:CC:10:E1:80:E8:82:B3:85:CC",
+      "1F:49:14:F7:D8:74:95:1D:DD:AE:02:C0:BE:FD:3A:2D:82:75:51:85",
+      "D6:DA:A8:20:8D:09:D2:15:4D:24:B5:2F:CB:34:6E:B2:58:B2:8A:58",
+      "32:3C:11:8E:1B:F7:B8:B6:52:54:E2:E2:10:0D:D6:02:90:37:F0:96",
+      "67:65:0D:F1:7E:8E:7E:5B:82:40:A4:F4:56:4B:CF:E2:3D:69:C6:F0",
+      "FE:B8:C4:32:DC:F9:76:9A:CE:AE:3D:D8:90:8F:FD:28:86:65:64:7D",
+      "4A:BD:EE:EC:95:0D:35:9C:89:AE:C7:52:A1:2C:5B:29:F6:D6:AA:0C",
+      "33:9B:6B:14:50:24:9B:55:7A:01:87:72:84:D9:E0:2F:C3:D2:D8:E9",
+      "AB:48:F3:33:DB:04:AB:B9:C0:72:DA:5B:0C:C1:D0:57:F0:36:9B:46",
+      "36:B1:2B:49:F9:81:9E:D7:4C:9E:BC:38:0F:C6:56:8F:5D:AC:B2:F7",
+      "37:F7:6D:E6:07:7C:90:C5:B1:3E:93:1A:B7:41:10:B4:F2:E4:9A:27",
+      "3B:C4:9F:48:F8:F3:73:A0:9C:1E:BD:F8:5B:B1:C3:65:C7:D8:11:B3",
+      "AC:ED:5F:65:53:FD:25:CE:01:5F:1F:7A:48:3B:6A:74:9F:61:78:C6",
+      "B1:BC:96:8B:D4:F4:9D:62:2A:A8:9A:81:F2:15:01:52:A4:1D:82:9C",
+      "47:BE:AB:C9:22:EA:E8:0E:78:78:34:62:A7:9F:45:C2:54:FD:E6:8B",
+      "31:7A:2A:D0:7F:2B:33:5E:F5:A1:C3:4E:4B:57:E8:B7:D8:F1:FC:A6",
+      "39:21:C1:15:C1:5D:0E:CA:5C:CB:5B:C4:F0:7D:21:D8:05:0B:56:6A",
+      "3A:44:73:5A:E5:81:90:1F:24:86:61:46:1E:3B:9C:C4:5F:F5:3A:1B",
+      "B3:1E:B1:B7:40:E3:6C:84:02:DA:DC:37:D4:4D:F5:D4:67:49:52:F9",
+      "C0:60:ED:44:CB:D8:81:BD:0E:F8:6C:0B:A2:87:DD:CF:81:67:47:8C",
+      "81:96:8B:3A:EF:1C:DC:70:F5:FA:32:69:C2:92:A3:63:5B:D1:23:D3",
+      "62:7F:8D:78:27:65:63:99:D2:7D:7F:90:44:C9:FE:B3:F3:3E:FA:9A",
+      "C8:EC:8C:87:92:69:CB:4B:AB:39:E9:8D:7E:57:67:F3:14:95:73:9D",
+      "03:9E:ED:B8:0B:E7:A0:3C:69:53:89:3B:20:D2:D9:32:3A:4C:2A:FD",
+      "CB:A1:C5:F8:B0:E3:5E:B8:B9:45:12:D3:F9:34:A2:E9:06:10:D3:36",
+      "10:1D:FA:3F:D5:0B:CB:BB:9B:B5:60:0C:19:55:A4:1A:F4:73:3A:04",
+      "87:82:C6:C3:04:35:3B:CF:D2:96:92:D2:59:3E:7D:44:D9:34:FF:11",
+      "AE:C5:FB:3F:C8:E1:BF:C4:E5:4F:03:07:5A:9A:E8:00:B7:F7:B6:FA",
+      "21:FC:BD:8E:7F:6C:AF:05:1B:D1:B3:43:EC:A8:E7:61:47:F2:0F:8A",
+      "2A:C8:D5:8B:57:CE:BF:2F:49:AF:F2:FC:76:8F:51:14:62:90:7A:41",
+      "CA:BB:51:67:24:00:58:8E:64:19:F1:D4:08:78:D0:40:3A:A2:02:64",
+      "5D:98:9C:DB:15:96:11:36:51:65:64:1B:56:0F:DB:EA:2A:C2:3E:F1",
+      "D8:A6:33:2C:E0:03:6F:B1:85:F6:63:4F:7D:6A:06:65:26:32:28:27",
+      "23:E5:94:94:51:95:F2:41:48:03:B4:D5:64:D2:A3:A3:F5:D8:8B:8C",
+      "F9:B5:B6:32:45:5F:9C:BE:EC:57:5F:80:DC:E9:6E:2C:C7:B2:78:B7",
+      "5F:3A:FC:0A:8B:64:F6:86:67:34:74:DF:7E:A9:A2:FE:F9:FA:7A:51",
+      "E6:21:F3:35:43:79:05:9A:4B:68:30:9D:8A:2F:74:22:15:87:EC:79",
+      "DA:40:18:8B:91:89:A3:ED:EE:AE:DA:97:FE:2F:9D:F5:B7:D1:8A:41",
+      "89:DF:74:FE:5C:F4:0F:4A:80:F9:E3:37:7D:54:DA:91:E1:01:31:8E",
+      "E0:B4:32:2E:B2:F6:A5:68:B6:54:53:84:48:18:4A:50:36:87:43:84",
+      "61:57:3A:11:DF:0E:D8:7E:D5:92:65:22:EA:D0:56:D7:44:B3:23:71",
+      "0B:77:BE:BB:CB:7A:A2:47:05:DE:CC:0F:BD:6A:02:FC:7A:BD:9B:52",
+      "99:A6:9B:E6:1A:FE:88:6B:4D:2B:82:00:7C:B8:54:FC:31:7E:15:39",
+      "6E:3A:55:A4:19:0C:19:5C:93:84:3C:C0:DB:72:2E:31:30:61:F0:B1",
+      "E5:DF:74:3C:B6:01:C4:9B:98:43:DC:AB:8C:E8:6A:81:10:9F:E4:8E",
+      "F9:CD:0E:2C:DA:76:24:C1:8F:BD:F0:F0:AB:B6:45:B8:F7:FE:D5:7A",
+      "23:88:C9:D3:71:CC:9E:96:3D:FF:7D:3C:A7:CE:FC:D6:25:EC:19:0D",
+      "8C:96:BA:EB:DD:2B:07:07:48:EE:30:32:66:A0:F3:98:6E:7C:AE:58",
+      "7F:8A:B0:CF:D0:51:87:6A:66:F3:36:0F:47:C8:8D:8C:D3:35:FC:74",
+      "4E:B6:D5:78:49:9B:1C:CF:5F:58:1E:AD:56:BE:3D:9B:67:44:A5:E5",
+      "B1:2E:13:63:45:86:A4:6F:1A:B2:60:68:37:58:2D:C4:AC:FD:94:97",
+      "04:83:ED:33:99:AC:36:08:05:87:22:ED:BC:5E:46:00:E3:BE:F9:D7",
+  };
+}
diff --git a/tests/tests/security/src/android/security/cts/CertificateTest.java b/tests/tests/security/src/android/security/cts/CertificateTest.java
new file mode 100644
index 0000000..541673b
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CertificateTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.test.AndroidTestCase;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class CertificateTest extends AndroidTestCase {
+
+    public void testCertificates() throws Exception {
+        Set<String> expectedCertificates = getExpectedCertificates();
+        Set<String> deviceCertificates = getDeviceCertificates();
+        expectedCertificates.removeAll(deviceCertificates);
+        assertTrue("Missing certificates: " + expectedCertificates, expectedCertificates.isEmpty());
+    }
+
+    private Set<String> getExpectedCertificates() {
+        Set<String> certificates = new HashSet<String>();
+        for (int i = 0; i < CertificateData.CERTIFICATE_DATA.length; i++) {
+            certificates.add(CertificateData.CERTIFICATE_DATA[i]);
+        }
+        return certificates;
+    }
+
+    private Set<String> getDeviceCertificates() throws KeyStoreException,
+            NoSuchAlgorithmException, CertificateException, IOException {
+        String trustStore = System.getProperty("javax.net.ssl.trustStore", null);
+        assertNotNull(trustStore);
+
+        KeyStore keyStore = KeyStore.getInstance("BKS");
+        InputStream inputStream = new FileInputStream(trustStore);
+        keyStore.load(inputStream, null);
+
+        List<String> aliases = Collections.list(keyStore.aliases());
+        assertFalse(aliases.isEmpty());
+
+        Set<String> certificates = new HashSet<String>();
+        for (String alias : aliases) {
+            assertTrue(keyStore.isCertificateEntry(alias));
+            X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias);
+            assertEquals(certificate.getSubjectUniqueID(), certificate.getIssuerUniqueID());
+            assertNotNull(certificate.getSubjectDN());
+            assertNotNull(certificate.getIssuerDN());
+            String fingerprint = getFingerprint(certificate);
+            certificates.add(fingerprint);
+        }
+        return certificates;
+    }
+
+    private String getFingerprint(X509Certificate certificate) throws CertificateEncodingException,
+            NoSuchAlgorithmException {
+        MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
+        messageDigest.update(certificate.getEncoded());
+        byte[] sha1 = messageDigest.digest();
+        return convertToHexFingerprint(sha1);
+    }
+
+    private String convertToHexFingerprint(byte[] sha1) {
+        StringBuilder fingerprint = new StringBuilder();
+        for (int i = 0; i < sha1.length; i++) {
+            fingerprint.append(String.format("%02X", sha1[i]));
+            if (i + 1 < sha1.length) {
+                fingerprint.append(":");
+            }
+        }
+        return fingerprint.toString();
+    }
+}
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..12b4392 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;
         }
 
@@ -115,6 +115,9 @@
     private static Set<String> getSysFsPath(String file) throws IOException {
         Set<String> retval = new HashSet<String>();
         File netlink = new File(file);
+        if (!netlink.canRead()) {
+            return retval;
+        }
         Scanner scanner = null;
         try {
             scanner = new Scanner(netlink);
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index c201fb7..7b56b56 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -27,9 +27,12 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.os.SystemClock;
 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;
@@ -53,38 +56,92 @@
 
     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 =
+            Arrays.asList(
+                    "310410",   // AT&T Mobility
+                    "44010",    // NTT DOCOMO
+                    "45005",    // SKT Mobility
+                    "45002",    // SKT Mobility
+                    "45008",    // KT Mobility
+                    "45006",    // LGT
+                    "311660",   // MetroPCS
+                    "310120",   // Sprint
+                    "44053",    // KDDI
+                    "44054",    // KDDI
+                    "44070",    // KDDI
+                    "44071",    // KDDI
+                    "44072",    // KDDI
+                    "44073",    // KDDI
+                    "44074",    // KDDI
+                    "44075",    // KDDI
+                    "44076",    // KDDI
+                    "311870",   // Boost Mobile
+                    "311220",   // USCC
+                    "302720",   // Rogers
+                    "30272",    // Rogers
+                    "302370",   // Fido
+                    "30237"     // Fido
+            );
 
     // List of network operators that doesn't support Data(binary) SMS message
     private static final List<String> UNSUPPORT_DATA_SMS_MESSAGES =
             Arrays.asList(
-                    "44010"    // NTT DOCOMO
+                    "44010",    // NTT DOCOMO
+                    "44020",    // SBM
+                    "302720",   // Rogers
+                    "30272",    // Rogers
+                    "302370",   // Fido
+                    "30237"     // Fido
             );
 
     // List of network operators that doesn't support Maltipart SMS message
     private static final List<String> UNSUPPORT_MULTIPART_SMS_MESSAGES =
             Arrays.asList(
-                    "44010"    // NTT DOCOMO
+                    "44010",    // NTT DOCOMO
+                    "44020",    // SBM
+                    "302720",   // Rogers
+                    "30272",    // Rogers
+                    "302370",   // Fido
+                    "30237",    // Fido
+                    "45008"     // KT
             );
 
     private TelephonyManager mTelephonyManager;
+    private PackageManager mPackageManager;
     private String mDestAddr;
     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 * 4;
+    private static final int TIME_OUT = 1000 * 60 * 5;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mTelephonyManager =
             (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
+        mPackageManager = mContext.getPackageManager();
         mDestAddr = mTelephonyManager.getLine1Number();
         mText = "This is a test message";
+
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            mDeliveryReportSupported = false;
+        } else {
+            // exclude the networks that don't support SMS delivery report
+            String mccmnc = mTelephonyManager.getSimOperator();
+            mDeliveryReportSupported = !(NO_DELIVERY_REPORTS.contains(mccmnc));
+        }
     }
 
     @TestTargetNew(
@@ -120,8 +177,7 @@
         )
     })
     public void testSendMessages() throws InterruptedException {
-        PackageManager packageManager = mContext.getPackageManager();
-        if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             return;
         }
 
@@ -132,18 +188,25 @@
 
         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();
         sendTextMessage(mDestAddr, mDestAddr, mSentIntent, mDeliveredIntent);
-        mSendReceiver.waitForCalls(1, TIME_OUT);
-        mDeliveryReceiver.waitForCalls(1, TIME_OUT);
+        assertTrue(mSendReceiver.waitForCalls(1, TIME_OUT));
+        if (mDeliveryReportSupported) {
+            assertTrue(mDeliveryReceiver.waitForCalls(1, TIME_OUT));
+        }
 
         if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
             // TODO: temp workaround, OCTET encoding for EMS not properly supported
@@ -157,8 +220,13 @@
 
             init();
             sendDataMessage(mDestAddr, port, data, mSentIntent, mDeliveredIntent);
-            mSendReceiver.waitForCalls(1, TIME_OUT);
-            mDeliveryReceiver.waitForCalls(1, TIME_OUT);
+            assertTrue(mSendReceiver.waitForCalls(1, TIME_OUT));
+            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.
@@ -176,8 +244,10 @@
                 deliveryIntents.add(PendingIntent.getBroadcast(getContext(), 0, mDeliveryIntent, 0));
             }
             sendMultiPartTextMessage(mDestAddr, parts, sentIntents, deliveryIntents);
-            mSendReceiver.waitForCalls(numParts, TIME_OUT);
-            mDeliveryReceiver.waitForCalls(numParts, TIME_OUT);
+            assertTrue(mSendReceiver.waitForCalls(numParts, TIME_OUT));
+            if (mDeliveryReportSupported) {
+              assertTrue(mDeliveryReceiver.waitForCalls(numParts, TIME_OUT));
+            }
         } else {
             // This GSM network doesn't support Multipart SMS message.
             // Skip the test.
@@ -187,6 +257,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,
@@ -242,6 +314,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;
@@ -252,12 +343,20 @@
             }
         }
 
-        public void waitForCalls(int expectedCalls, long timeout) throws InterruptedException {
+        public boolean waitForCalls(int expectedCalls, long timeout) throws InterruptedException {
             synchronized(mLock) {
                 mExpectedCalls = expectedCalls;
-                if (mCalls < mExpectedCalls) {
-                    mLock.wait(timeout);
+                long startTime = SystemClock.elapsedRealtime();
+
+                while (mCalls < mExpectedCalls) {
+                    long waitTime = timeout - (SystemClock.elapsedRealtime() - startTime);
+                    if (waitTime > 0) {
+                        mLock.wait(waitTime);
+                    } else {
+                        return false;  // timed out
+                    }
                 }
+                return true;  // success
             }
         }
     }
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
index 44b0871..515f8b5 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
@@ -212,7 +212,7 @@
         assertEquals(SCA2, sms.getServiceCenterAddress());
         assertEquals(OA2, sms.getOriginatingAddress());
         assertEquals(MESSAGE_BODY2, sms.getMessageBody());
-        CharSequence msgBody = (CharSequence) sms.getMessageBody();
+        CharSequence msgBody = sms.getMessageBody();
         result = SmsMessage.calculateLength(msgBody, false);
         assertEquals(SMS_NUMBER2, result[0]);
         assertEquals(sms.getMessageBody().length(), result[1]);
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 0528897..6e8aa27 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -21,14 +21,13 @@
 import dalvik.annotation.TestTargetNew;
 import dalvik.annotation.TestTargets;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.os.Build;
 import android.os.Looper;
 import android.os.cts.TestThread;
-import android.provider.Settings;
 import android.telephony.CellLocation;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
@@ -289,7 +288,7 @@
         int phoneType = mTelephonyManager.getPhoneType();
         switch (phoneType) {
             case TelephonyManager.PHONE_TYPE_GSM:
-                assertImeiDeviceId(deviceId);
+                assertGsmDeviceId(deviceId);
                 break;
 
             case TelephonyManager.PHONE_TYPE_CDMA:
@@ -298,7 +297,7 @@
 
             case TelephonyManager.PHONE_TYPE_NONE:
                 assertNull(deviceId);
-                assertHardwareId();
+                assertSerialNumber();
                 assertMacAddressReported();
                 break;
 
@@ -307,17 +306,15 @@
         }
     }
 
-    private static void assertImeiDeviceId(String deviceId) {
-        assertImeiFormat(deviceId);
-        assertImeiCheckDigit(deviceId);
-        assertReportingBodyIdentifier(deviceId, true); // Must be decimal identifier
-    }
-
-    private static void assertImeiFormat(String deviceId) {
-        // IMEI must include the check digit
-        String imeiPattern = "[0-9]{15}";
+    private static void assertGsmDeviceId(String deviceId) {
+        // IMEI may include the check digit
+        String imeiPattern = "[0-9]{14,15}";
         assertTrue("IMEI device id " + deviceId + " does not match pattern " + imeiPattern,
                 Pattern.matches(imeiPattern, deviceId));
+        if (deviceId.length() == 15) {
+            // if the ID is 15 digits, the 15th must be a check digit.
+            assertImeiCheckDigit(deviceId);
+        }
     }
 
     private static void assertImeiCheckDigit(String deviceId) {
@@ -357,19 +354,10 @@
         return sum == 0 ? 0 : 10 - sum;
     }
 
-    private static void assertReportingBodyIdentifier(String deviceId, boolean decimalIdentifier) {
-        // Check the reporting body identifier
-        int reportingBodyIdentifier = Integer.parseInt(deviceId.substring(0, 2), 16);
-        int decimalBound = 0xA0;
-        String message = String.format("%s RR %x not %s than %x",
-                decimalIdentifier ? "IMEI" : "MEID",
-                reportingBodyIdentifier,
-                decimalIdentifier ? "<" : ">=",
-                decimalBound);
-        assertEquals(message, decimalIdentifier, reportingBodyIdentifier < decimalBound);
-    }
-
     private static void assertCdmaDeviceId(String deviceId) {
+        // CDMA device IDs may either be a 14-hex-digit MEID or an
+        // 8-hex-digit ESN.  If it's an ESN, it may not be a
+        // pseudo-ESN.
         if (deviceId.length() == 14) {
             assertMeidFormat(deviceId);
         } else if (deviceId.length() == 8) {
@@ -388,29 +376,19 @@
     }
 
     private static void assertMeidFormat(String deviceId) {
-        if (deviceId.substring(0, 2).matches("99|98|97")) {
-            // MEID must NOT include the check digit.
-            String meidPattern = "(99|98|97)[0-9]{12}";
-            assertTrue("MEID device id " + deviceId + " does not match pattern " + meidPattern,
-                    Pattern.matches(meidPattern, deviceId));
-        } else {
-            // MEID must NOT include the check digit.
-            String meidPattern = "[0-9a-fA-F]{14}";
-            assertTrue("MEID device id " + deviceId + " does not match pattern " + meidPattern,
-                    Pattern.matches(meidPattern, deviceId));
-            assertReportingBodyIdentifier(deviceId, false);
-        }
+        // MEID must NOT include the check digit.
+        String meidPattern = "[0-9a-fA-F]{14}";
+        assertTrue("MEID device id " + deviceId + " does not match pattern " + meidPattern,
+                   Pattern.matches(meidPattern, deviceId));
     }
 
-    private void assertHardwareId() {
-        ContentResolver resolver = mContext.getContentResolver();
-        String hardwareId = Settings.Secure.getString(resolver, "hardware_id");
-        assertNotNull("Non-telephony devices must define a Settings.Secure 'hardware_id' property.",
-                hardwareId);
+    private void assertSerialNumber() {
+        assertNotNull("Non-telephony devices must have a Build.SERIAL number.",
+                Build.SERIAL);
         assertTrue("Hardware id must be no longer than 20 characters.",
-                hardwareId.length() <= 20);
+                Build.SERIAL.length() <= 20);
         assertTrue("Hardware id must be alphanumeric.",
-                Pattern.matches("[0-9A-Za-z]+", hardwareId));
+                Pattern.matches("[0-9A-Za-z]+", Build.SERIAL));
     }
 
     private void assertMacAddressReported() {
diff --git a/tests/tests/telephony/src/android/telephony/gsm/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/gsm/cts/SmsManagerTest.java
index 8dc6936..512e89d 100644
--- a/tests/tests/telephony/src/android/telephony/gsm/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/gsm/cts/SmsManagerTest.java
@@ -28,6 +28,7 @@
 @TestTargetClass(SmsManager.class)
 public class SmsManagerTest extends android.telephony.cts.SmsManagerTest {
 
+    @Override
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         method = "getDefault",
diff --git a/tests/tests/text/src/android/text/cts/TextUtilsTest.java b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
old mode 100644
new mode 100755
index 04f9366..0b274f5
--- a/tests/tests/text/src/android/text/cts/TextUtilsTest.java
+++ b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
@@ -98,7 +98,7 @@
         // issue 1688347, the expected result for this case does not be described
         // in the javadoc of commaEllipsize().
         assertEquals("",
-                TextUtils.commaEllipsize(text, p, textWidth - 1, "plus 1", "%d plus").toString());
+                TextUtils.commaEllipsize(text, p, textWidth - 1.4f, "plus 1", "%d plus").toString());
         // avail is long enough for only one item plus the appropriate ellipsis.
         assertEquals("long, 3 plus",
                 TextUtils.commaEllipsize(text, p, textWidth, "plus 1", "%d plus").toString());
diff --git a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
index a5dd335..6d034b9 100644
--- a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
+++ b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
@@ -37,7 +37,6 @@
     private static final long HOUR_DURATION = 2 * 60 * 60 * 1000;
     private static final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
     private long mBaseTime;
-    private Locale mDefaultLocale;
     private Context mContext;
 
     @Override
@@ -45,18 +44,6 @@
         super.setUp();
         mContext = getContext();
         mBaseTime = System.currentTimeMillis();
-        mDefaultLocale = Locale.getDefault();
-        if (!mDefaultLocale.equals(Locale.US)) {
-            Locale.setDefault(Locale.US);
-        }
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        if (!Locale.getDefault().equals(mDefaultLocale)) {
-            Locale.setDefault(mDefaultLocale);
-        }
-        super.tearDown();
     }
 
     @TestTargetNew(
@@ -65,6 +52,10 @@
         args = {int.class, int.class}
     )
     public void testGetDayOfWeekString() {
+        if (!LocaleUtils.isCurrentLocale(mContext, Locale.US)) {
+            return;
+        }
+
         assertEquals("Sunday",
                 DateUtils.getDayOfWeekString(Calendar.SUNDAY, DateUtils.LENGTH_LONG));
         assertEquals("Sun",
@@ -86,6 +77,9 @@
         args = {int.class, int.class}
     )
     public void testGetMonthString() {
+        if (!LocaleUtils.isCurrentLocale(mContext, Locale.US)) {
+            return;
+        }
         assertEquals("January", DateUtils.getMonthString(Calendar.JANUARY, DateUtils.LENGTH_LONG));
         assertEquals("Jan",
                 DateUtils.getMonthString(Calendar.JANUARY, DateUtils.LENGTH_MEDIUM));
@@ -104,6 +98,9 @@
         args = {int.class}
     )
     public void testGetAMPMString() {
+        if (!LocaleUtils.isCurrentLocale(mContext, Locale.US)) {
+            return;
+        }
         assertEquals("am", DateUtils.getAMPMString(Calendar.AM));
         assertEquals("pm", DateUtils.getAMPMString(Calendar.PM));
     }
@@ -142,6 +139,9 @@
     })
 
     public void testGetSpanString() {
+        if (!LocaleUtils.isCurrentLocale(mContext, Locale.US)) {
+            return;
+        }
         assertEquals("0 minutes ago",
                 DateUtils.getRelativeTimeSpanString(mBaseTime - MIN_DURATION).toString());
         assertEquals("in 0 minutes",
@@ -198,8 +198,7 @@
     })
     @SuppressWarnings("deprecation")
     public void testFormatMethods() {
-        if (!LocaleUtils.isSupportedLocale(mContext, Locale.US)) {
-            // Locale is set to US in setUp method.
+        if (!LocaleUtils.isCurrentLocale(mContext, Locale.US)) {
             return;
         }
 
diff --git a/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
index a7c55a1..1d6c109 100644
--- a/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
@@ -143,23 +143,6 @@
         assertSelection(-1);
         runTestOnUiThread(new Runnable() {
             public void run() {
-                mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_DOWN);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        assertSelection(END_OF_1ST_LINE);
-
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                Selection.removeSelection(mEditable);
-                mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_RIGHT);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        assertSelection(END_OF_1ST_LINE);
-
-        runTestOnUiThread(new Runnable() {
-            public void run() {
                 Selection.removeSelection(mEditable);
                 mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_UP);
             }
@@ -189,22 +172,6 @@
         runTestOnUiThread(new Runnable() {
             public void run() {
                 Selection.removeSelection(mEditable);
-                mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_DOWN);
-            }
-        });
-        assertSelection(END_OF_ALL_TEXT);
-
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                Selection.removeSelection(mEditable);
-                mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_RIGHT);
-            }
-        });
-        assertSelection(END_OF_ALL_TEXT);
-
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                Selection.removeSelection(mEditable);
                 mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_UP);
             }
         });
diff --git a/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
index 225c4b4..b3d3a39 100644
--- a/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
@@ -28,6 +28,7 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.text.InputType;
 import android.text.method.DateTimeKeyListener;
+import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.widget.TextView;
 
@@ -107,12 +108,15 @@
      * Scenario description:
      * 1. Press '1' key and check if the content of TextView becomes "1"
      * 2. Press '2' key and check if the content of TextView becomes "12"
-     * 3. Press 'a' key and check if the content of TextView becomes "12a"
-     * 4. Press an unaccepted key if it exists. and this key will not be accepted.
-     * 5. remove DateKeyListener and Press '1' key, this key will not be accepted
+     * 3. Press 'a' key if it is producible
+     * 4. Press 'p' key if it is producible
+     * 5. Press 'm' key if it is producible
+     * 6. Press an unaccepted key if it exists. and this key will not be accepted.
+     * 7. Remove DateKeyListener and Press '1' key, this key will not be accepted
      */
     public void testDateTimeKeyListener() {
         final DateTimeKeyListener dateTimeKeyListener = DateTimeKeyListener.getInstance();
+        String expectedText = "";
 
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
@@ -121,25 +125,45 @@
             }
         });
         mInstrumentation.waitForIdleSync();
-        assertEquals("", mTextView.getText().toString());
+        assertEquals(expectedText, mTextView.getText().toString());
 
         // press '1' key.
         mInstrumentation.sendStringSync("1");
-        assertEquals("1", mTextView.getText().toString());
+        expectedText += "1";
+        assertEquals(expectedText, mTextView.getText().toString());
 
         // press '2' key.
         mInstrumentation.sendStringSync("2");
-        assertEquals("12", mTextView.getText().toString());
+        expectedText += "2";
+        assertEquals(expectedText, mTextView.getText().toString());
 
-        // press 'a' key.
-        mInstrumentation.sendStringSync("a");
-        assertEquals("12a", mTextView.getText().toString());
+        // press 'a' key if producible
+        KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+        if ('a' == kcm.getMatch(KeyEvent.KEYCODE_A, DateTimeKeyListener.CHARACTERS)) {
+            expectedText += "a";
+            mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_A);
+            assertEquals(expectedText, mTextView.getText().toString());
+        }
+
+        // press 'p' key if producible
+        if ('p' == kcm.getMatch(KeyEvent.KEYCODE_P, DateTimeKeyListener.CHARACTERS)) {
+            expectedText += "p";
+            mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_P);
+            assertEquals(expectedText, mTextView.getText().toString());
+        }
+
+        // press 'm' key if producible
+        if ('m' == kcm.getMatch(KeyEvent.KEYCODE_M, DateTimeKeyListener.CHARACTERS)) {
+            expectedText += "m";
+            mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_M);
+            assertEquals(expectedText, mTextView.getText().toString());
+        }
 
         // press an unaccepted key if it exists.
         int keyCode = TextMethodUtils.getUnacceptedKeyCode(DateTimeKeyListener.CHARACTERS);
         if (-1 != keyCode) {
             sendKeys(keyCode);
-            assertEquals("12a", mTextView.getText().toString());
+            assertEquals(expectedText, mTextView.getText().toString());
         }
 
         // remove DateTimeKeyListener
@@ -150,10 +174,10 @@
             }
         });
         mInstrumentation.waitForIdleSync();
-        assertEquals("12a", mTextView.getText().toString());
+        assertEquals(expectedText, mTextView.getText().toString());
 
         mInstrumentation.sendStringSync("1");
-        assertEquals("12a", mTextView.getText().toString());
+        assertEquals(expectedText, mTextView.getText().toString());
     }
 
     private class MyDateTimeKeyListener extends DateTimeKeyListener {
diff --git a/tests/tests/text/src/android/text/method/cts/LinkMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/LinkMovementMethodTest.java
index 57c6d33..cbfc327 100644
--- a/tests/tests/text/src/android/text/method/cts/LinkMovementMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/LinkMovementMethodTest.java
@@ -129,7 +129,7 @@
         Selection.setSelection(spannable, 0, spannable.length());
 
         assertSelection(spannable, 0, spannable.length());
-        assertEquals(2, spannable.getSpans(0, spannable.length(), Object.class).length);
+        assertTrue("Expected at least 2 spans", 2 <= spannable.getSpans(0, spannable.length(), Object.class).length);
         method.onTakeFocus(null, spannable, View.FOCUS_UP);
         assertSelection(spannable, -1);
         assertEquals(1, spannable.getSpans(0, spannable.length(), Object.class).length);
@@ -141,7 +141,7 @@
         // focus forwards
         Selection.setSelection(spannable, 0, spannable.length());
         assertSelection(spannable, 0, spannable.length());
-        assertEquals(3, spannable.getSpans(0, spannable.length(), Object.class).length);
+        assertTrue("Expected at least 3 spans", 3 <= spannable.getSpans(0, spannable.length(), Object.class).length);
         method.onTakeFocus(null, spannable, View.FOCUS_RIGHT);
         assertSelection(spannable, -1);
         assertEquals(0, spannable.getSpans(0, spannable.length(), Object.class).length);
@@ -151,7 +151,7 @@
         // param direction is unknown(0)
         Selection.setSelection(spannable, 0, spannable.length());
         assertSelection(spannable, 0, spannable.length());
-        assertEquals(3, spannable.getSpans(0, spannable.length(), Object.class).length);
+        assertTrue("Expected at least 3 spans", 3 <= spannable.getSpans(0, spannable.length(), Object.class).length);
         method.onTakeFocus(null, spannable, 0);
         assertSelection(spannable, -1);
         assertEquals(0, spannable.getSpans(0, spannable.length(), Object.class).length);
@@ -576,7 +576,7 @@
         Selection.setSelection(spannable, 0, spannable.length());
 
         assertSelection(spannable, 0, spannable.length());
-        assertEquals(3, spannable.getSpans(0, spannable.length(), Object.class).length);
+        assertTrue("Expected at least 3 spans", 3 <= spannable.getSpans(0, spannable.length(), Object.class).length);
         method.initialize(null, spannable);
         assertSelection(spannable, -1);
         assertEquals(0, spannable.getSpans(0, spannable.length(), Object.class).length);
diff --git a/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java
index 5c0b9f1..d9242c5 100644
--- a/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java
@@ -37,9 +37,11 @@
 import android.widget.TextView;
 import android.widget.TextView.BufferType;
 
+
 @TestTargetClass(NumberKeyListener.class)
 public class NumberKeyListenerTest extends
         ActivityInstrumentationTestCase2<KeyListenerStubActivity> {
+
     private MockNumberKeyListener mNumberKeyListener;
     private Activity mActivity;
     private Instrumentation mInstrumentation;
@@ -74,7 +76,7 @@
     @ToBeFixed(bug = "1695243", explanation = "Android API javadocs are incomplete, " +
             "should add NPE description in javadoc.")
     public void testFilter() {
-        mNumberKeyListener = new MockNumberKeyListener();
+        mNumberKeyListener = new MockNumberKeyListener(MockNumberKeyListener.DIGITS);
         String source = "Android test";
         SpannableString dest = new SpannableString("012345");
         assertEquals("", mNumberKeyListener.filter(source, 0, source.length(),
@@ -122,12 +124,12 @@
     )
     @ToBeFixed(bug = "1695243", explanation = "Android API javadocs are incomplete.")
     public void testLookup() {
-        mNumberKeyListener = new MockNumberKeyListener();
-
+        mNumberKeyListener = new MockNumberKeyListener(MockNumberKeyListener.DIGITS);
         KeyEvent event1 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
         SpannableString str = new SpannableString("012345");
         assertEquals('0', mNumberKeyListener.lookup(event1, str));
 
+        mNumberKeyListener = new MockNumberKeyListener(MockNumberKeyListener.NOTHING);
         KeyEvent event2 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A);
         str = new SpannableString("ABCD");
         assertEquals('\0', mNumberKeyListener.lookup(event2, str));
@@ -147,7 +149,7 @@
     )
     @ToBeFixed(bug = "1695243", explanation = "Android API javadocs are incomplete.")
     public void testOk() {
-        mNumberKeyListener = new MockNumberKeyListener();
+        mNumberKeyListener = new MockNumberKeyListener(MockNumberKeyListener.DIGITS);
 
         assertTrue(mNumberKeyListener.callOk(mNumberKeyListener.getAcceptedChars(), '3'));
         assertFalse(mNumberKeyListener.callOk(mNumberKeyListener.getAcceptedChars(), 'e'));
@@ -172,7 +174,8 @@
     )
     public void testPressKey() {
         final CharSequence text = "123456";
-        final MockNumberKeyListener numberKeyListener = new MockNumberKeyListener();
+        final MockNumberKeyListener numberKeyListener =
+            new MockNumberKeyListener(MockNumberKeyListener.DIGITS);
 
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
@@ -189,7 +192,7 @@
         assertEquals("0123456", mTextView.getText().toString());
 
         // an unaccepted key if it exists.
-        int keyCode = TextMethodUtils.getUnacceptedKeyCode(MockNumberKeyListener.CHARACTERS);
+        int keyCode = TextMethodUtils.getUnacceptedKeyCode(MockNumberKeyListener.DIGITS);
         if (-1 != keyCode) {
             sendKeys(keyCode);
             // text of TextView will not be changed.
@@ -209,12 +212,21 @@
     }
 
     private static class MockNumberKeyListener extends NumberKeyListener {
-        static final char[] CHARACTERS = new char[] {'0', '1', '2',
-                '3', '4', '5', '6', '7', '8', '9'};
+
+        static final char[] DIGITS =
+                new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+        static final char[] NOTHING = new char[0];
+
+        private final char[] mAcceptedChars;
+
+        MockNumberKeyListener(char[] acceptedChars) {
+            this.mAcceptedChars = acceptedChars;
+        }
 
         @Override
         protected char[] getAcceptedChars() {
-            return CHARACTERS;
+            return mAcceptedChars;
         }
 
         @Override
diff --git a/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java
old mode 100644
new mode 100755
index 9968bda..be2eede
--- a/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java
@@ -187,7 +187,7 @@
             }
         }));
         assertTrue(mTextView.getScrollX() > previousScrollX);
-        assertEquals(rightMost, mTextView.getScrollX(), 0f);
+        assertEquals(rightMost, mTextView.getScrollX(), 1.0f);
 
         previousScrollX = mTextView.getScrollX();
         assertTrue(getActionResult(new ActionRunnerWithResult() {
diff --git a/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
index 278af6b..d087fc4 100644
--- a/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
@@ -27,6 +27,7 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.text.InputType;
 import android.text.method.TimeKeyListener;
+import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.widget.TextView;
 
@@ -100,13 +101,15 @@
      * Scenario description:
      * 1. Press '1' key and check if the content of TextView becomes "1"
      * 2. Press '2' key and check if the content of TextView becomes "12"
-     * 3. Press 'a' key and check if the content of TextView becomes "12a"
-     * 4. Press an unaccepted key if it exists and this key could not be entered.
-     * 5. Press 'm' key and check if the content of TextView becomes "12am"
-     * 6. remove TimeKeyListener, '1' key will not be accepted.
+     * 3. Press 'a' key if it is producible
+     * 4. Press 'p' key if it is producible
+     * 5. Press 'm' key if it is producible
+     * 6. Press an unaccepted key if it exists and this key could not be entered.
+     * 7. Remove TimeKeyListener, '1' key will not be accepted.
      */
     public void testTimeKeyListener() {
         final TimeKeyListener timeKeyListener = TimeKeyListener.getInstance();
+        String expectedText = "";
 
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
@@ -115,31 +118,47 @@
             }
         });
         mInstrumentation.waitForIdleSync();
-        assertEquals("", mTextView.getText().toString());
+        assertEquals(expectedText, mTextView.getText().toString());
 
         // press '1' key.
         mInstrumentation.sendStringSync("1");
-        assertEquals("1", mTextView.getText().toString());
+        expectedText += "1";
+        assertEquals(expectedText, mTextView.getText().toString());
 
         // press '2' key.
         mInstrumentation.sendStringSync("2");
+        expectedText += "2";
         assertEquals("12", mTextView.getText().toString());
 
-        // press 'a' key.
-        mInstrumentation.sendStringSync("a");
-        assertEquals("12a", mTextView.getText().toString());
+        // press 'a' key if producible
+        KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+        if ('a' == kcm.getMatch(KeyEvent.KEYCODE_A, TimeKeyListener.CHARACTERS)) {
+            expectedText += "a";
+            mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_A);
+            assertEquals(expectedText, mTextView.getText().toString());
+        }
+
+        // press 'p' key if producible
+        if ('p' == kcm.getMatch(KeyEvent.KEYCODE_P, TimeKeyListener.CHARACTERS)) {
+            expectedText += "p";
+            mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_P);
+            assertEquals(expectedText, mTextView.getText().toString());
+        }
+
+        // press 'm' key if producible
+        if ('m' == kcm.getMatch(KeyEvent.KEYCODE_M, TimeKeyListener.CHARACTERS)) {
+            expectedText += "m";
+            mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_M);
+            assertEquals(expectedText, mTextView.getText().toString());
+        }
 
         // press an unaccepted key if it exists.
         int keyCode = TextMethodUtils.getUnacceptedKeyCode(TimeKeyListener.CHARACTERS);
         if (-1 != keyCode) {
             sendKeys(keyCode);
-            assertEquals("12a", mTextView.getText().toString());
+            assertEquals(expectedText, mTextView.getText().toString());
         }
 
-        // press 'm' key.
-        mInstrumentation.sendStringSync("m");
-        assertEquals("12am", mTextView.getText().toString());
-
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
                 mTextView.setKeyListener(null);
@@ -150,7 +169,7 @@
 
         // press '1' key.
         mInstrumentation.sendStringSync("1");
-        assertEquals("12am", mTextView.getText().toString());
+        assertEquals(expectedText, mTextView.getText().toString());
     }
 
     private class MyTimeKeyListener extends TimeKeyListener {
diff --git a/tests/tests/text/src/android/text/method/cts/TouchTest.java b/tests/tests/text/src/android/text/method/cts/TouchTest.java
old mode 100644
new mode 100755
index e5275af..7217db7
--- a/tests/tests/text/src/android/text/method/cts/TouchTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TouchTest.java
@@ -39,7 +39,9 @@
     private Activity mActivity;
     private static final String LONG_TEXT = "Scrolls the specified widget to the specified " +
             "coordinates, except constrains the X scrolling position to the horizontal regions " +
-            "of the text that will be visible after scrolling to the specified Y position.";
+            "of the text that will be visible after scrolling to the specified Y position." +
+            "This is the description of the test.";
+
     private boolean mReturnFromTouchEvent;
 
     public TouchTest() {
@@ -96,7 +98,7 @@
             }
         });
         getInstrumentation().waitForIdleSync();
-        assertEquals(width - tv.getWidth(), tv.getScrollX());
+        assertEquals(width - tv.getWidth(), tv.getScrollX(), 1.0f);
         assertEquals(5, tv.getScrollY());
 
         runTestOnUiThread(new Runnable() {
@@ -105,7 +107,7 @@
             }
         });
         getInstrumentation().waitForIdleSync();
-        assertEquals(width - tv.getWidth(), tv.getScrollX());
+        assertEquals(width - tv.getWidth(), tv.getScrollX(), 1.0f);
         assertEquals(5, tv.getScrollY());
     }
 
diff --git a/tests/tests/util/src/android/util/cts/EventLogTest.java b/tests/tests/util/src/android/util/cts/EventLogTest.java
index c803174..318b010 100644
--- a/tests/tests/util/src/android/util/cts/EventLogTest.java
+++ b/tests/tests/util/src/android/util/cts/EventLogTest.java
@@ -18,9 +18,12 @@
 
 import android.os.Process;
 import android.util.EventLog;
+import android.util.EventLog.Event;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 
 import junit.framework.TestCase;
 
@@ -30,13 +33,14 @@
     private static final int E_TAG = 2718;
 
     public void testWriteEvent() throws Exception {
-        long t0 = getTime();
+        long markerData = System.currentTimeMillis();
+        EventLog.writeEvent(ANSWER_TAG, markerData);
         EventLog.writeEvent(ANSWER_TAG, 12345);
         EventLog.writeEvent(ANSWER_TAG, 23456L);
         EventLog.writeEvent(ANSWER_TAG, "Test");
         EventLog.writeEvent(ANSWER_TAG, 12345, 23456L, "Test");
 
-        ArrayList<EventLog.Event> events = getEventsSince(t0, new int[] {ANSWER_TAG});
+        List<EventLog.Event> events = getEventsAfterMarker(markerData, ANSWER_TAG);
         assertEquals(4, events.size());
         assertEquals(ANSWER_TAG, events.get(0).getTag());
         assertEquals(12345, events.get(0).getData());
@@ -57,7 +61,8 @@
         Object[] longArray = new Object[1000];
         for (int i = 0; i < 1000; i++) longArray[i] = 12345;
 
-        long t0 = getTime();
+        Long markerData = System.currentTimeMillis();
+        EventLog.writeEvent(ANSWER_TAG, markerData);
         EventLog.writeEvent(ANSWER_TAG, longString.toString());
         EventLog.writeEvent(ANSWER_TAG, "hi", longString.toString());
         EventLog.writeEvent(ANSWER_TAG, 12345, longString.toString());
@@ -65,7 +70,7 @@
         EventLog.writeEvent(ANSWER_TAG, longString.toString(), longString.toString());
         EventLog.writeEvent(ANSWER_TAG, longArray);
 
-        ArrayList<EventLog.Event> events = getEventsSince(t0, new int[] {ANSWER_TAG});
+        List<Event> events = getEventsAfterMarker(markerData, ANSWER_TAG);
         assertEquals(6, events.size());
 
         // subtract: log header, type byte, final newline
@@ -105,11 +110,12 @@
     }
 
     public void testWriteNullEvent() throws Exception {
-        long t0 = getTime();
+        Long markerData = System.currentTimeMillis();
+        EventLog.writeEvent(ANSWER_TAG, markerData);
         EventLog.writeEvent(ANSWER_TAG, (String) null);
         EventLog.writeEvent(ANSWER_TAG, 12345, (String) null);
 
-        ArrayList<EventLog.Event> events = getEventsSince(t0, new int[] {ANSWER_TAG});
+        List<EventLog.Event> events = getEventsAfterMarker(markerData, ANSWER_TAG);
         assertEquals(2, events.size());
         assertEquals("NULL", events.get(0).getData());
 
@@ -120,31 +126,65 @@
     }
 
     public void testReadEvents() throws Exception {
-        long t0 = getTime();
-        EventLog.writeEvent(ANSWER_TAG, 0);
-        long t1 = getTime();
-        EventLog.writeEvent(PI_TAG, "1");
-        long t2 = getTime();
-        EventLog.writeEvent(E_TAG, 2);
-        long t3 = getTime();
+        Long markerData = System.currentTimeMillis();
+        EventLog.writeEvent(ANSWER_TAG, markerData);
 
-        // Exclude E_TAG
-        ArrayList<EventLog.Event> events = getEventsSince(t0, new int[] {ANSWER_TAG, PI_TAG});
+        Long data0 = markerData + 1;
+        EventLog.writeEvent(ANSWER_TAG, data0);
+
+        Long data1 = data0 + 1;
+        EventLog.writeEvent(PI_TAG, data1);
+
+        Long data2 = data1 + 1;
+        EventLog.writeEvent(E_TAG, data2);
+
+        List<Event> events = getEventsAfterMarker(markerData, ANSWER_TAG, PI_TAG, E_TAG);
+        assertEquals(3, events.size());
+        assertEvent(events.get(0), ANSWER_TAG, data0);
+        assertEvent(events.get(1), PI_TAG, data1);
+        assertEvent(events.get(2), E_TAG, data2);
+
+        events = getEventsAfterMarker(markerData, ANSWER_TAG, E_TAG);
         assertEquals(2, events.size());
+        assertEvent(events.get(0), ANSWER_TAG, data0);
+        assertEvent(events.get(1), E_TAG, data2);
 
-        assertEquals(Process.myPid(), events.get(0).getProcessId());
-        assertEquals(Process.myTid(), events.get(0).getThreadId());
-        assertTrue(events.get(0).getTimeNanos() >= t0 * 1000000L);
-        assertTrue(events.get(0).getTimeNanos() <= t1 * 1000000L);
-        assertEquals(ANSWER_TAG, events.get(0).getTag());
-        assertEquals(0, events.get(0).getData());
+        events = getEventsAfterMarker(markerData, ANSWER_TAG);
+        assertEquals(1, events.size());
+        assertEvent(events.get(0), ANSWER_TAG, data0);
+    }
 
-        assertEquals(Process.myPid(), events.get(1).getProcessId());
-        assertEquals(Process.myTid(), events.get(1).getThreadId());
-        assertTrue(events.get(1).getTimeNanos() >= t1 * 1000000L);
-        assertTrue(events.get(1).getTimeNanos() <= t2 * 1000000L);
-        assertEquals(PI_TAG, events.get(1).getTag());
-        assertEquals("1", events.get(1).getData());
+    /** Return elements after and the event that has the marker data and matching tag. */
+    private List<Event> getEventsAfterMarker(Object marker, int... tags) throws IOException {
+        List<Event> events = new ArrayList<Event>();
+        EventLog.readEvents(tags, events);
+
+        for (Iterator<Event> itr = events.iterator(); itr.hasNext(); ) {
+            Event event = itr.next();
+            itr.remove();
+            if (marker.equals(event.getData())) {
+                break;
+            }
+        }
+
+        assertEventTimes(events);
+
+        return events;
+    }
+
+    private void assertEvent(Event event, int expectedTag, Object expectedData) {
+        assertEquals(Process.myPid(), event.getProcessId());
+        assertEquals(Process.myTid(), event.getThreadId());
+        assertEquals(expectedTag, event.getTag());
+        assertEquals(expectedData, event.getData());
+    }
+
+    private void assertEventTimes(List<Event> events) {
+        for (int i = 0; i + 1 < events.size(); i++) {
+            long time = events.get(i).getTimeNanos();
+            long nextTime = events.get(i).getTimeNanos();
+            assertTrue(time <= nextTime);
+        }
     }
 
     public void testGetTagName() throws Exception {
@@ -160,23 +200,4 @@
         assertEquals(E_TAG, EventLog.getTagCode("e"));
         assertEquals(-1, EventLog.getTagCode("does_not_exist"));
     }
-
-    private long getTime() throws InterruptedException {
-        // The precision of currentTimeMillis is poor compared to event timestamps
-        Thread.sleep(20);
-        return System.currentTimeMillis() - 10;
-    }
-
-    private ArrayList<EventLog.Event> getEventsSince(long since, int[] tags) throws IOException {
-        ArrayList<EventLog.Event> tmp = new ArrayList<EventLog.Event>();
-        EventLog.readEvents(tags, tmp);
-
-        ArrayList<EventLog.Event> out = new ArrayList<EventLog.Event>();
-        for (EventLog.Event event : tmp) {
-            if (event.getTimeNanos() / 1000000 >= since) {
-                out.add(event);
-            }
-        }
-        return out;
-    }
 }
diff --git a/tests/tests/util/src/android/util/cts/TimeUtilsTest.java b/tests/tests/util/src/android/util/cts/TimeUtilsTest.java
index 8a720ef..47e37a0 100644
--- a/tests/tests/util/src/android/util/cts/TimeUtilsTest.java
+++ b/tests/tests/util/src/android/util/cts/TimeUtilsTest.java
@@ -21,8 +21,6 @@
 import dalvik.annotation.TestTargetNew;
 import dalvik.annotation.TestTargets;
 
-import org.apache.harmony.luni.internal.util.ZoneInfoDB;
-
 import android.util.TimeUtils;
 
 import java.util.Calendar;
@@ -473,20 +471,6 @@
         }
     }
 
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            notes = "Test method: getTimeZoneDatabaseVersion",
-            method = "getTimeZoneDatabaseVersion",
-            args = {}
-        )
-    })
-    public void testGetTimeZoneDatabaseVersion(){
-        String version = TimeUtils.getTimeZoneDatabaseVersion();
-        assertTrue(5 == version.length());
-        assertEquals(ZoneInfoDB.getVersion(), version);
-    }
-
     private static TimeZone guessTimeZone(Calendar c, String country) {
         return TimeUtils.getTimeZone(c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET),
                                      c.get(Calendar.DST_OFFSET) != 0,
diff --git a/tests/tests/view/src/android/view/cts/MotionEventTest.java b/tests/tests/view/src/android/view/cts/MotionEventTest.java
index beece1e..e4df594 100644
--- a/tests/tests/view/src/android/view/cts/MotionEventTest.java
+++ b/tests/tests/view/src/android/view/cts/MotionEventTest.java
@@ -488,5 +488,13 @@
         assertEquals(1, mMotionEvent2.getHistorySize());
 
         mMotionEvent2.recycle();
+        
+        try {
+            mMotionEvent2.recycle();
+            fail("recycle() should throw an exception when the event has already been recycled.");
+        } catch (RuntimeException ex) {
+        }
+        
+        mMotionEvent2 = null; // since it was recycled, don't try to recycle again in tear down
     }
 }
diff --git a/tests/tests/view/src/android/view/cts/TouchDelegateTest.java b/tests/tests/view/src/android/view/cts/TouchDelegateTest.java
index f82b903..0a78d1f 100644
--- a/tests/tests/view/src/android/view/cts/TouchDelegateTest.java
+++ b/tests/tests/view/src/android/view/cts/TouchDelegateTest.java
@@ -16,6 +16,11 @@
 
 package android.view.cts;
 
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.app.cts.MockActivity;
@@ -25,45 +30,22 @@
 import android.view.MotionEvent;
 import android.view.TouchDelegate;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.LinearLayout;
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestTargets;
 
 @TestTargetClass(TouchDelegate.class)
 public class TouchDelegateTest extends ActivityInstrumentationTestCase2<MockActivity> {
     private static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
     private static final int ACTION_DOWN = MotionEvent.ACTION_DOWN;
-    private static final int ACTION_UP = MotionEvent.ACTION_UP;
-    private static final int ACTION_CANCEL = MotionEvent.ACTION_CANCEL;
-    private static final int ACTION_MOVE = MotionEvent.ACTION_MOVE;
 
-    private ViewConfiguration mViewConfig;
     private Activity mActivity;
     private Instrumentation mInstrumentation;
-    private TouchDelegate mTouchDelegate;
     private Button mButton;
     private Rect mRect;
 
     private int mXInside;
     private int mYInside;
-    private int mXOutside;
-    private int mYOutside;
-    private int mScaledTouchSlop;
-
-    private MotionEvent mActionDownInside;
-    private MotionEvent mActionDownOutside;
-    private MotionEvent mActionUpInside;
-    private MotionEvent mActionUpOutside;
-    private MotionEvent mActionMoveInside;
-    private MotionEvent mActionMoveOutside;
-    private MotionEvent mActionCancelInside;
-    private MotionEvent mActionCancelOutside;
 
     private Exception mException;
 
@@ -76,7 +58,6 @@
         super.setUp();
         mActivity = getActivity();
         mInstrumentation = getInstrumentation();
-        mViewConfig = ViewConfiguration.get(mActivity);
 
         mButton = new Button(mActivity);
         mActivity.runOnUiThread(new Runnable() {
@@ -99,38 +80,11 @@
         int bottom = mButton.getBottom();
         mXInside = (mButton.getLeft() + right) / 3;
         mYInside = (mButton.getTop() + bottom) / 3;
-        mScaledTouchSlop = mViewConfig.getScaledTouchSlop() << 1;
-        mXOutside = right + mScaledTouchSlop;
-        mYOutside = bottom + mScaledTouchSlop;
 
         mRect = new Rect();
         mButton.getHitRect(mRect);
     }
 
-    private void init() {
-        mTouchDelegate = new TouchDelegate(mRect, mButton);
-
-        mActionDownInside = MotionEvent.obtain(0, 0, ACTION_DOWN, mXInside, mYInside, 0);
-        mActionDownOutside = MotionEvent.obtain(0, 0, ACTION_DOWN, mXOutside, mYOutside, 0);
-        mActionUpInside = MotionEvent.obtain(0, 0, ACTION_UP, mXInside, mYInside, 0);
-        mActionUpOutside = MotionEvent.obtain(0, 0, ACTION_UP, mXOutside, mYOutside, 0);
-        mActionMoveInside = MotionEvent.obtain(0, 0, ACTION_MOVE, mXInside, mYInside, 0);
-        mActionMoveOutside = MotionEvent.obtain(0, 0, ACTION_MOVE, mXOutside, mYOutside, 0);
-        mActionCancelInside = MotionEvent.obtain(0, 0, ACTION_CANCEL, mXInside, mYInside, 0);
-        mActionCancelOutside = MotionEvent.obtain(0, 0, ACTION_CANCEL, mXOutside, mYOutside, 0);
-    }
-
-    private void clear() {
-        mActionDownInside.recycle();
-        mActionDownOutside.recycle();
-        mActionUpInside.recycle();
-        mActionUpOutside.recycle();
-        mActionMoveInside.recycle();
-        mActionMoveOutside.recycle();
-        mActionCancelInside.recycle();
-        mActionCancelOutside.recycle();
-    }
-
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
@@ -154,59 +108,6 @@
         assertFalse(touchDelegate.mOnTouchEventCalled);
         view.onTouchEvent(MotionEvent.obtain(0, 0, ACTION_DOWN, mXInside, mYInside, 0));
         assertTrue(touchDelegate.mOnTouchEventCalled);
-
-    }
-
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "onTouchEvent",
-        args = {MotionEvent.class}
-    )
-    @UiThreadTest
-    @BrokenTest("Will fail in batch mode but can pass if only run this TestCase")
-    public void testOn() {
-
-        init();
-        assertTrue(mTouchDelegate.onTouchEvent(mActionDownInside));
-        clear();
-
-        init();
-        assertFalse(mTouchDelegate.onTouchEvent(mActionDownOutside));
-        clear();
-
-        init();
-        assertTrue(mTouchDelegate.onTouchEvent(mActionDownInside));
-        assertTrue(mTouchDelegate.onTouchEvent(mActionUpInside));
-        clear();
-
-        init();
-        assertFalse(mTouchDelegate.onTouchEvent(mActionUpInside));
-        clear();
-
-        init();
-        assertTrue(mTouchDelegate.onTouchEvent(mActionDownInside));
-        assertTrue(mTouchDelegate.onTouchEvent(mActionUpOutside));
-        clear();
-
-        init();
-        assertFalse(mTouchDelegate.onTouchEvent(mActionMoveInside));
-        clear();
-
-        init();
-        assertTrue(mTouchDelegate.onTouchEvent(mActionDownInside));
-        assertTrue(mTouchDelegate.onTouchEvent(mActionMoveInside));
-        clear();
-
-        init();
-        assertTrue(mTouchDelegate.onTouchEvent(mActionDownInside));
-        assertTrue(mTouchDelegate.onTouchEvent(mActionMoveOutside));
-        clear();
-
-        init();
-        assertTrue(mTouchDelegate.onTouchEvent(mActionDownInside));
-        assertTrue(mTouchDelegate.onTouchEvent(mActionCancelInside));
-        assertFalse(mTouchDelegate.onTouchEvent(mActionUpInside));
-        clear();
     }
 
     class MockTouchDelegate extends TouchDelegate {
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 74a5f8c..87b3eae 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -558,26 +558,6 @@
         assertEquals(-1, view.getBaseline());
     }
 
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "computeScroll",
-        args = {}
-    )
-    public void testComputeScroll() throws Throwable {
-        final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
-        assertTrue(view.hasCalledComputeScroll());
-
-        view.reset();
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                view.requestLayout();
-                view.invalidate();
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        assertTrue(view.hasCalledComputeScroll());
-    }
-
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
@@ -2730,8 +2710,7 @@
         });
 
         assertFalse(view.hasCalledOnKeyMultiple());
-        KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_ENTER);
-        getInstrumentation().sendKeySync(keyEvent);
+        view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_ENTER));
         assertTrue(view.hasCalledOnKeyMultiple());
     }
 
@@ -4674,6 +4653,10 @@
             mHasChildDrawableStateChanged = false;
             mHasBroughtChildToFront = false;
         }
+
+        public void childOverlayStateChanged(View child) {
+
+        }
     }
 
     private final class OnCreateContextMenuListenerImpl implements OnCreateContextMenuListener {
diff --git a/tests/tests/view/src/android/view/cts/WindowTest.java b/tests/tests/view/src/android/view/cts/WindowTest.java
index c851a19..88edecf 100755
--- a/tests/tests/view/src/android/view/cts/WindowTest.java
+++ b/tests/tests/view/src/android/view/cts/WindowTest.java
@@ -39,12 +39,14 @@
 import android.util.DisplayMetrics;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.Gravity;
+import android.view.InputQueue;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
+import android.view.SurfaceHolder;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
@@ -1253,6 +1255,14 @@
         public void setDefaultWindowFormat(int format) {
             super.setDefaultWindowFormat(format);
         }
+        
+        @Override
+        public void takeSurface(SurfaceHolder.Callback2 callback) {
+        }
+        
+        @Override
+        public void takeInputQueue(InputQueue.Callback callback) {
+        }
     }
 
     private class MockWindowCallback implements Window.Callback {
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java b/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
index 896e99f..bdee05a 100644
--- a/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
+++ b/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
@@ -97,6 +97,11 @@
             args = {int.class, int.class}
         ),
         @TestTargetNew(
+                level = TestLevel.COMPLETE,
+                method = "getSelectedText",
+                args = {int.class}
+            ),
+        @TestTargetNew(
             level = TestLevel.COMPLETE,
             method = "getCursorCapsMode",
             args = {int.class}
@@ -127,6 +132,11 @@
             args = {CharSequence.class, int.class}
         ),
         @TestTargetNew(
+                level = TestLevel.COMPLETE,
+                method = "setComposingRegion",
+                args = {int.class, int.class}
+            ),
+        @TestTargetNew(
             level = TestLevel.COMPLETE,
             method = "sendKeyEvent",
             args = {KeyEvent.class}
@@ -184,6 +194,10 @@
         assertTrue(inputConnection.isSetComposingTextCalled);
         wrapper.setSelection(0, 10);
         assertTrue(inputConnection.isSetSelectionCalled);
+        wrapper.getSelectedText(0);
+        assertTrue(inputConnection.isGetSelectedTextCalled);
+        wrapper.setComposingRegion(0, 3);
+        assertTrue(inputConnection.isSetComposingRegionCalled);
     }
 
     private class MockInputConnection implements InputConnection {
@@ -198,12 +212,14 @@
         public boolean isGetExtractedTextCalled;
         public boolean isGetTextAfterCursorCalled;
         public boolean isGetTextBeforeCursorCalled;
+        public boolean isGetSelectedTextCalled;
         public boolean isPerformContextMenuActionCalled;
         public boolean isPerformEditorActionCalled;
         public boolean isPerformPrivateCommandCalled;
         public boolean isReportFullscreenModeCalled;
         public boolean isSendKeyEventCalled;
         public boolean isSetComposingTextCalled;
+        public boolean isSetComposingRegionCalled;
         public boolean isSetSelectionCalled;
 
         public boolean beginBatchEdit() {
@@ -261,6 +277,11 @@
             return null;
         }
 
+        public CharSequence getSelectedText(int flags) {
+            isGetSelectedTextCalled = true;
+            return null;
+        }
+
         public boolean performContextMenuAction(int id) {
             isPerformContextMenuActionCalled = true;
             return false;
@@ -291,6 +312,11 @@
             return false;
         }
 
+        public boolean setComposingRegion(int start, int end) {
+            isSetComposingRegionCalled = true;
+            return false;
+        }
+
         public boolean setSelection(int start, int end) {
             isSetSelectionCalled = true;
             return false;
diff --git a/tests/tests/webkit/src/android/webkit/cts/CacheManager_CacheResultTest.java b/tests/tests/webkit/src/android/webkit/cts/CacheManager_CacheResultTest.java
old mode 100644
new mode 100755
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
index 795ed61..cf20217 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
@@ -41,7 +41,7 @@
     private WebIconDatabase mIconDb;
 
     public WebChromeClientTest() {
-        super("com.android.cts.stub", WebViewStubActivity.class);
+        super(WebViewStubActivity.class);
     }
 
     @Override
@@ -72,7 +72,7 @@
             args = {WebView.class, int.class}
         )
     })
-    public void testOnProgressChanged() throws InterruptedException {
+    public void testOnProgressChanged() {
         final MockWebChromeClient webChromeClient = new MockWebChromeClient();
         mWebView.setWebChromeClient(webChromeClient);
 
@@ -119,15 +119,21 @@
             args = {WebView.class, Bitmap.class}
         )
     })
-    public void testOnReceivedIcon() throws Exception {
+    public void testOnReceivedIcon() throws Throwable {
         final MockWebChromeClient webChromeClient = new MockWebChromeClient();
         mWebView.setWebChromeClient(webChromeClient);
 
-        WebIconDatabase mIconDb = WebIconDatabase.getInstance();
-        String dbPath = getActivity().getFilesDir().toString() + "/icons";
-        mIconDb.open(dbPath);
-        Thread.sleep(3000);
-        mIconDb.removeAllIcons();
+        runTestOnUiThread(new Runnable() {
+
+            @Override
+            public void run() {
+                // getInstance must run on the UI thread
+                WebIconDatabase mIconDb = WebIconDatabase.getInstance();
+                String dbPath = getActivity().getFilesDir().toString() + "/icons";
+                mIconDb.open(dbPath);
+                mIconDb.removeAllIcons();
+            }
+        });
 
         assertFalse(webChromeClient.hadOnReceivedIcon());
 
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java b/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java
index 016d566..71ba504 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java
@@ -104,42 +104,6 @@
         assertTrue(firstId != secondId);
     }
 
-    @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getOriginalUrl",
-            args = {}
-    )
-    @ToBeFixed(explanation = "History item does not have the original URL set after a redirect.")
-    @BrokenTest(value = "Bug 2121787: Test times out on the host side. Not 100% reproducible.")
-    public void testRedirect() throws InterruptedException {
-        final WebView view = getActivity().getWebView();
-        view.setWebChromeClient(new WebChromeClient());
-        // set the web view client so that redirects are loaded in the WebView itself
-        view.setWebViewClient(new WebViewClient());
-        WebBackForwardList list = view.copyBackForwardList();
-        assertEquals(0, list.getSize());
-
-        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        String redirect = mWebServer.getRedirectingAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        assertLoadUrlSuccessfully(view, redirect);
-        // wait for the redirect to take place
-        new DelayedCheck(10000) {
-            @Override
-            protected boolean check() {
-                WebBackForwardList list = view.copyBackForwardList();
-                return list.getSize() >= 1;
-            }
-        }.run();
-        list = view.copyBackForwardList();
-        assertEquals(1, list.getSize());
-        WebHistoryItem item = list.getCurrentItem();
-        assertNotNull(item);
-        assertEquals(url, item.getUrl());
-        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, item.getTitle());
-        // To be fixed: item.getOriginalUrl() returns null
-        // assertEquals(redirect, item.getOriginalUrl());
-    }
-
     private void assertLoadUrlSuccessfully(final WebView view, String url) {
         view.loadUrl(url);
         // wait for the page load to complete
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebIconDatabaseTest.java b/tests/tests/webkit/src/android/webkit/cts/WebIconDatabaseTest.java
deleted file mode 100644
index 64b5204..0000000
--- a/tests/tests/webkit/src/android/webkit/cts/WebIconDatabaseTest.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2009 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.webkit.cts;
-
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestTargets;
-
-import android.graphics.Bitmap;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.view.animation.cts.DelayedCheck;
-import android.webkit.WebIconDatabase;
-import android.webkit.WebView;
-
-import java.io.File;
-
-@TestTargetClass(android.webkit.WebIconDatabase.class)
-public class WebIconDatabaseTest extends
-                 ActivityInstrumentationTestCase2<WebViewStubActivity> {
-    private static final long ICON_FETCH_TIMEOUT = 15000;
-    private static final String DATA_FOLDER = "/webkittest/";
-    private String mFilePath;
-    private WebView mWebView;
-    private CtsTestServer mWebServer;
-
-    /**
-     * Instantiates a new text view test.
-     */
-    public WebIconDatabaseTest() {
-        super("com.android.cts.stub", WebViewStubActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        WebViewStubActivity activity = (WebViewStubActivity) getActivity();
-        mFilePath = activity.getFilesDir().toString() + DATA_FOLDER;
-        clearDatabasePath();
-
-        mWebView = activity.getWebView();
-        mWebView.clearCache(true);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        clearDatabasePath();
-        if (mWebServer != null) {
-            mWebServer.shutdown();
-        }
-        super.tearDown();
-    }
-
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "open",
-            args = {String.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "close",
-            args = {}
-        )
-    })
-    @BrokenTest(value="intermittently fails bug 2250024")
-    public void testOpen() {
-        final WebIconDatabase webIconDatabase = WebIconDatabase.getInstance();
-
-        final File path = new File(mFilePath);
-        // To assure no files under the directory
-        assertNull(path.listFiles());
-        // open() should create and open database file for storing icon related datum.
-        webIconDatabase.open(mFilePath);
-
-        // Need to wait for a moment, let the internal Handler complete the operation
-        new DelayedCheck(10000) {
-            @Override
-            protected boolean check() {
-                return path.listFiles() != null;
-            }
-        }.run();
-
-        assertTrue(path.listFiles().length > 0);
-
-        webIconDatabase.close();
-    }
-
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "getInstance",
-        args = {}
-    )
-    @UiThreadTest
-    public void testGetInstance() {
-        WebIconDatabase webIconDatabase1 = WebIconDatabase.getInstance();
-        WebIconDatabase webIconDatabase2 = WebIconDatabase.getInstance();
-
-        assertSame(webIconDatabase1, webIconDatabase2);
-    }
-
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "retainIconForPageUrl",
-            args = {String.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "requestIconForPageUrl",
-            args = {String.class, WebIconDatabase.IconListener.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "releaseIconForPageUrl",
-            args = {String.class}
-        )
-    })
-    @BrokenTest(value="intermittently fails bug 2250024")
-    public void testRetainIconForPageUrl() throws Exception {
-        final WebIconDatabase webIconDatabase = WebIconDatabase.getInstance();
-        webIconDatabase.open(mFilePath);
-
-        mWebServer = new CtsTestServer(getActivity());
-        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        assertLoadUrlSuccessfully(mWebView, url);
-
-        MyIconListener listener = new MyIconListener();
-
-        webIconDatabase.retainIconForPageUrl(url);
-
-        webIconDatabase.requestIconForPageUrl(url, listener);
-
-        listener.waitForIcon(ICON_FETCH_TIMEOUT);
-        assertTrue(listener.hasReceivedStatus());
-        assertNotNull(listener.getIcon());
-
-        // release the icon.
-        webIconDatabase.releaseIconForPageUrl(url);
-
-        listener = new MyIconListener();
-        webIconDatabase.requestIconForPageUrl(url, listener);
-
-        listener.waitForIcon(ICON_FETCH_TIMEOUT);
-
-        assertTrue(listener.hasReceivedStatus());
-        assertNotNull(listener.getIcon());
-
-        webIconDatabase.close();
-    }
-
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "removeAllIcons",
-        args = {}
-    )
-    @BrokenTest(value="intermittently fails bug 2250024")
-    public void testRemoveAllIcons() throws Exception {
-        final WebIconDatabase webIconDatabase = WebIconDatabase.getInstance();
-        webIconDatabase.open(mFilePath);
-
-        mWebServer = new CtsTestServer(getActivity());
-        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        assertLoadUrlSuccessfully(mWebView, url);
-
-        MyIconListener listener = new MyIconListener();
-
-        webIconDatabase.retainIconForPageUrl(url);
-
-        webIconDatabase.requestIconForPageUrl(url, listener);
-
-        listener.waitForIcon(ICON_FETCH_TIMEOUT);
-        assertTrue(listener.hasReceivedStatus());
-        assertNotNull(listener.getIcon());
-
-        // remove all icons.
-        webIconDatabase.removeAllIcons();
-        
-        listener = new MyIconListener();
-        webIconDatabase.requestIconForPageUrl(url, listener);
-
-        listener.waitForIcon(ICON_FETCH_TIMEOUT);
-
-        assertFalse(listener.hasReceivedStatus());
-        assertNull(listener.getIcon());
-
-        webIconDatabase.close();
-    }
-
-    private static class MyIconListener implements WebIconDatabase.IconListener {
-        private Bitmap mIcon;
-        private String mUrl;
-        private boolean mHasReceivedIcon = false;
-
-        public synchronized void onReceivedIcon(String url, Bitmap icon) {
-            mHasReceivedIcon = true;
-            mIcon = icon;
-            mUrl = url;
-            notifyAll();
-        }
-
-        public synchronized void waitForIcon(long timeout) throws InterruptedException {
-            if (!mHasReceivedIcon) {
-                wait(timeout);
-            }
-        }
-
-        public boolean hasReceivedStatus() {
-            return mHasReceivedIcon;
-        }
-
-        public Bitmap getIcon() {
-            return mIcon;
-        }
-
-        public String getUrl() {
-            return mUrl;
-        }
-    }
-
-    private void clearDatabasePath() throws InterruptedException {
-        File path = new File(mFilePath);
-        if (path.exists()) {
-            // FIXME: WebIconDatabase.close() is asynchronous, so some files may still be in use
-            // after we return. Wait some time for the files to be closed.
-            Thread.sleep(1000);
-            File[] files = path.listFiles();
-            if (files != null) {
-                for (int i = 0; i < files.length; i++) {
-                    assertTrue(files[i].delete());
-                }
-            }
-            path.delete();
-        }
-    }
-
-    private void assertLoadUrlSuccessfully(final WebView view, String url) {
-        view.loadUrl(url);
-        new DelayedCheck(10000) {
-            @Override
-            protected boolean check() {
-                return view.getProgress() == 100;
-            }
-        }.run();
-    }
-}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 435fc3f..b8f6059 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -665,12 +665,14 @@
         String url = TestHtmlConstants.EMBEDDED_IMG_URL;
         String ext = MimeTypeMap.getFileExtensionFromUrl(url);
         loadAssetUrl(url);
+        Thread.sleep(1000);
         assertFalse(mWebServer.getLastRequestUrl().endsWith(ext));
 
         mWebView.clearCache(true);
         mSettings.setLoadsImagesAutomatically(false);
         assertFalse(mSettings.getLoadsImagesAutomatically());
         loadAssetUrl(url);
+        Thread.sleep(1000);
         assertTrue(mWebServer.getLastRequestUrl().endsWith(ext));
     }
 
@@ -996,6 +998,37 @@
         assertTrue(mSettings.getBuiltInZoomControls());
     }
 
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setAppCacheEnabled",
+            args = {}
+        )
+    })
+    public void testSetAppCacheEnabled() throws Exception {
+        // Tests that when AppCache is enabled and used, but the database path
+        // is not set or is set to an inaccessible path, the WebView does not crash.
+        startWebServer();
+        String url = mWebServer.getAppCacheUrl();
+        mSettings.setAppCacheEnabled(true);
+        mSettings.setJavaScriptEnabled(true);
+
+        mWebView.loadUrl(url);
+        new DelayedCheck(10000) {
+            protected boolean check() {
+                return mWebView.getTitle().equals("Done");
+            }
+        }.run();
+
+        mSettings.setAppCachePath("/data/foo");
+        mWebView.loadUrl(url);
+        new DelayedCheck(10000) {
+            protected boolean check() {
+                return mWebView.getTitle().equals("Done");
+            }
+        }.run();
+    }
+
     /**
      * Starts the internal web server. The server will be shut down automatically
      * during tearDown().
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
old mode 100644
new mode 100755
index 554acd8..6e0be6c
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -908,14 +908,21 @@
         args = {String.class},
         notes = "Cannot check highlighting"
     )
-    @UiThreadTest
-    public void testFindAll() throws InterruptedException {
-        String p = "<p>Find all instances of find on the page and highlight them.</p>";
+    public void testFindAll() throws Throwable {
+        getActivity().runOnUiThread(new Runnable() {
+            public void run() {
+                String p = "<p>Find all instances of find on the page and highlight them.</p>";
 
-        mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", "UTF-8");
+                mWebView.loadData("<html><body>" + p +"</body></html>", "text/html", "UTF-8");
+            }
+        });
         waitForLoadComplete(mWebView, TEST_TIMEOUT);
 
-        assertEquals(2, mWebView.findAll("find"));
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                assertEquals(2, mWebView.findAll("find"));
+            }
+        });
     }
 
     @TestTargets({
@@ -1352,39 +1359,6 @@
 
     @TestTargetNew(
         level = TestLevel.COMPLETE,
-        method = "clearFormData",
-        args = {}
-    )
-    @BrokenTest(value = "Causes the process to crash some time after test completion.")
-    public void testClearFormData() throws Throwable {
-        String form = "<form><input type=\"text\" name=\"testClearFormData\"></form>";
-        mWebView.loadData("<html><body>" + form + "</body></html>", "text/html", "UTF-8");
-        waitForLoadComplete(mWebView, TEST_TIMEOUT);
-        moveFocusDown();
-        getInstrumentation().sendStringSync("test");
-        sendKeys(KeyEvent.KEYCODE_ENTER);
-
-        mWebView.reload();
-        waitForLoadComplete(mWebView, TEST_TIMEOUT);
-        moveFocusDown();
-        View input = mWebView.findFocus();
-        assertTrue(input instanceof AutoCompleteTextView);
-        getInstrumentation().sendStringSync("te");
-        assertTrue(((AutoCompleteTextView) input).isPopupShowing());
-
-        mWebView.reload();
-        waitForLoadComplete(mWebView, TEST_TIMEOUT);
-        moveFocusDown();
-        mWebView.clearFormData();
-        // no auto completion choice after clearing
-        input = mWebView.findFocus();
-        assertTrue(input instanceof AutoCompleteTextView);
-        getInstrumentation().sendStringSync("te");
-        assertFalse(((AutoCompleteTextView) input).isPopupShowing());
-    }
-
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
         method = "getHitTestResult",
         args = {}
     )
@@ -1458,13 +1432,13 @@
         waitForLoadComplete(mWebView, TEST_TIMEOUT);
         final float defaultScale = getInstrumentation().getTargetContext().getResources().
             getDisplayMetrics().density;
-        assertEquals(defaultScale, mWebView.getScale(), 0f);
+        assertEquals(defaultScale, mWebView.getScale(), .01f);
 
         mWebView.setInitialScale(0);
         // modify content to fool WebKit into re-loading
         mWebView.loadData("<html><body>" + p + "2" + "</body></html>", "text/html", "UTF-8");
         waitForLoadComplete(mWebView, TEST_TIMEOUT);
-        assertEquals(defaultScale, mWebView.getScale(), 0f);
+        assertEquals(defaultScale, mWebView.getScale(), .01f);
 
         mWebView.setInitialScale(50);
         mWebView.loadData("<html><body>" + p + "3" + "</body></html>", "text/html", "UTF-8");
@@ -1474,7 +1448,7 @@
         mWebView.setInitialScale(0);
         mWebView.loadData("<html><body>" + p + "4" + "</body></html>", "text/html", "UTF-8");
         waitForLoadComplete(mWebView, TEST_TIMEOUT);
-        assertEquals(defaultScale, mWebView.getScale(), 0f);
+        assertEquals(defaultScale, mWebView.getScale(), .01f);
     }
 
     @TestTargetNew(
@@ -1669,42 +1643,6 @@
         mWebView.clearSslPreferences();
     }
 
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "pauseTimers",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "resumeTimers",
-            args = {}
-        )
-    })
-    @ToBeFixed(explanation = "WebView.pauseTimers() does not pause javascript timers")
-    @BrokenTest(value = "Frequently crashes the process some time after test completion.")
-    public void testPauseTimers() throws Exception {
-        WebSettings settings = mWebView.getSettings();
-        settings.setJavaScriptEnabled(true);
-        startWebServer(false);
-        // load a page which increments the number in its title every second
-        String url = mWebServer.getAssetUrl(TestHtmlConstants.TEST_TIMER_URL);
-        assertLoadUrlSuccessfully(mWebView, url);
-        int counter = Integer.parseInt(mWebView.getTitle());
-        Thread.sleep(2000);
-        assertTrue(Integer.parseInt(mWebView.getTitle()) > counter);
-        mWebView.pauseTimers();
-        Thread.sleep(2000); // give the implementation time to stop the timer
-        counter = Integer.parseInt(mWebView.getTitle());
-        Thread.sleep(2000);
-        // ToBeFixed: Uncomment the following line once pauseTimers() is fixed
-        // assertEquals(counter, Integer.parseInt(mWebView.getTitle()));
-        mWebView.resumeTimers();
-        Thread.sleep(2000);
-        assertTrue(Integer.parseInt(mWebView.getTitle()) > counter);
-    }
-
-
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         method = "requestChildRectangleOnScreen",
@@ -1732,22 +1670,6 @@
         });
     }
 
-    @TestTargetNew(
-        level = TestLevel.SUFFICIENT,
-        method = "performLongClick",
-        args = {}
-    )
-    public void testPerformLongClick() throws InterruptedException {
-        String form = "<p><form><input type=\"text\" name=\"Test\"><br>"
-                + "<input type=\"submit\" value=\"Submit\"></form></p>";
-        mWebView.loadDataWithBaseURL("fake://home", "<html><body>" + form
-                + "</body></html>", "text/html", "UTF-8", null);
-        waitForLoadComplete(mWebView, TEST_TIMEOUT);
-
-        mWebView.performLongClick();
-        // no simple way to check the effect of a long click
-    }
-
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index 0995f28..4bf19d2 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -199,55 +199,6 @@
         });
         mInstrumentation.waitForIdleSync();
     }
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        method = "setOnScrollListener",
-        args = {android.widget.AbsListView.OnScrollListener.class}
-    )
-    public void testSetOnScrollListener() throws Throwable {
-        MockOnScrollListener onScrollListener = new MockOnScrollListener();
-
-        assertNull(onScrollListener.getView());
-        assertEquals(0, onScrollListener.getFirstVisibleItem());
-        assertEquals(0, onScrollListener.getVisibleItemCount());
-        assertEquals(0, onScrollListener.getTotalItemCount());
-        assertEquals(-1, onScrollListener.getScrollState());
-
-        assertFalse(onScrollListener.isOnScrollCalled());
-        assertFalse(onScrollListener.isOnScrollStateChangedCalled());
-
-        mListView.setOnScrollListener(onScrollListener);
-        assertSame(mListView, onScrollListener.getView());
-        assertEquals(0, onScrollListener.getFirstVisibleItem());
-        assertEquals(0, onScrollListener.getVisibleItemCount());
-        assertEquals(0, onScrollListener.getTotalItemCount());
-        assertEquals(-1, onScrollListener.getScrollState());
-
-        assertTrue(onScrollListener.isOnScrollCalled());
-        assertFalse(onScrollListener.isOnScrollStateChangedCalled());
-        onScrollListener.reset();
-
-        setAdapter();
-
-        assertSame(mListView, onScrollListener.getView());
-        assertEquals(0, onScrollListener.getFirstVisibleItem());
-        assertEquals(mListView.getChildCount(), onScrollListener.getVisibleItemCount());
-        assertEquals(mCountryList.length, onScrollListener.getTotalItemCount());
-        assertEquals(-1, onScrollListener.getScrollState());
-
-        assertTrue(onScrollListener.isOnScrollCalled());
-        assertFalse(onScrollListener.isOnScrollStateChangedCalled());
-        onScrollListener.reset();
-
-        TouchUtils.scrollToBottom(this, mActivity, mListView);
-        assertSame(mListView, onScrollListener.getView());
-        assertEquals(mListView.getChildCount(), onScrollListener.getVisibleItemCount());
-        assertEquals(mCountryList.length, onScrollListener.getTotalItemCount());
-        assertEquals(OnScrollListener.SCROLL_STATE_IDLE, onScrollListener.getScrollState());
-
-        assertTrue(onScrollListener.isOnScrollCalled());
-        assertTrue(onScrollListener.isOnScrollStateChangedCalled());
-    }
 
     @TestTargetNew(
         level = TestLevel.COMPLETE,
diff --git a/tests/tests/widget/src/android/widget/cts/CursorTreeAdapterTest.java b/tests/tests/widget/src/android/widget/cts/CursorTreeAdapterTest.java
index 0927b61..e448a97 100644
--- a/tests/tests/widget/src/android/widget/cts/CursorTreeAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CursorTreeAdapterTest.java
@@ -79,12 +79,20 @@
         mDatabase.execSQL("CREATE TABLE child1 (_id INTEGER PRIMARY KEY, value TEXT);");
         mDatabase.execSQL("INSERT INTO child1 (value) VALUES ('" + CHILD_VALUE_ONE + "');");
         mDatabase.execSQL("INSERT INTO child1 (value) VALUES ('" + CHILD_VALUE_TWO + "');");
+        return getChild1Cursor();
+    }
+
+    private Cursor getChild1Cursor() {
         return mDatabase.query("child1", VALUE_PROJECTION, null, null, null, null, null);
     }
 
     private Cursor createChild2Cursor() {
         mDatabase.execSQL("CREATE TABLE child2 (_id INTEGER PRIMARY KEY, value TEXT);");
         mDatabase.execSQL("INSERT INTO child2 (value) VALUES ('" + CHILD_VALUE_THREE + "');");
+        return getChild2Cursor();
+    }
+
+    private Cursor getChild2Cursor() {
         return mDatabase.query("child2", VALUE_PROJECTION, null, null, null, null, null);
     }
 
@@ -679,7 +687,7 @@
         level = TestLevel.COMPLETE,
         notes = "Test {@link CursorAdapter#getChildView(int, int, boolean, View, ViewGroup)}",
         method = "getChildView",
-        args = {int.class, int.class, boolean.class, android.view.View.class, 
+        args = {int.class, int.class, boolean.class, android.view.View.class,
                 android.view.ViewGroup.class}
     )
     public void testGetChildView() {
@@ -792,9 +800,15 @@
 
             if (0 == groupCursor.getPosition()) {
                 mHasAddedChild1IntoCache = true;
+                if (mChildCursor1.isClosed()) {
+                    mChildCursor1 = getChild1Cursor();
+                }
                 return mChildCursor1;
             } else if (1 == groupCursor.getPosition()) {
                 mHasAddedChild2IntoCache = true;
+                if (mChildCursor2.isClosed()) {
+                    mChildCursor2 = getChild2Cursor();
+                }
                 return mChildCursor2;
             }
             return null;
diff --git a/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java b/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java
old mode 100644
new mode 100755
index 1f8cd23..172d8a9
--- a/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java
@@ -379,8 +379,8 @@
         int topDelta = rect.top - container.getTop();
         int bottomDelta = container.getBottom() - rect.bottom;
 
-        assertTrue(Math.abs(leftDelta - rightDelta) < 1);
-        assertTrue(Math.abs(topDelta - bottomDelta) < 1);
+        assertTrue(Math.abs(leftDelta - rightDelta) <= 1);
+        assertTrue(Math.abs(topDelta - bottomDelta) <= 1);
     }
 
     private AttributeSet getAttributeSet() throws XmlPullParserException, IOException {
diff --git a/tests/tests/widget/src/android/widget/cts/GalleryTest.java b/tests/tests/widget/src/android/widget/cts/GalleryTest.java
index e95deb7..aea178a 100644
--- a/tests/tests/widget/src/android/widget/cts/GalleryTest.java
+++ b/tests/tests/widget/src/android/widget/cts/GalleryTest.java
@@ -19,7 +19,6 @@
 import com.android.cts.stub.R;
 import com.android.internal.view.menu.ContextMenuBuilder;
 
-import dalvik.annotation.BrokenTest;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
@@ -34,7 +33,6 @@
 import android.content.Context;
 import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.TouchUtils;
 import android.test.UiThreadTest;
 import android.test.ViewAsserts;
 import android.util.AttributeSet;
@@ -133,70 +131,6 @@
         }
     }
 
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "setCallbackDuringFling",
-            args = {boolean.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "onTouchEvent",
-            args = {android.view.MotionEvent.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "onFling",
-            args = {MotionEvent.class, MotionEvent.class, float.class, float.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "onDown",
-            args = {android.view.MotionEvent.class}
-        )
-    })
-    @BrokenTest("listener.isItemSelected() is false, need to investigate")
-    public void testSetCallbackDuringFling() {
-        MockOnItemSelectedListener listener = new MockOnItemSelectedListener();
-        mGallery.setOnItemSelectedListener(listener);
-
-        mGallery.setCallbackDuringFling(true);
-
-        int[] xy = new int[2];
-        getSelectedViewCenter(mGallery, xy);
-
-        // This drags over only one item.
-        TouchUtils.drag(this, xy[0], 0, xy[1], xy[1], 1);
-
-        listener.reset();
-        // This will drags over several items.
-        TouchUtils.drag(this, xy[0], 0, xy[1], xy[1], 1);
-
-        assertTrue(listener.isItemSelected());
-        // onItemSelected called more than once
-        assertTrue(listener.getItemSelectedCalledCount() > 1);
-
-        listener.reset();
-        mGallery.setCallbackDuringFling(false);
-
-        TouchUtils.drag(this, xy[0], 240, xy[1], xy[1], 1);
-
-        assertTrue(listener.isItemSelected());
-        // onItemSelected called only once
-        assertTrue(listener.getItemSelectedCalledCount() == 1);
-    }
-
-    private void getSelectedViewCenter(Gallery gallery, int[] xy) {
-        View v = gallery.getSelectedView();
-        v.getLocationOnScreen(xy);
-
-        final int viewWidth = v.getWidth();
-        final int viewHeight = v.getHeight();
-
-        xy[1] += viewHeight / 2;
-        xy[0] += viewWidth / 2;
-    }
-
     @TestTargetNew(
         level = TestLevel.NOT_FEASIBLE,
         method = "setAnimationDuration",
diff --git a/tests/tests/widget/src/android/widget/cts/GridViewTest.java b/tests/tests/widget/src/android/widget/cts/GridViewTest.java
index 8f342b0..2131965 100644
--- a/tests/tests/widget/src/android/widget/cts/GridViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/GridViewTest.java
@@ -215,11 +215,11 @@
         mInstrumentation.waitForIdleSync();
 
         assertEquals(0, mGridView.getSelectedItemPosition());
-        KeyEvent event = new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_DPAD_RIGHT);
+        KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
         mInstrumentation.sendKeySync(event);
         assertEquals(1, mGridView.getSelectedItemPosition());
 
-        event = new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_DPAD_LEFT);
+        event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
         mInstrumentation.sendKeySync(event);
         assertEquals(0, mGridView.getSelectedItemPosition());
 
@@ -535,7 +535,7 @@
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         method = "attachLayoutAnimationParameters",
-        args = {android.view.View.class, android.view.ViewGroup.LayoutParams.class, int.class, 
+        args = {android.view.View.class, android.view.ViewGroup.LayoutParams.class, int.class,
                 int.class}
     )
     public void testAttachLayoutAnimationParameters() {
@@ -635,50 +635,6 @@
         assertEquals(child0.getLeft(), child1.getLeft());
     }
 
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            notes = "Test {@link GridView#computeVerticalScrollExtent()}",
-            method = "computeVerticalScrollExtent",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            notes = "Test {@link GridView#computeVerticalScrollExtent()}",
-            method = "computeVerticalScrollOffset",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            notes = "Test {@link GridView#computeVerticalScrollExtent()}",
-            method = "computeVerticalScrollRange",
-            args = {}
-        )
-    })
-    public void testScroll() throws Throwable {
-        final MockGridView mockGridView= new MockGridView(mActivity);
-        final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
-        // this test case can not be ran in UI thread.
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                mActivity.getWindow().setContentView(mockGridView, params);
-                mockGridView.setAdapter(new ImageAdapter(mActivity));
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        TouchUtils.scrollToTop(this, mActivity, mockGridView);
-
-        int oldRange = mockGridView.computeVerticalScrollRange();
-        int oldExtent = mockGridView.computeVerticalScrollExtent();
-        int oldOffset = mockGridView.computeVerticalScrollOffset();
-
-        TouchUtils.scrollToBottom(this, mActivity, mockGridView);
-        assertEquals(oldRange, mockGridView.computeVerticalScrollRange());
-        assertEquals(oldExtent, mockGridView.computeVerticalScrollExtent());
-        assertTrue(oldOffset < mockGridView.computeVerticalScrollOffset());
-    }
-
     private static class MockGridView extends GridView {
         private boolean mCalledOnMeasure = false;
         private boolean mCalledOnFocusChanged = false;
diff --git a/tests/tests/widget/src/android/widget/cts/ImageSwitcherTest.java b/tests/tests/widget/src/android/widget/cts/ImageSwitcherTest.java
index 9e32748..a7667ba 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageSwitcherTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageSwitcherTest.java
@@ -136,18 +136,20 @@
         imageSwitcher.setImageURI(uri);
         assertSame(iv1, imageSwitcher.getCurrentView());
 
-        Bitmap testImageBitmap = WidgetTestUtils.getUnscaledBitmap(getContext().getResources(),
-                R.raw.testimage);
         BitmapDrawable currViewBitmap =
             (BitmapDrawable) ((ImageView) imageSwitcher.getCurrentView()).getDrawable();
+        Bitmap testImageBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(
+                getContext().getResources(), R.raw.testimage,
+                currViewBitmap.getBitmap().getConfig());
         WidgetTestUtils.assertEquals(testImageBitmap, currViewBitmap.getBitmap());
 
         createSampleImage(imagefile, R.raw.scenery);
         uri = Uri.parse(imagefile.getPath());
         imageSwitcher.setImageURI(uri);
         assertSame(iv, imageSwitcher.getCurrentView());
-        Bitmap sceneryImageBitmap = WidgetTestUtils.getUnscaledBitmap(getContext().getResources(),
-                R.raw.scenery);
+        Bitmap sceneryImageBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(
+                getContext().getResources(), R.raw.scenery,
+                currViewBitmap.getBitmap().getConfig());
         currViewBitmap =
             (BitmapDrawable) ((ImageView) imageSwitcher.getCurrentView()).getDrawable();
         WidgetTestUtils.assertEquals(sceneryImageBitmap, currViewBitmap.getBitmap());
diff --git a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
index 5214b50..0777259 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
@@ -276,10 +276,12 @@
         assertTrue(mImageView.isLayoutRequested());
         assertNotNull(mImageView.getDrawable());
 
-        Bitmap testimageBitmap = WidgetTestUtils.getUnscaledBitmap(mActivity.getResources(),
-                R.raw.testimage);
         Drawable imageViewDrawable = mImageView.getDrawable();
         BitmapDrawable imageViewBitmap = (BitmapDrawable) imageViewDrawable;
+        Bitmap.Config viewConfig = imageViewBitmap.getBitmap().getConfig();
+        Bitmap testimageBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(
+                mActivity.getResources(), R.raw.testimage, viewConfig);
+
         WidgetTestUtils.assertEquals(testimageBitmap, imageViewBitmap.getBitmap());
     }
 
diff --git a/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java b/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java
index 40465ea..9f27138 100644
--- a/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java
@@ -18,7 +18,6 @@
 
 import com.android.cts.stub.R;
 
-import dalvik.annotation.BrokenTest;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
@@ -31,11 +30,9 @@
 import android.app.Instrumentation;
 import android.content.Context;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.TouchUtils;
 import android.test.UiThreadTest;
 import android.util.AttributeSet;
 import android.util.Xml;
-import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -266,50 +263,6 @@
 
     @TestTargetNew(
         level = TestLevel.COMPLETE,
-        notes = "Test {@link MediaController#onTouchEvent(MotionEvent)}, " +
-                "this function always returns true",
-        method = "onTouchEvent",
-        args = {android.view.MotionEvent.class}
-    )
-    @ToBeFixed(bug = "1559790", explanation = "MediaController does not appear " +
-            "when the user touches the anchor view.")
-    @BrokenTest("NullPointerException thrown; no stacktrace in result")
-    public void testOnTouchEvent() {
-        final XmlPullParser parser =
-                mActivity.getResources().getXml(R.layout.mediacontroller_layout);
-        final AttributeSet attrs = Xml.asAttributeSet(parser);
-        mMediaController = new MediaController(mActivity, attrs);
-        final MockMediaPlayerControl mediaPlayerControl = new MockMediaPlayerControl();
-        mMediaController.setMediaPlayer(mediaPlayerControl);
-
-        final VideoView videoView =
-                (VideoView) mActivity.findViewById(R.id.mediacontroller_videoview);
-        videoView.setMediaController(mMediaController);
-        mActivity.runOnUiThread(new Runnable() {
-            public void run() {
-                videoView.setVideoPath(prepareSampleVideo());
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertFalse(mMediaController.isShowing());
-        TouchUtils.tapView(this, videoView);
-        mInstrumentation.waitForIdleSync();
-
-        // isShowing() should return true, but MediaController still not shows, this may be a bug.
-        assertFalse(mMediaController.isShowing());
-
-        // timeout is larger than duration, in case the system is sluggish
-        new DelayedCheck(DEFAULT_TIMEOUT + 500) {
-            @Override
-            protected boolean check() {
-                return !mMediaController.isShowing();
-            }
-        }.run();
-    }
-
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
         notes = "Test {@link MediaController#onTrackballEvent(MotionEvent)}, " +
                 "this function always returns false",
         method = "onTrackballEvent",
@@ -351,46 +304,6 @@
 
     @TestTargetNew(
         level = TestLevel.COMPLETE,
-        notes = "Test {@link MediaController#dispatchKeyEvent(KeyEvent)}",
-        method = "dispatchKeyEvent",
-        args = {android.view.KeyEvent.class}
-    )
-    @ToBeFixed(bug = "1559790", explanation = "MediaController does not appear " +
-            "when the user presses a key.")
-    @BrokenTest("Fragile test. Passes only occasionally.")
-    public void testDispatchKeyEvent() {
-        mMediaController = new MediaController(mActivity);
-        final MockMediaPlayerControl mediaPlayerControl = new MockMediaPlayerControl();
-        mMediaController.setMediaPlayer(mediaPlayerControl);
-
-        final VideoView videoView =
-                (VideoView) mActivity.findViewById(R.id.mediacontroller_videoview);
-        videoView.setMediaController(mMediaController);
-        mActivity.runOnUiThread(new Runnable() {
-            public void run() {
-                videoView.setVideoPath(prepareSampleVideo());
-                videoView.requestFocus();
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_SPACE);
-        mInstrumentation.waitForIdleSync();
-
-        // isShowing() should return true, but MediaController still not shows, this may be a bug.
-        assertFalse(mMediaController.isShowing());
-
-        // timeout is larger than duration, in case the system is sluggish
-        new DelayedCheck(DEFAULT_TIMEOUT + 500) {
-            @Override
-            protected boolean check() {
-                return !mMediaController.isShowing();
-            }
-        }.run();
-    }
-
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
         notes = "Test {@link MediaController#setEnabled(boolean)}",
         method = "setEnabled",
         args = {boolean.class}
diff --git a/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java b/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
index 4c5c717..1b9b517 100644
--- a/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
@@ -18,7 +18,6 @@
 
 import com.android.cts.stub.R;
 
-import dalvik.annotation.BrokenTest;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
@@ -26,7 +25,6 @@
 import dalvik.annotation.ToBeFixed;
 
 import android.content.Context;
-import android.content.res.Resources.NotFoundException;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.drawable.Drawable;
@@ -34,7 +32,6 @@
 import android.test.InstrumentationTestCase;
 import android.view.View;
 import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.CycleInterpolator;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 import android.widget.ProgressBar;
@@ -72,7 +69,7 @@
 
         new ProgressBar(mContext, null);
 
-        new ProgressBar(mContext, null, com.android.internal.R.attr.progressBarStyle);
+        new ProgressBar(mContext, null, android.R.attr.progressBarStyle);
     }
 
     @TestTargets({
@@ -98,8 +95,7 @@
         // because default is Indeterminate only progressBar, can't change the status
         assertTrue(progressBar.isIndeterminate());
 
-        progressBar = new ProgressBar(mContext, null,
-                com.android.internal.R.attr.progressBarStyleHorizontal);
+        progressBar = new ProgressBar(mContext, null, android.R.attr.progressBarStyleHorizontal);
         assertFalse(progressBar.isIndeterminate());
 
         progressBar.setIndeterminate(true);
@@ -152,7 +148,7 @@
     })
     public void testAccessProgressDrawable() {
         ProgressBar progressBar = new ProgressBar(mContext, null,
-                        com.android.internal.R.attr.progressBarStyleHorizontal);
+                android.R.attr.progressBarStyleHorizontal);
 
         // set ProgressDrawable
         // normal value
@@ -182,7 +178,7 @@
     })
     public void testAccessProgress() {
         ProgressBar progressBar = new ProgressBar(mContext, null,
-                com.android.internal.R.attr.progressBarStyleHorizontal);
+                android.R.attr.progressBarStyleHorizontal);
         assertEquals(0, progressBar.getProgress());
 
         final int maxProgress = progressBar.getMax();
@@ -221,7 +217,7 @@
     })
     public void testAccessSecondaryProgress() {
         ProgressBar progressBar = new ProgressBar(mContext, null,
-                com.android.internal.R.attr.progressBarStyleHorizontal);
+                android.R.attr.progressBarStyleHorizontal);
         assertEquals(0, progressBar.getSecondaryProgress());
 
         final int maxProgress = progressBar.getMax();
@@ -253,7 +249,7 @@
     )
     public void testIncrementProgressBy() {
         ProgressBar progressBar = new ProgressBar(mContext, null,
-                com.android.internal.R.attr.progressBarStyleHorizontal);
+                android.R.attr.progressBarStyleHorizontal);
 
         // normal value
         int increment = 1;
@@ -283,7 +279,7 @@
     )
     public void testIncrementSecondaryProgressBy() {
         ProgressBar progressBar = new ProgressBar(mContext, null,
-                com.android.internal.R.attr.progressBarStyleHorizontal);
+                android.R.attr.progressBarStyleHorizontal);
 
         // normal value
         int increment = 1;
@@ -319,8 +315,7 @@
         )
     })
     public void testAccessInterpolator() {
-        ProgressBar progressBar = new ProgressBar(mContext, null,
-                com.android.internal.R.attr.progressBarStyle);
+        ProgressBar progressBar = new ProgressBar(mContext);
 
         // default should be LinearInterpolator
         assertTrue(progressBar.getInterpolator() instanceof LinearInterpolator);
@@ -329,54 +324,6 @@
         Interpolator i = new AccelerateDecelerateInterpolator();
         progressBar.setInterpolator(i);
         assertEquals(i, progressBar.getInterpolator());
-
-        // exceptional value
-        progressBar.setInterpolator(null);
-        assertNull(progressBar.getInterpolator());
-
-        // TODO: test whether setInterpolator takes effect? How to get the animation?
-    }
-
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "setInterpolator",
-            args = {android.content.Context.class, int.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getInterpolator",
-            args = {}
-        )
-    })
-    @ToBeFixed(bug = "1695243", explanation = "the javadoc for setInterpolator() is incomplete." +
-            "1. not clear what is supposed to happen if context or resID is exceptional.")
-    @BrokenTest("Initial setInterpolator() call occasionally fails with NPE. context null?")
-    public void testAccessInterpolatorContext() {
-        ProgressBar progressBar = new ProgressBar(mContext, null,
-                com.android.internal.R.attr.progressBarStyle);
-
-        // default is LinearInterpolator
-        assertTrue(progressBar.getInterpolator() instanceof LinearInterpolator);
-
-        // normal value
-        progressBar.setInterpolator(mContext.getApplicationContext(), R.anim.move_cycle);
-        assertTrue(progressBar.getInterpolator() instanceof CycleInterpolator);
-
-        // exceptional value
-        try {
-            progressBar.setInterpolator(null, R.anim.move_ani);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // issue 1695243, not clear what is supposed to happen if context is null.
-        }
-
-        try {
-            progressBar.setInterpolator(mContext.getApplicationContext(), -1);
-            fail("Should throw NotFoundException");
-        } catch (NotFoundException e) {
-            // issue 1695243, not clear what is supposed to happen if resID is exceptional.
-        }
     }
 
     @TestTargetNew(
@@ -388,7 +335,7 @@
             "1. not clear what is supposed result if visibility isn't VISIBLE, INVISIBLE or GONE.")
     public void testSetVisibility() {
         ProgressBar progressBar = new ProgressBar(mContext, null,
-                com.android.internal.R.attr.progressBarStyleHorizontal);
+                android.R.attr.progressBarStyleHorizontal);
 
         // set visibility
         // normal value
@@ -472,7 +419,7 @@
     })
     public void testAccessMax() {
         ProgressBar progressBar = new ProgressBar(mContext, null,
-                com.android.internal.R.attr.progressBarStyleHorizontal);
+                android.R.attr.progressBarStyleHorizontal);
 
         // set Progress
         int progress = 10;
@@ -612,7 +559,7 @@
     })
     public void testOnSaveAndRestoreInstanceState() {
         ProgressBar progressBar = new ProgressBar(mContext, null,
-                com.android.internal.R.attr.progressBarStyleHorizontal);
+                android.R.attr.progressBarStyleHorizontal);
         int oldProgress = 1;
         int oldSecondaryProgress = progressBar.getMax() - 1;
         progressBar.setProgress(oldProgress);
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
index 1671f28..9331ff9 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
@@ -18,7 +18,6 @@
 
 import com.android.cts.stub.R;
 
-import dalvik.annotation.BrokenTest;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
@@ -218,14 +217,11 @@
         method = "setImageViewUri",
         args = {int.class, android.net.Uri.class}
     )
-    @BrokenTest("needs investigation")
     public void testSetImageViewUri() throws IOException {
-        File imageFile = null;
+        String path = getTestImagePath();
+        File imageFile = new File(path);
 
         try {
-            // create the test image first
-            String path = getTestImagePath();
-            imageFile = new File(path);
             createSampleImage(imageFile, R.raw.testimage);
 
             Uri uri = Uri.parse(path);
@@ -234,15 +230,13 @@
 
             mRemoteViews.setImageViewUri(R.id.remoteView_image, uri);
             mRemoteViews.reapply(mActivity, mResult);
-            BitmapDrawable d = (BitmapDrawable) mActivity
-            .getResources().getDrawable(R.drawable.testimage);
-            WidgetTestUtils.assertEquals(d.getBitmap(),
-                    ((BitmapDrawable) image.getDrawable()).getBitmap());
+
+            Bitmap imageViewBitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
+            Bitmap expectedBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(
+                    mActivity.getResources(), R.raw.testimage, imageViewBitmap.getConfig());
+            WidgetTestUtils.assertEquals(expectedBitmap, imageViewBitmap);
         } finally {
-            if (imageFile != null) {
-                // remove the test image file
-                imageFile.delete();
-            }
+            imageFile.delete();
         }
     }
 
@@ -602,34 +596,36 @@
         method = "setUri",
         args = {int.class, java.lang.String.class, android.net.Uri.class}
     )
-    @BrokenTest("needs investigation")
     public void testSetUri() throws IOException {
-        // create the test image first
         String path = getTestImagePath();
         File imagefile = new File(path);
-        createSampleImage(imagefile, R.raw.testimage);
 
-        Uri uri = Uri.parse(path);
-        ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
-        assertNull(image.getDrawable());
-
-        mRemoteViews.setUri(R.id.remoteView_image, "setImageURI", uri);
-        mRemoteViews.reapply(mActivity, mResult);
-        BitmapDrawable d = (BitmapDrawable) mActivity
-                .getResources().getDrawable(R.drawable.testimage);
-        WidgetTestUtils.assertEquals(d.getBitmap(),
-                ((BitmapDrawable) image.getDrawable()).getBitmap());
-
-        mRemoteViews.setUri(R.id.remoteView_absolute, "setImageURI", uri);
         try {
-            mRemoteViews.reapply(mActivity, mResult);
-            fail("Should throw ActionException");
-        } catch (ActionException e) {
-            // expected
-        }
+            createSampleImage(imagefile, R.raw.testimage);
 
-        // remove the test image file
-        imagefile.delete();
+            Uri uri = Uri.parse(path);
+            ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
+            assertNull(image.getDrawable());
+
+            mRemoteViews.setUri(R.id.remoteView_image, "setImageURI", uri);
+            mRemoteViews.reapply(mActivity, mResult);
+
+            Bitmap imageViewBitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
+            Bitmap expectedBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(
+                    mActivity.getResources(), R.raw.testimage, imageViewBitmap.getConfig());
+            WidgetTestUtils.assertEquals(expectedBitmap, imageViewBitmap);
+
+            mRemoteViews.setUri(R.id.remoteView_absolute, "setImageURI", uri);
+            try {
+                mRemoteViews.reapply(mActivity, mResult);
+                fail("Should throw ActionException");
+            } catch (ActionException e) {
+                // expected
+            }
+        } finally {
+            // remove the test image file
+            imagefile.delete();
+        }
     }
 
     @TestTargetNew(
diff --git a/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java b/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java
index b852e5b..cff8142 100644
--- a/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java
@@ -78,10 +78,10 @@
 
         // calculate pixel positions from dpi constants.
         final float density = getActivity().getResources().getDisplayMetrics().density;
-        mItemWidth = (int) (ITEM_WIDTH_DPI * density);
-        mItemHeight = (int) (ITEM_HEIGHT_DPI * density);
-        mPageWidth = (int) (PAGE_WIDTH_DPI * density);
-        mPageHeight = (int) (PAGE_HEIGHT_DPI * density);
+        mItemWidth = (int) (ITEM_WIDTH_DPI * density + 0.5f);
+        mItemHeight = (int) (ITEM_HEIGHT_DPI * density + 0.5f);
+        mPageWidth = (int) (PAGE_WIDTH_DPI * density + 0.5f);
+        mPageHeight = (int) (PAGE_HEIGHT_DPI * density + 0.5f);
 
         mScrollBottom = mItemHeight * ITEM_COUNT - mPageHeight;
         mScrollRight = mItemWidth - mPageWidth;
diff --git a/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java b/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java
index 6fb7063..d20582b 100644
--- a/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java
@@ -410,10 +410,10 @@
             mSimpleAdapter.setViewImage(view, SimpleCursorAdapterTest.createTestImage(mContext,
                     "testimage", com.android.cts.stub.R.raw.testimage));
             assertNotNull(view.getDrawable());
-            Bitmap testBitmap = WidgetTestUtils.getUnscaledBitmap(mContext.getResources(),
-                    com.android.cts.stub.R.raw.testimage);
-            WidgetTestUtils.assertEquals(testBitmap,
-                    ((BitmapDrawable) view.getDrawable()).getBitmap());
+            Bitmap actualBitmap = ((BitmapDrawable) view.getDrawable()).getBitmap();
+            Bitmap testBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(mContext.getResources(),
+                    com.android.cts.stub.R.raw.testimage, actualBitmap.getConfig());
+            WidgetTestUtils.assertEquals(testBitmap, actualBitmap);
         } finally {
             SimpleCursorAdapterTest.destroyTestImage(mContext,"testimage");
         }
diff --git a/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java b/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java
index 8f32228..08e425a 100644
--- a/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java
@@ -256,10 +256,10 @@
             mSimpleCursorAdapter.setViewImage(view,
                     createTestImage(mContext, SAMPLE_IMAGE_NAME, testimgRawId));
             assertNotNull(view.getDrawable());
-            Bitmap testBitmap = WidgetTestUtils.getUnscaledBitmap(mContext.getResources(),
-                    testimgRawId);
-            WidgetTestUtils.assertEquals(testBitmap,
-                    ((BitmapDrawable) view.getDrawable()).getBitmap());
+            Bitmap actualBitmap = ((BitmapDrawable) view.getDrawable()).getBitmap();
+            Bitmap testBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(mContext.getResources(),
+                    testimgRawId, actualBitmap.getConfig());
+            WidgetTestUtils.assertEquals(testBitmap, actualBitmap);
         } finally {
             destroyTestImage(mContext, SAMPLE_IMAGE_NAME);
         }
diff --git a/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java b/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java
index 7295e75..8e6f0f3 100644
--- a/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java
@@ -83,24 +83,24 @@
             level = TestLevel.COMPLETE,
             notes = "Test constructors",
             method = "SimpleCursorTreeAdapter",
-            args = {android.content.Context.class, android.database.Cursor.class, int.class, 
-                    int.class, java.lang.String[].class, int[].class, int.class, int.class, 
+            args = {android.content.Context.class, android.database.Cursor.class, int.class,
+                    int.class, java.lang.String[].class, int[].class, int.class, int.class,
                     java.lang.String[].class, int[].class}
         ),
         @TestTargetNew(
             level = TestLevel.COMPLETE,
             notes = "Test constructors",
             method = "SimpleCursorTreeAdapter",
-            args = {android.content.Context.class, android.database.Cursor.class, int.class, 
-                    int.class, java.lang.String[].class, int[].class, int.class, 
+            args = {android.content.Context.class, android.database.Cursor.class, int.class,
+                    int.class, java.lang.String[].class, int[].class, int.class,
                     java.lang.String[].class, int[].class}
         ),
         @TestTargetNew(
             level = TestLevel.COMPLETE,
             notes = "Test constructors",
             method = "SimpleCursorTreeAdapter",
-            args = {android.content.Context.class, android.database.Cursor.class, int.class, 
-                    java.lang.String[].class, int[].class, int.class, java.lang.String[].class, 
+            args = {android.content.Context.class, android.database.Cursor.class, int.class,
+                    java.lang.String[].class, int[].class, int.class, java.lang.String[].class,
                     int[].class}
         )
     })
@@ -122,7 +122,7 @@
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         method = "bindChildView",
-        args = {android.view.View.class, android.content.Context.class, 
+        args = {android.view.View.class, android.content.Context.class,
                 android.database.Cursor.class, boolean.class}
     )
     public void testBindChildView() {
@@ -146,7 +146,7 @@
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         method = "bindGroupView",
-        args = {android.view.View.class, android.content.Context.class, 
+        args = {android.view.View.class, android.content.Context.class,
                 android.database.Cursor.class, boolean.class}
     )
     // The param context and isExpanded is never readed.
@@ -183,7 +183,7 @@
         // color drawable
         ImageView view = new ImageView(mContext);
         assertNull(view.getDrawable());
-        mSimpleCursorTreeAdapter.setViewImage(view, 
+        mSimpleCursorTreeAdapter.setViewImage(view,
                 String.valueOf(com.android.cts.stub.R.drawable.scenery));
         BitmapDrawable d = (BitmapDrawable) mContext.getResources().getDrawable(
                 com.android.cts.stub.R.drawable.scenery);
@@ -213,10 +213,10 @@
             mSimpleCursorTreeAdapter.setViewImage(view,
                     SimpleCursorAdapterTest.createTestImage(mContext, SAMPLE_IMAGE_NAME,
                             com.android.cts.stub.R.raw.testimage));
-            Bitmap test = WidgetTestUtils.getUnscaledBitmap(mContext.getResources(),
-                    com.android.cts.stub.R.raw.testimage);
-            WidgetTestUtils.assertEquals(test,
-                    ((BitmapDrawable) view.getDrawable()).getBitmap());
+            Bitmap actualBitmap = ((BitmapDrawable) view.getDrawable()).getBitmap();
+            Bitmap test = WidgetTestUtils.getUnscaledAndDitheredBitmap(mContext.getResources(),
+                    com.android.cts.stub.R.raw.testimage, actualBitmap.getConfig());
+            WidgetTestUtils.assertEquals(test, actualBitmap);
         } finally {
             SimpleCursorAdapterTest.destroyTestImage(mContext, SAMPLE_IMAGE_NAME);
         }
diff --git a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
index 258883d..8214102 100644
--- a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
@@ -16,6 +16,14 @@
 
 package android.widget.cts;
 
+import com.android.cts.stub.R;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+import dalvik.annotation.ToBeFixed;
+
 import android.content.Context;
 import android.content.res.XmlResourceParser;
 import android.test.ActivityInstrumentationTestCase2;
@@ -33,15 +41,6 @@
 import android.widget.TableRow;
 import android.widget.TextView;
 
-import com.android.cts.stub.R;
-
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.TestTargets;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.ToBeFixed;
-
 /**
  * Test {@link TableLayout}.
  */
@@ -492,169 +491,6 @@
         assertTrue(tableLayout.getChildAt(1).isLayoutRequested());
     }
 
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            notes = "test whether columns are actually shrunk",
-            method = "setColumnShrinkable",
-            args = {java.lang.Integer.class, java.lang.Boolean.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            notes = "test whether columns are actually shrunk",
-            method = "setShrinkAllColumns",
-            args = {java.lang.Boolean.class}
-        )
-    })
-    @ToBeFixed( bug = "", explanation = "After set a column unable to be shrunk," +
-            " the other shrinkable columns are not shrunk more.")
-    @BrokenTest("fails consistently")
-    public void testColumnShrinkableEffect() {
-        final TableStubActivity activity = getActivity();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            public void run() {
-                activity.setContentView(com.android.cts.stub.R.layout.table_layout_2);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        final TableLayout tableLayout =
-                (TableLayout) activity.findViewById(com.android.cts.stub.R.id.table2);
-
-        final int columnVirtualIndex0 = 1;
-        final int columnVirtualIndex1 = 2;
-        final int columnVirtualIndex2 = 4;
-        final TextView child0 = (TextView) ((TableRow) tableLayout.getChildAt(0)).getChildAt(0);
-        final TextView child1 = (TextView) ((TableRow) tableLayout.getChildAt(0)).getChildAt(1);
-        final TextView child2 = (TextView) ((TableRow) tableLayout.getChildAt(0)).getChildAt(2);
-
-        // get the original width of each child.
-        int oldWidth0 = child0.getWidth();
-        int oldWidth1 = child1.getWidth();
-        int oldWidth2 = child2.getWidth();
-        child0.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY);
-        int orignalWidth0 = child0.getMeasuredWidth();
-        // child1 has 2 columns.
-        child1.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY);
-        TextView column12 = (TextView) ((TableRow) tableLayout.getChildAt(1)).getChildAt(2);
-        column12.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY);
-        int orignalWidth1 = child1.getMeasuredWidth() + column12.getMeasuredWidth();
-        child2.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY);
-        int orignalWidth2 = child2.getMeasuredWidth();
-        int totalSpace = tableLayout.getWidth() - orignalWidth0
-                - orignalWidth1 - orignalWidth2;
-
-        // Test: set column 2 which is the start column for child 1 is able to be shrunk.
-        getInstrumentation().runOnMainSync(new Runnable() {
-            public void run() {
-                tableLayout.setColumnShrinkable(columnVirtualIndex1, true);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        assertTrue(oldWidth0 < child0.getWidth());
-        assertTrue(oldWidth1 > child1.getWidth());
-        assertEquals(oldWidth2, child2.getWidth());
-        int extraSpace = totalSpace / 2;
-        assertEquals(dropNegative(orignalWidth0 + extraSpace), child0.getWidth());
-        assertEquals(dropNegative(orignalWidth1 + extraSpace), child1.getWidth());
-        assertEquals(orignalWidth2, child2.getWidth());
-        oldWidth0 = child0.getWidth();
-        oldWidth1 = child1.getWidth();
-        oldWidth2 = child2.getWidth();
-
-        // Test: set column 4 which is the column for child 2 is able to be shrunk.
-        getInstrumentation().runOnMainSync(new Runnable() {
-            public void run() {
-                tableLayout.setColumnShrinkable(columnVirtualIndex2, true);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        assertTrue(oldWidth0 < child0.getWidth());
-        assertTrue(oldWidth1 < child1.getWidth());
-        assertTrue(oldWidth2 > child2.getWidth());
-        extraSpace = totalSpace / 3;
-        assertEquals(dropNegative(orignalWidth0 + extraSpace), child0.getWidth());
-        assertEquals(dropNegative(orignalWidth1 + extraSpace), child1.getWidth());
-        assertEquals(dropNegative(orignalWidth2 + extraSpace), child2.getWidth());
-        oldWidth0 = child0.getWidth();
-        oldWidth1 = child1.getWidth();
-        oldWidth2 = child2.getWidth();
-
-        // Test: set column 3 which is the end column for child 1 is able to be shrunk.
-        getInstrumentation().runOnMainSync(new Runnable() {
-            public void run() {
-                tableLayout.setColumnShrinkable(columnVirtualIndex1+1, true);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        assertTrue(oldWidth0 < child0.getWidth());
-        assertTrue(oldWidth1 > child1.getWidth());
-        assertTrue(oldWidth2 < child2.getWidth());
-        extraSpace = totalSpace / 4;
-        assertEquals(dropNegative(orignalWidth0 + extraSpace), child0.getWidth());
-        assertEquals(dropNegative(orignalWidth1 + extraSpace * 2), child1.getWidth());
-        assertEquals(dropNegative(orignalWidth2 + extraSpace), child2.getWidth());
-        oldWidth0 = child0.getWidth();
-        oldWidth1 = child1.getWidth();
-        oldWidth2 = child2.getWidth();
-
-        // Test: set column 1 which is the column for child 0 is unable to be shrunk.
-        getInstrumentation().runOnMainSync(new Runnable() {
-            public void run() {
-                tableLayout.setColumnShrinkable(columnVirtualIndex0, false);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        assertTrue(oldWidth0 < child0.getWidth());
-        // assertTrue(oldWidth1 > column1.getWidth());
-        // assertTrue(oldWidth2 > column2.getWidth());
-        assertEquals(oldWidth1, child1.getWidth());
-        assertEquals(oldWidth2, child2.getWidth());
-        // extraSpace = totalSpace / 3;
-        extraSpace = totalSpace / 4;
-        assertEquals(orignalWidth0, child0.getWidth());
-        assertEquals(orignalWidth1 + extraSpace * 2, child1.getWidth());
-        assertEquals(orignalWidth2 + extraSpace, child2.getWidth());
-        oldWidth0 = child0.getWidth();
-        oldWidth1 = child1.getWidth();
-        oldWidth2 = child2.getWidth();
-
-        // Test: mark all columns are able to be shrunk.
-        getInstrumentation().runOnMainSync(new Runnable() {
-            public void run() {
-                tableLayout.setShrinkAllColumns(true);
-                tableLayout.requestLayout();
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        assertTrue(oldWidth0 > child0.getWidth());
-        assertTrue(oldWidth1 < child1.getWidth());
-        assertTrue(oldWidth2 < child2.getWidth());
-        extraSpace = totalSpace / 5;
-        assertEquals(orignalWidth0 + extraSpace, child0.getWidth());
-        assertEquals(orignalWidth1 + extraSpace * 2, child1.getWidth());
-        assertEquals(orignalWidth2 + extraSpace, child2.getWidth());
-        oldWidth0 = child0.getWidth();
-        oldWidth1 = child1.getWidth();
-        oldWidth2 = child2.getWidth();
-
-        // Test: Remove the mark for all columns are able to be shrunk.
-        getInstrumentation().runOnMainSync(new Runnable() {
-            public void run() {
-                tableLayout.setShrinkAllColumns(false);
-                tableLayout.requestLayout();
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        assertTrue(oldWidth0 < child0.getWidth());
-        assertTrue(oldWidth1 > child1.getWidth());
-        assertTrue(oldWidth2 > child2.getWidth());
-        // extraSpace = totalSpace / 3;
-        extraSpace = totalSpace / 4;
-        assertEquals(orignalWidth0, child0.getWidth());
-        assertEquals(orignalWidth1 + extraSpace * 2, child1.getWidth());
-        assertEquals(orignalWidth2 + extraSpace, child2.getWidth());
-    }
-
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         notes = "Test addView(View child)",
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
old mode 100644
new mode 100755
index 9bd8cc4..dc8e8b7
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -80,11 +80,9 @@
 import android.util.TypedValue;
 import android.view.ContextMenu;
 import android.view.Gravity;
-import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.View.OnCreateContextMenuListener;
 import android.view.View.OnLongClickListener;
@@ -93,7 +91,6 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
-import android.widget.AbsoluteLayout;
 import android.widget.FrameLayout;
 import android.widget.Scroller;
 import android.widget.TextView;
@@ -232,7 +229,6 @@
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
                 mTextView = findTextView(R.id.textview_text);
-                mTextView.setText("");
             }
         });
         mInstrumentation.waitForIdleSync();
@@ -244,45 +240,20 @@
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
                 mTextView.setKeyListener(digitsKeyListener);
-                mTextView.requestFocus();
             }
         });
         mInstrumentation.waitForIdleSync();
         assertSame(digitsKeyListener, mTextView.getKeyListener());
-        assertEquals("", mTextView.getText().toString());
-
-        // press '-' key.
-        mInstrumentation.sendStringSync("-");
-        assertEquals("", mTextView.getText().toString());
-
-        // press '1' key.
-        mInstrumentation.sendStringSync("1");
-        assertEquals("1", mTextView.getText().toString());
-
-        // press '.' key.
-        mInstrumentation.sendStringSync(".");
-        assertEquals("1", mTextView.getText().toString());
-
-        // press 'a' key.
-        mInstrumentation.sendStringSync("a");
-        assertEquals("1", mTextView.getText().toString());
 
         final QwertyKeyListener qwertyKeyListener
                 = QwertyKeyListener.getInstance(false, Capitalize.NONE);
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
                 mTextView.setKeyListener(qwertyKeyListener);
-                mTextView.requestFocus();
             }
         });
         mInstrumentation.waitForIdleSync();
         assertSame(qwertyKeyListener, mTextView.getKeyListener());
-        assertEquals("1", mTextView.getText().toString());
-
-        // press 'a' key.
-        mInstrumentation.sendStringSync("a");
-
-        assertEquals("1a", mTextView.getText().toString());
     }
 
     @TestTargets({
@@ -1786,10 +1757,9 @@
         mInstrumentation.waitForIdleSync();
         assertNull(mTextView.getError());
 
-        final DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance();
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
-                mTextView.setKeyListener(digitsKeyListener);
+                mTextView.setKeyListener(DigitsKeyListener.getInstance(""));
                 mTextView.setText("", BufferType.EDITABLE);
                 mTextView.setError(errorText);
                 mTextView.requestFocus();
@@ -1805,6 +1775,16 @@
         // The icon and error message will not be reset to null
         assertNotNull(mTextView.getError());
 
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                mTextView.setKeyListener(DigitsKeyListener.getInstance());
+                mTextView.setText("", BufferType.EDITABLE);
+                mTextView.setError(errorText);
+                mTextView.requestFocus();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
         mInstrumentation.sendStringSync("1");
         // a key event cause changes to the TextView's text
         assertEquals("1", mTextView.getText().toString());
@@ -1900,12 +1880,12 @@
         mTextView.getFocusedRect(rc);
         assertNotNull(mTextView.getLayout());
         assertEquals(mTextView.getLayout().getPrimaryHorizontal(13),
-                (float) rc.left, 0.01f);
+                (float) rc.left, 0.4f);
         // 'right' is one pixel larger than 'left'
         assertEquals(mTextView.getLayout().getPrimaryHorizontal(13) + 1,
-                (float) rc.right, 0.01f);
+                (float) rc.right, 0.4f);
         assertEquals(mTextView.getLayout().getLineTop(0), rc.top);
-        assertEquals(mTextView.getLayout().getLineBottom(0), rc.bottom);
+        assertEquals(mTextView.getLayout().getLineBottom(0), rc.bottom, 0.4f);
 
         // Exception
         try {
diff --git a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
index 00db05f..44d48a5 100644
--- a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
@@ -18,12 +18,10 @@
 
 import com.android.cts.stub.R;
 
-import dalvik.annotation.BrokenTest;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
 import dalvik.annotation.TestTargets;
-import dalvik.annotation.ToBeFixed;
 
 import android.app.Activity;
 import android.app.Instrumentation;
@@ -32,9 +30,7 @@
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
 import android.media.MediaPlayer.OnPreparedListener;
-import android.net.Uri;
 import android.test.ActivityInstrumentationTestCase2;
-import android.view.KeyEvent;
 import android.view.View.MeasureSpec;
 import android.view.animation.cts.DelayedCheck;
 import android.widget.MediaController;
@@ -240,142 +236,6 @@
         }.run();
     }
 
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "setVideoURI",
-            args = {android.net.Uri.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "setOnPreparedListener",
-            args = {android.media.MediaPlayer.OnPreparedListener.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "isPlaying",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "pause",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "start",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "seekTo",
-            args = {int.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "stopPlayback",
-            args = {}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "getCurrentPosition",
-            args = {}
-        )
-    })
-    @BrokenTest("Fails in individual mode (current pos > 0 before start)")
-    public void testPlayVideo2() throws Throwable {
-        final int seekTo = mVideoView.getDuration() >> 1;
-        final MockOnPreparedListener listener = new MockOnPreparedListener();
-        mVideoView.setOnPreparedListener(listener);
-
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                mVideoView.setVideoURI(Uri.parse(mVideoPath));
-            }
-        });
-        new DelayedCheck(TIME_OUT) {
-            @Override
-            protected boolean check() {
-                return listener.isTriggered();
-            }
-        }.run();
-        assertEquals(0, mVideoView.getCurrentPosition());
-
-        // test start
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                mVideoView.start();
-            }
-        });
-        new DelayedCheck(TIME_OUT) {
-            @Override
-            protected boolean check() {
-                return mVideoView.isPlaying();
-            }
-        }.run();
-        assertTrue(mVideoView.getCurrentPosition() > 0);
-
-        // test pause
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                mVideoView.pause();
-            }
-        });
-        new DelayedCheck(TIME_OUT) {
-            @Override
-            protected boolean check() {
-                return !mVideoView.isPlaying();
-            }
-        }.run();
-        int currentPosition = mVideoView.getCurrentPosition();
-
-        // sleep a second and then check whether player is paused.
-        Thread.sleep(OPERATION_INTERVAL);
-        assertEquals(currentPosition, mVideoView.getCurrentPosition());
-
-        // test seekTo
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                mVideoView.seekTo(seekTo);
-            }
-        });
-        new DelayedCheck(TIME_OUT) {
-            @Override
-            protected boolean check() {
-                return mVideoView.getCurrentPosition() >= seekTo;
-            }
-        }.run();
-        assertFalse(mVideoView.isPlaying());
-
-        // test start again
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                mVideoView.start();
-            }
-        });
-        new DelayedCheck(TIME_OUT) {
-            @Override
-            protected boolean check() {
-                return mVideoView.isPlaying();
-            }
-        }.run();
-        assertTrue(mVideoView.getCurrentPosition() > seekTo);
-
-        // test stop
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                mVideoView.stopPlayback();
-            }
-        });
-        new DelayedCheck(TIME_OUT) {
-            @Override
-            protected boolean check() {
-                return !mVideoView.isPlaying();
-            }
-        }.run();
-        assertEquals(0, mVideoView.getCurrentPosition());
-    }
-
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         method = "setOnErrorListener",
@@ -449,92 +309,6 @@
     }
 
     @TestTargetNew(
-        level = TestLevel.NOT_NECESSARY,
-        method = "onTouchEvent",
-        args = {android.view.MotionEvent.class}
-    )
-    public void testOnTouchEvent() {
-        // onTouchEvent() is implementation details, do NOT test
-    }
-
-    @TestTargetNew(
-        level = TestLevel.SUFFICIENT,
-        method = "onKeyDown",
-        args = {int.class, android.view.KeyEvent.class}
-    )
-    @ToBeFixed(bug = "", explanation = "After pressing KEYCODE_HEADSETHOOK, "
-            + "the video should be playing, but it did not until time out.")
-    @BrokenTest("Video starts playing automatically after setting the path.")
-    public void testOnKeyDown() throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                mVideoView.setVideoPath(mVideoPath);
-                mVideoView.requestFocus();
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertFalse(mVideoView.isPlaying());
-        sendKeys(KeyEvent.KEYCODE_HEADSETHOOK);
-        // video should be played.
-        new DelayedCheck(TIME_OUT) {
-            @Override
-            protected boolean check() {
-                return !mVideoView.isPlaying();
-            }
-        }.run();
-        assertFalse(mMediaController.isShowing());
-
-        sendKeys(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
-        new DelayedCheck(TIME_OUT) {
-            @Override
-            protected boolean check() {
-                return !mVideoView.isPlaying();
-            }
-        }.run();
-        // MediaController should show
-        assertFalse(mMediaController.isShowing());
-
-        runTestOnUiThread(new Runnable() {
-            public void run() {
-                mVideoView.start();
-            }
-        });
-        new DelayedCheck(TIME_OUT) {
-            @Override
-            protected boolean check() {
-                return mVideoView.isPlaying();
-            }
-        }.run();
-
-        sendKeys(KeyEvent.KEYCODE_MEDIA_STOP);
-        new DelayedCheck(TIME_OUT) {
-            @Override
-            protected boolean check() {
-                return !mVideoView.isPlaying();
-            }
-        }.run();
-    }
-
-    @TestTargetNew(
-        level = TestLevel.NOT_NECESSARY,
-        method = "onMeasure",
-        args = {int.class, int.class}
-    )
-    public void testOnMeasure() {
-        // Do not test onMeasure(), implementation details
-    }
-
-    @TestTargetNew(
-        level = TestLevel.NOT_NECESSARY,
-        method = "onTrackballEvent",
-        args = {android.view.MotionEvent.class}
-    )
-    public void testOnTrackballEvent() {
-        // Do not test onTrackballEvent(), implementation details
-    }
-
-    @TestTargetNew(
         level = TestLevel.COMPLETE,
         method = "getDuration",
         args = {}
diff --git a/tools/annotation-helper/.classpath b/tools/annotation-helper/.classpath
deleted file mode 100644
index bece0f3..0000000
--- a/tools/annotation-helper/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry exported="true" kind="lib" path="bin/"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/tools/annotation-helper/.project b/tools/annotation-helper/.project
deleted file mode 100644
index ea4be4f..0000000
--- a/tools/annotation-helper/.project
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>spechelper</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.pde.ManifestBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.pde.SchemaBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.pde.PluginNature</nature>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/tools/annotation-helper/.settings/org.eclipse.jdt.core.prefs b/tools/annotation-helper/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index c4095e1..0000000
--- a/tools/annotation-helper/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,57 +0,0 @@
-#Wed Jun 04 14:49:33 CEST 2008
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=ignore
-org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
-org.eclipse.jdt.core.compiler.problem.deprecation=ignore
-org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
-org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
-org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore
-org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
-org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
-org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
-org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
-org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore
-org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
-org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
-org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
-org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
-org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
-org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
-org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
-org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
-org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
-org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
-org.eclipse.jdt.core.compiler.problem.nullReference=ignore
-org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
-org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
-org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
-org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
-org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=ignore
-org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
-org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
-org.eclipse.jdt.core.compiler.problem.typeParameterHiding=ignore
-org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
-org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
-org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
-org.eclipse.jdt.core.compiler.problem.unusedImport=warning
-org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
-org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
-org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
-org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
-org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
-org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
-org.eclipse.jdt.core.compiler.processAnnotations=disabled
diff --git a/tools/annotation-helper/META-INF/MANIFEST.MF b/tools/annotation-helper/META-INF/MANIFEST.MF
deleted file mode 100644
index 1f39a53..0000000
--- a/tools/annotation-helper/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,21 +0,0 @@
-Manifest-Version: 1.0
-Bundle-ManifestVersion: 2
-Bundle-Name: Spechelper Plug-in
-Bundle-SymbolicName: spechelper; singleton:=true
-Bundle-Version: 1.0.0
-Bundle-Activator: spechelper.Activator
-Require-Bundle: org.eclipse.ui,
- org.eclipse.core.runtime
-Eclipse-LazyStart: false
-Import-Package: org.eclipse.jdt.core,
- org.eclipse.jdt.core.compiler,
- org.eclipse.jdt.core.dom,
- org.eclipse.jdt.core.util,
- org.eclipse.jdt.internal.ui,
- org.eclipse.jdt.internal.ui.javaeditor,
- org.eclipse.jdt.ui,
- org.eclipse.jdt.ui.text.java,
- org.eclipse.jface.text,
- org.eclipse.jface.text.contentassist,
- org.eclipse.ui.texteditor
-Bundle-ClassPath: bin/
diff --git a/tools/annotation-helper/README.txt b/tools/annotation-helper/README.txt
deleted file mode 100644
index 8ff7eec..0000000
--- a/tools/annotation-helper/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-for information, read the javadoc under src/spechelper/SimpleComputer.java
diff --git a/tools/annotation-helper/build.properties b/tools/annotation-helper/build.properties
deleted file mode 100644
index e5dc709..0000000
--- a/tools/annotation-helper/build.properties
+++ /dev/null
@@ -1,9 +0,0 @@
-output.. = bin/
-bin.includes = plugin.xml,\
-               META-INF/,\
-               bin/,\
-               build.properties,\
-               feature.xml
-src.includes = src/,\
-               plugin.xml,\
-               feature.xml
diff --git a/tools/annotation-helper/plugin.xml b/tools/annotation-helper/plugin.xml
deleted file mode 100644
index 6cc641d..0000000
--- a/tools/annotation-helper/plugin.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?eclipse version="3.2"?>
-<plugin>
-	<extension 
-		point="org.eclipse.jdt.ui.javaCompletionProposalComputer" 
-		id="WordCompletionProposalComputer"
-		name="Noser Annotation Completion Proposal Computer">
-
-		<javaCompletionProposalComputer 
-		    activate="true"
-			class="spechelper.SimpleComputer"
-			categoryId="org.eclipse.ui.texteditor.textual_proposals">
-		</javaCompletionProposalComputer>
-	</extension>
-</plugin>
diff --git a/tools/annotation-helper/src/spechelper/MethodSelector.java b/tools/annotation-helper/src/spechelper/MethodSelector.java
deleted file mode 100644
index a0b4bba..0000000
--- a/tools/annotation-helper/src/spechelper/MethodSelector.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2008 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 spechelper;
-
-import org.eclipse.jdt.core.Flags;
-import org.eclipse.jdt.core.ICompilationUnit;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.IMethod;
-import org.eclipse.jdt.core.IType;
-import org.eclipse.jdt.core.ITypeParameter;
-import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jdt.core.Signature;
-import org.eclipse.jdt.core.dom.AST;
-import org.eclipse.jdt.core.dom.ASTParser;
-import org.eclipse.jdt.core.dom.ASTVisitor;
-import org.eclipse.jdt.core.dom.CompilationUnit;
-import org.eclipse.jdt.core.dom.Expression;
-import org.eclipse.jdt.core.dom.ITypeBinding;
-import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
-import org.eclipse.jdt.internal.ui.JavaPlugin;
-import org.eclipse.jdt.ui.JavaElementLabelProvider;
-import org.eclipse.jface.dialogs.IDialogConstants;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.IEditorInput;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.dialogs.ElementListSelectionDialog;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 
- */
-public class MethodSelector {
-
-    public String obtainReplacement(String buffer) {
-        IMethod method = selectMethod();
-        // if user did cancel the selection
-        if (method == null) {
-            return null;
-        }
-
-        // see if we are already in a annotation:
-        // if yes -> only dump the testtarget annotation, not the complete
-        // TestInfo
-        // (could not easily find this out with CompilationUnit, since inserting
-        // a :
-        // broke the AST - maybe use WorkingCopy and so on,
-        // but for now: do it with simple String analysis
-        boolean shortOnly = false;
-        int annotPos = buffer.lastIndexOf("@TestInfo");
-        // the latest annotation - count "(" ")" pairs - if not the same count
-        // we assume to be in the annotation (H: code compiles fine)
-        if (annotPos != -1) {
-            String sub = buffer.substring(annotPos);
-            // only consider the latest 6 lines for the annotation to occur
-            // (6 = range within which the annotation @TestTarget
-            // must occur, but out of range to reach the annotation from the
-            // previous method - ah i'd prefer working with compilationUnit...
-            String[] lines = sub.split("\n");
-            for (int i = lines.length - 6; i < lines.length; i++) {
-                String line = lines[i];
-                if (line.contains("@TestTarget")) {
-                    shortOnly = true;
-                }
-            }
-        }
-
-        return generateAnnotation(shortOnly, method);
-    }
-
-
-    private String generateAnnotation(boolean shortOnly, IMethod method) {
-        String[] ptypes = method.getParameterTypes();
-        String param = "";
-        for (int i = 0; i < ptypes.length; i++) {
-            String ptype = ptypes[i];
-            String sig = Signature.toString(ptype);
-            // kind of a hack: convert all Generic Type args to Object, or to
-            // its bound Type
-            if (sig.length() == 1) {
-                ITypeParameter tps = method.getTypeParameter(sig);
-                sig = "Object";
-
-                if (tps != null && tps.exists()) {
-                    try {
-                        String[] bounds = tps.getBounds();
-                        if (bounds.length > 0) {
-                            sig = bounds[0];
-                        }
-                    } catch (JavaModelException e) {
-                        e.printStackTrace();
-                    }
-
-                }
-            }
-            // omit type signature
-            sig = sig.replaceAll("<.*>", "");
-            param += (i > 0 ? ", " : "") + sig + ".class";
-        }
-        String IND = "    ";
-
-        String targ = "@TestTarget(\n" + IND + "      methodName = \""
-                + method.getElementName() + "\",\n" + IND
-                + "      methodArgs = {" + param + "}\n" + IND + "    )\n";
-
-        String s;
-        if (shortOnly) {
-            s = targ;
-        } else {
-
-            s = "@TestInfo(\n" + IND + "  status = TestStatus.TBR,\n" + IND
-                    + "  notes = \"\",\n" + IND + "  targets = {\n" + IND
-                    + "    " + targ + IND + "})";
-        }
-        return s;
-    }
-
-    private IMethod selectMethod() {
-        IEditorPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
-                .getActivePage().getActiveEditor();
-        Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
-                .getShell();
-        IEditorInput ei = part.getEditorInput();
-        final ICompilationUnit cu = JavaPlugin.getDefault()
-                .getWorkingCopyManager().getWorkingCopy(ei);
-        // cu != null since we register only for java/javadoc completion
-        // proposals
-        ASTParser parser = ASTParser.newParser(AST.JLS3);
-        parser.setSource(cu);
-        parser.setResolveBindings(true);
-        CompilationUnit unit = (CompilationUnit) parser.createAST(null);
-
-        class MHolder {
-            IMethod method;
-        }
-        final MHolder mholder = new MHolder();
-
-        class FHolder {
-            boolean foundClassAnnotation;
-        }
-        final FHolder fholder = new FHolder();
-
-        unit.accept(new ASTVisitor() {
-            public boolean visit(SingleMemberAnnotation node) {
-                String name = node.getTypeName().getFullyQualifiedName();
-                if (!name.equals("TestTargetClass")) {
-                    return false;
-                }
-                fholder.foundClassAnnotation = true;
-                Expression targetClassE = node.getValue();
-                ITypeBinding ty = targetClassE.resolveTypeBinding();
-                if (ty == null) {
-                    return false;
-                }
-                ITypeBinding[] classTypes = ty.getTypeArguments();
-                if (classTypes.length > 0) {
-                    ITypeBinding tp = classTypes[0];
-                    String qname = tp.getQualifiedName();
-                    System.out.println("qname:" + qname);
-                    IJavaProject myProject = cu.getJavaProject();
-                    try {
-                        IType myType = myProject.findType(qname);
-                        if (myType != null) {
-                            Shell parent = PlatformUI.getWorkbench()
-                                    .getActiveWorkbenchWindow().getShell();
-                            ElementListSelectionDialog dialog = new ElementListSelectionDialog(
-                                    parent,
-                                    new JavaElementLabelProvider(
-                                            JavaElementLabelProvider.SHOW_PARAMETERS
-                                                    | JavaElementLabelProvider.SHOW_OVERLAY_ICONS
-                                                    | JavaElementLabelProvider.SHOW_RETURN_TYPE));
-                            // restrict to public/protected methods only
-                            IMethod[] allMeth = myType.getMethods();
-                            List<IMethod> pubproMethods = new ArrayList<IMethod>();
-                            for (int i = 0; i < allMeth.length; i++) {
-                                IMethod method = allMeth[i];
-                                if ((method.getFlags() & (Flags.AccPublic | Flags.AccProtected)) != 0) {
-                                    pubproMethods.add(method);
-                                }
-                            }
-                            IMethod[] res = pubproMethods
-                                    .toArray(new IMethod[pubproMethods.size()]);
-                            dialog.setIgnoreCase(true);
-                            dialog.setBlockOnOpen(true);
-                            dialog.setElements(res);//
-                            dialog.setFilter("");
-                            dialog.setTitle(qname);
-                            if (dialog.open() != IDialogConstants.CANCEL_ID) {
-                                Object[] types = dialog.getResult();
-                                System.out.println("selected:" + types[0]);
-                                IMethod method = (IMethod) types[0];
-                                mholder.method = method;
-
-                            } else {
-                                // System.out.println("cancelled!!");
-                            }
-                        }
-                    } catch (JavaModelException e) {
-                        e.printStackTrace();
-                    }
-                }
-                return true;
-            }
-        });
-        if (!fholder.foundClassAnnotation) {
-            MessageDialog.openInformation(shell, "Class Annotation missing",
-                    "@TestTargetClass(...) is missing");
-            return null;
-        }
-        return mholder.method;
-    }
-}
diff --git a/tools/annotation-helper/src/spechelper/MyCompletion.java b/tools/annotation-helper/src/spechelper/MyCompletion.java
deleted file mode 100644
index 7a5c3c7..0000000
--- a/tools/annotation-helper/src/spechelper/MyCompletion.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2008 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 spechelper;
-
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.contentassist.ICompletionProposal;
-import org.eclipse.jface.text.contentassist.IContextInformation;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Point;
-
-public class MyCompletion implements ICompletionProposal {
-
-    private String m_displ;
-    private String m_replace;
-    private int m_offset;
-    private int m_replacelen;
-    private int m_cursorpos;
-    private Image m_img;
-    private IContextInformation m_context;
-    private String m_addinfo;
-    private String fBuffer;
-
-    public MyCompletion(String buffer, String replacementString,
-            int replacementOffset, int replacementLength, int cursorPosition,
-            Image image, String displayString,
-            IContextInformation contextInformation,
-            String additionalProposalInfo) {
-        m_replace = replacementString;
-        m_offset = replacementOffset;
-        m_replacelen = replacementLength;
-        m_cursorpos = cursorPosition;
-        m_img = image;
-        fBuffer = buffer;
-        m_displ = displayString;
-        m_context = contextInformation;
-        m_addinfo = additionalProposalInfo;
-    }
-
-    public void apply(IDocument document) {
-        try {
-            MethodSelector ms = new MethodSelector();
-            String replace = ms.obtainReplacement(fBuffer);
-            if (replace == null) {
-                m_cursorpos = 0;
-                return;
-            }
-            m_cursorpos = replace.length();
-            document.replace(m_offset, m_replacelen, replace);
-        } catch (BadLocationException x) {
-            // ignore
-        }
-    }
-
-    public Point getSelection(IDocument document) {
-        return new Point(m_offset + m_cursorpos, 0);
-    }
-
-    public IContextInformation getContextInformation() {
-        return m_context;
-    }
-
-    public Image getImage() {
-        return m_img;
-    }
-
-    public String getDisplayString() {
-        if (m_displ != null) return m_displ;
-        return m_replace;
-    }
-
-    public String getAdditionalProposalInfo() {
-        return m_addinfo;
-    }
-}
diff --git a/tools/annotation-helper/src/spechelper/SimpleComputer.java b/tools/annotation-helper/src/spechelper/SimpleComputer.java
deleted file mode 100644
index 757b1a8..0000000
--- a/tools/annotation-helper/src/spechelper/SimpleComputer.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2008 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 spechelper;
-
-import dalvik.annotation.TestTargetClass;
-
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext;
-import org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.contentassist.ICompletionProposal;
-
-import java.util.List;
-import java.util.Vector;
-import java.util.regex.Pattern;
-
-/**
- * <p>
- * a plugin to auto-insert the following annotation constructs: 
- * TestInfo.java, TestStatus.java, TestTarget.java, and TestTargetClass.java
- * under 
- * /android/device/dalvik/libcore/dalvik/src/main/java/dalvik/annotation/
- * <p>
- * usage:<br>
- * - install export/plugins/spechelper_1.0.0.jar into your eclipse/plugin folder.<br>
- * - restart eclipse<br>
- * - open a java file<br>
- * - insert the TestTargetClass annotation above the class declaration, e.g.
- *   <code>@TestTargetClass(Pattern.class)</code><br>
- * - insert a ":" one line above the signature of a method to be annotated,  
- *   and press ctrl-space for eclipse autocompletion. a popup appears which
- *   lists all target methods. choose one, and the annotation will be filled in
- *   at the cursor position.<br>
- * <p>  
- *   to annotate more than one target method, simply add a comma after the
- *   first TestTarget, press enter and insert a ":", press ctrl-space again.
- *   
- * <p>
- *  a sample:  
- *   
-<pre>
-package org.apache.harmony.tests.java.util.regex;
-
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestInfo;
-import dalvik.annotation.TestTarget;
-import dalvik.annotation.TestStatus;
-
-import junit.framework.TestCase;
-
-import java.util.regex.Pattern;
-
-@TestTargetClass(Pattern.class)
-
-public class PatternTest extends TestCase {
-    
-    // add ":", press ctrl-space here to let the eclipse plugin generate 
-    // the next few lines
-    @TestInfo(
-      status = TestStatus.TBR,
-      notes = "",
-      targets = {
-        @TestTarget(
-          methodName = "compile",
-          methodArgs = {String.class}
-        )
-    })
-    public void foo() {
-        //
-    }
-
-    @TestInfo(
-      status = TestStatus.TBR,
-      notes = "",
-      targets = {
-        @TestTarget(
-          methodName = "compile",
-          methodArgs = {String.class}
-        ),
-        // add ":", press ctrl-space here to insert another TestTarget
-    })
-    public void bar() {
-        //
-    }
-    
-    @TestInfo(
-      status = TestStatus.TBR,
-      notes = "",
-      targets = {
-        @TestTarget(
-          methodName = "compile",
-          methodArgs = {String.class}
-        ),
-        @TestTarget(
-          methodName = "split",
-          methodArgs = {CharSequence.class, int.class}
-        )
-
-    })
-    public void foobarsample() {
-        //
-    }
-    
-}
-</pre>
- *   
- *   
- *
- */
-public class SimpleComputer implements IJavaCompletionProposalComputer {
-
-    public List<ICompletionProposal> computeCompletionProposals(
-            ContentAssistInvocationContext context, IProgressMonitor monitor) {
-        List<ICompletionProposal> ret = new Vector<ICompletionProposal>();
-        try {
-            int offs = context.getInvocationOffset();
-            String buffer = context.getDocument().get(0, offs);
-            //System.out.println("buffer:'"+buffer+"'");
-            //System.out.println("offset:"+offs);
-            String keyWord = ":";
-            String keyWordInfo = "':': noser: autofills the annotation";
-
-            int idx = 0;
-            // find the replacement position
-            int klen = keyWord.length();
-            for (int i = 0; i < klen; i++) {
-                String test = keyWord.substring(0, klen - i);
-                if (buffer.endsWith(test)) {
-                    idx = klen - i;
-                    break;
-                }
-            }
-            if (idx != 0) {
-                System.out.println("idx:"+idx);
-                String replace ="hi there! a longer sample text\nnew line";
-                    ICompletionProposal ci = new MyCompletion(buffer, replace,
-                            context.getInvocationOffset() - idx, idx, replace
-                                    .length(), null, keyWordInfo, null, null);
-                    ret.add(ci);
-            }
-        } catch (BadLocationException e) {
-            e.printStackTrace();
-        }
-        return ret;
-    }
-
-
-    public List<ICompletionProposal> computeContextInformation(
-            ContentAssistInvocationContext context, IProgressMonitor monitor) {
-        return new Vector<ICompletionProposal>();
-    }
-
-    public String getErrorMessage() {
-        return "Error from SimpleComputer";
-    }
-
-    public void sessionEnded() {
-        //System.out.println("session ended");
-    }
-
-    public void sessionStarted() {
-        //System.out.println("session started");
-    }
-
-}
diff --git a/tools/annotation-helper/src/spechelper/Test.java b/tools/annotation-helper/src/spechelper/Test.java
deleted file mode 100644
index 01b11df..0000000
--- a/tools/annotation-helper/src/spechelper/Test.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2008 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 spechelper;
-
-import java.util.Comparator;
-
-public class Test {
-    
-    public Test() {}
-    
-    public <E>  void bla(Comparable<Long> comp1, Comparator<Comparable<String>> comp2, E arg, int a, byte[] b, char[] c, double d, float f, boolean bo, int[][] ar, String[][] arr,
-            long l, String... strs) {
-    }
-    
-    public String fooaaa(int aasdfsdfsd) {
-        return null;
-    }
-    
-    private void priv() {}
-
-    protected void prot() {}
-    
-    void packloc() {}
-    
-    public void testInputfoo() {
-        Math.sin(0d);
-    }
-}
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
index 54d00b2..8b00ddf 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
@@ -31,17 +31,22 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import java.io.IOException;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Scanner;
 import java.util.Set;
 
 public class DeviceInfoInstrument extends Instrumentation {
 
     private static final String TAG = "DeviceInfoInstrument";
 
-    private static final String OPEN_GL_ES_VERSION = "opengles_version";
+    // constants for device info attributes to be sent as instrumentation keys
+    // these values should correspond to attributes defined in cts_result.xsd
+    private static final String PARTITIONS = "partitions";
+    private static final String OPEN_GL_ES_VERSION = "openGlEsVersion";
     private static final String PROCESSES = "processes";
     private static final String FEATURES = "features";
     private static final String PHONE_NUMBER = "phoneNumber";
@@ -51,29 +56,27 @@
     private static final String NETWORK = "network";
     public static final String KEYPAD = "keypad";
     public static final String NAVIGATION = "navigation";
-    public static final String TOUCH_SCREEN = "touch_screen";
+    public static final String TOUCH_SCREEN = "touch";
+    private static final String SCREEN_Y_DENSITY = "Ydpi";
+    private static final String SCREEN_X_DENSITY = "Xdpi";
     private static final String SCREEN_SIZE = "screen_size";
     private static final String SCREEN_DENSITY_BUCKET = "screen_density_bucket";
-    private static final String SCREEN_Y_DENSITY = "screen_Y_density";
-    private static final String SCREEN_X_DENSITY = "screen_X_density";
     private static final String SCREEN_DENSITY = "screen_density";
     private static final String SCREEN_HEIGHT = "screen_height";
     private static final String SCREEN_WIDTH = "screen_width";
-    private static final String VERSION_SDK = "version_sdk";
-    private static final String VERSION_RELEASE = "version_release";
-    private static final String VERSION_INCREMENTAL = "version_incremental";
+    private static final String VERSION_SDK = "androidPlatformVersion";
+    private static final String VERSION_RELEASE = "buildVersion";
     private static final String BUILD_ABI = "build_abi";
     private static final String BUILD_ABI2 = "build_abi2";
     private static final String BUILD_FINGERPRINT = "build_fingerprint";
-    private static final String BUILD_TAGS = "build_tags";
     private static final String BUILD_TYPE = "build_type";
     private static final String BUILD_MODEL = "build_model";
     private static final String BUILD_BRAND = "build_brand";
     private static final String BUILD_MANUFACTURER = "build_manufacturer";
     private static final String BUILD_BOARD = "build_board";
     private static final String BUILD_DEVICE = "build_device";
-    private static final String PRODUCT_NAME = "product_name";
-    private static final String BUILD_ID = "build_id";
+    private static final String PRODUCT_NAME = "buildName";
+    private static final String BUILD_ID = "buildID";
     private static Bundle mResults = new Bundle();
 
     public DeviceInfoInstrument() {
@@ -96,12 +99,10 @@
         addResult(BUILD_BRAND, Build.BRAND);
         addResult(BUILD_MODEL, Build.MODEL);
         addResult(BUILD_TYPE, Build.TYPE);
-        addResult(BUILD_TAGS, Build.TAGS);
         addResult(BUILD_FINGERPRINT, Build.FINGERPRINT);
         addResult(BUILD_ABI, Build.CPU_ABI);
         addResult(BUILD_ABI2, Build.CPU_ABI2);
 
-        addResult(VERSION_INCREMENTAL, Build.VERSION.INCREMENTAL);
         addResult(VERSION_RELEASE, Build.VERSION.RELEASE);
         addResult(VERSION_SDK, Build.VERSION.SDK);
 
@@ -160,6 +161,9 @@
         String openGlEsVersion = getOpenGlEsVersion();
         addResult(OPEN_GL_ES_VERSION, openGlEsVersion);
 
+        // partitions
+        String partitions = getPartitions();
+        addResult(PARTITIONS, partitions);
 
         finish(Activity.RESULT_OK, mResults);
     }
@@ -331,4 +335,22 @@
         }
         return "No feature for Open GL ES version.";
     }
+
+    private String getPartitions() {
+        try {
+            StringBuilder builder = new StringBuilder();
+            Process df = new ProcessBuilder("df").start();
+            Scanner scanner = new Scanner(df.getInputStream());
+            try {
+                while (scanner.hasNextLine()) {
+                    builder.append(scanner.nextLine()).append(';');
+                }
+                return builder.toString();
+            } finally {
+                scanner.close();
+            }
+        } catch (IOException e) {
+            return "Not able to run df for partition information.";
+        }
+    }
 }
diff --git a/tools/host/etc/cts b/tools/host/etc/cts
index c368db2..ec8ff0b 100755
--- a/tools/host/etc/cts
+++ b/tools/host/etc/cts
@@ -17,10 +17,9 @@
 
 CTS_SH=cts
 CTS_LIB=cts.jar
-DDMS_LIB=ddmlib.jar
+DDMS_LIB=ddmlib-prebuilt.jar
 JUNIT_LIB=junit.jar
 HOSTTEST_LIB=hosttestlib.jar
-CTS_TEST_ANNOTATIONS_HOST_LIB=CtsTestAnnotationsHostLib.jar
 
 # Checking if "adb" is known by the system
 PATH=.:${PATH}
@@ -38,15 +37,13 @@
     BINARY_DIR=tools
     JAR_DIR=tools/lib
 else if [ ! -f ${ANDROID_ROOT}/bin/${ADB_TOOLS} ]; then
-    echo "Please set your Android Framework root path."
-    echo "Define \"ANDROID_ROOT\" variable"
+    echo "Missing ${ANDROID_ROOT}/bin/${ADB_TOOLS}"
     exit -1;
 fi;
 fi;
 
 if [ ! -f ${ANDROID_ROOT}/${JAR_DIR}/${DDMS_LIB} ]; then
-    echo "Please set your Android Framework root path."
-    echo "Define \"ANDROID_ROOT\" variable"
+    echo "Missing ${ANDROID_ROOT}/${JAR_DIR}/${DDMS_LIB}"
     exit -1;
 fi;
 
@@ -66,7 +63,6 @@
 ${ANDROID_ROOT}/${JAR_DIR}/${DDMS_LIB}:\
 ${ANDROID_ROOT}/${JAR_DIR}/${JUNIT_LIB}:\
 ${ANDROID_ROOT}/${JAR_DIR}/${HOSTTEST_LIB}:\
-${ANDROID_ROOT}/${JAR_DIR}/${CTS_TEST_ANNOTATIONS_HOST_LIB}:\
 ${ANDROID_ROOT}/${JAR_DIR}/${CTS_LIB}
 # Add path to CTS JAR file in the CTS archive
 CTS_LIBS=${CTS_LIBS}:${CTS_DIR}/${CTS_LIB}
diff --git a/tools/host/src/Android.mk b/tools/host/src/Android.mk
index 1fd8159..47a9cb8 100644
--- a/tools/host/src/Android.mk
+++ b/tools/host/src/Android.mk
@@ -21,7 +21,7 @@
 
 LOCAL_JAR_MANIFEST := ../etc/manifest.txt
 LOCAL_JAVA_LIBRARIES := \
-    ddmlib junit hosttestlib CtsTestAnnotationsHostLib
+    ddmlib-prebuilt junit hosttestlib
 
 LOCAL_MODULE := cts
 
diff --git a/tools/host/src/com/android/cts/ConsoleUi.java b/tools/host/src/com/android/cts/ConsoleUi.java
index c9b0e1d..ce26d52 100644
--- a/tools/host/src/com/android/cts/ConsoleUi.java
+++ b/tools/host/src/com/android/cts/ConsoleUi.java
@@ -80,7 +80,6 @@
         mResultCodeMap.put(CtsTestResult.STR_ERROR, CtsTestResult.CODE_ERROR);
         mResultCodeMap.put(CtsTestResult.STR_NOT_EXECUTED, CtsTestResult.CODE_NOT_EXECUTED);
         mResultCodeMap.put(CtsTestResult.STR_TIMEOUT, CtsTestResult.CODE_TIMEOUT);
-        mResultCodeMap.put(CtsTestResult.STR_OMITTED, CtsTestResult.CODE_OMITTED);
     }
 
     public ConsoleUi(TestHost host) {
@@ -1183,9 +1182,9 @@
             CUIOutputStream.println("There aren't any test results!");
         } else {
             CUIOutputStream.println("List of all results: ");
-            CUIOutputStream.println("Session\t\tTest result\t\t\t\t\tStart time\t\tEnd time\t"
+            CUIOutputStream.println("Session\t\tTest result\t\t\t\tStart time\t\tEnd time\t"
                     + "\tTest plan name\t");
-            CUIOutputStream.println("\t\tPass\tFail\tTimeout\tOmitted\tNotExecuted");
+            CUIOutputStream.println("\t\tPass\tFail\tTimeout\tNotExecuted");
 
             for (TestSession session : sessions) {
                 TestSessionLog log = session.getSessionLog();
@@ -1193,8 +1192,6 @@
                         CtsTestResult.CODE_PASS).size();
                 int failNum = log.getTestList(
                         CtsTestResult.CODE_FAIL).size();
-                int omittedNum = log.getTestList(
-                        CtsTestResult.CODE_OMITTED).size();
                 int notExecutedNum = log.getTestList(
                         CtsTestResult.CODE_NOT_EXECUTED).size();
                 int timeOutNum = log.getTestList(
@@ -1202,7 +1199,6 @@
 
                 String resStr = Long.toString(passNum) + "\t" + failNum;
                 resStr += "\t" + timeOutNum;
-                resStr += "\t" + omittedNum;
                 resStr += "\t" + notExecutedNum;
 
                 String startTimeStr =
diff --git a/tools/host/src/com/android/cts/CtsTestResult.java b/tools/host/src/com/android/cts/CtsTestResult.java
index 851b07d..b64863d 100644
--- a/tools/host/src/com/android/cts/CtsTestResult.java
+++ b/tools/host/src/com/android/cts/CtsTestResult.java
@@ -37,14 +37,12 @@
     public static final int CODE_FAIL = 2;
     public static final int CODE_ERROR = 3;
     public static final int CODE_TIMEOUT = 4;
-    public static final int CODE_OMITTED = 5;
     public static final int CODE_FIRST = CODE_INIT;
-    public static final int CODE_LAST = CODE_OMITTED;
+    public static final int CODE_LAST = CODE_TIMEOUT;
 
     public static final String STR_ERROR = "error";
     public static final String STR_TIMEOUT = "timeout";
     public static final String STR_NOT_EXECUTED = "notExecuted";
-    public static final String STR_OMITTED = "omitted";
     public static final String STR_FAIL = "fail";
     public static final String STR_PASS = "pass";
 
@@ -57,7 +55,6 @@
         sCodeToResultMap.put(CODE_FAIL, STR_FAIL);
         sCodeToResultMap.put(CODE_ERROR, STR_ERROR);
         sCodeToResultMap.put(CODE_TIMEOUT, STR_TIMEOUT);
-        sCodeToResultMap.put(CODE_OMITTED, STR_OMITTED);
         sResultToCodeMap = new HashMap<String, Integer>();
         for (int code : sCodeToResultMap.keySet()) {
             sResultToCodeMap.put(sCodeToResultMap.get(code), code);
diff --git a/tools/host/src/com/android/cts/DeviceManager.java b/tools/host/src/com/android/cts/DeviceManager.java
index c164ac1..c794332 100644
--- a/tools/host/src/com/android/cts/DeviceManager.java
+++ b/tools/host/src/com/android/cts/DeviceManager.java
@@ -16,8 +16,10 @@
 
 package com.android.cts;
 
+import com.android.ddmlib.AdbCommandRejectedException;
 import com.android.ddmlib.AndroidDebugBridge;
 import com.android.ddmlib.IDevice;
+import com.android.ddmlib.TimeoutException;
 import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
 
 import java.io.IOException;
@@ -230,6 +232,10 @@
                 mSemaphore.release();
             } catch (IOException e) {
                 // FIXME: handle failed connection to device.
+            } catch (TimeoutException e) {
+                // FIXME: handle failed connection to device.
+            } catch (AdbCommandRejectedException e) {
+                // FIXME: handle failed connection to device.
             }
         }
     }
diff --git a/tools/host/src/com/android/cts/HostConfig.java b/tools/host/src/com/android/cts/HostConfig.java
index 116a46f..fbea3a5 100644
--- a/tools/host/src/com/android/cts/HostConfig.java
+++ b/tools/host/src/com/android/cts/HostConfig.java
@@ -63,6 +63,7 @@
                                                   "logo.gif", "newrule-green.png"};
 
     private String mConfigRoot;
+    private String mLogRoot;
     private CaseRepository mCaseRepos;
     private ResultRepository mResultRepos;
     private PlanRepository mPlanRepos;
@@ -174,6 +175,13 @@
         String planRoot = repositoryRoot + File.separator + planCfg;
         String resRoot = repositoryRoot + File.separator + resCfg;
 
+        String logCfg = getStringAttributeValueOpt(doc, "TestLog", "path", fileName);
+        if (null == logCfg) {
+            mLogRoot = mConfigRoot;
+        } else {
+            mLogRoot = repositoryRoot + File.separator + logCfg;
+        }
+
         boolean validCase = true;
         if (!validateDirectory(caseRoot)) {
             validCase = new File(caseRoot).mkdirs();
@@ -189,12 +197,16 @@
         if (!validateDirectory(planRoot)) {
             validPlan = new File(planRoot).mkdirs();
         }
+        boolean validLog = true;
+        if (!validateDirectory(mLogRoot)) {
+            validLog = new File(mLogRoot).mkdirs();
+        }
 
         mCaseRepos = new CaseRepository(caseRoot);
         mResultRepos = new ResultRepository(resRoot);
         mPlanRepos = new PlanRepository(planRoot);
 
-        return validCase && validRes && validPlan;
+        return validCase && validRes && validPlan && validLog;
     }
 
     /**
@@ -285,6 +297,15 @@
     }
 
     /**
+     * Get the root directory of log files.
+     *
+     * @return the root directory of log files.
+     */
+    public String getLogRoot() {
+        return mLogRoot;
+    }
+
+    /**
      * Get string attribute value.
      *
      * @param doc The document.
@@ -317,6 +338,29 @@
     }
 
     /**
+     * Get string attribute value if it exists.
+     *
+     * @param doc The document.
+     * @param tagName The tag name.
+     * @param attrName The attribute name.
+     * @param fileName The file name.
+     * @return The attribute value.
+     */
+    private String getStringAttributeValueOpt(final Document doc,
+            final String tagName, final String attrName, final String fileName) {
+
+        String cfgStr = null;
+        try {
+            cfgStr = getStringAttributeValue(doc
+                    .getElementsByTagName(tagName).item(0), attrName);
+        } catch (Exception e) {
+            return null;
+        }
+
+        return cfgStr;
+    }
+
+    /**
      * Load configuration values from config file.
      *
      * @param doc The document from which to load the values.
diff --git a/tools/host/src/com/android/cts/HostUtils.java b/tools/host/src/com/android/cts/HostUtils.java
index 6754274..37222cc 100644
--- a/tools/host/src/com/android/cts/HostUtils.java
+++ b/tools/host/src/com/android/cts/HostUtils.java
@@ -46,10 +46,10 @@
  *
  */
 public class HostUtils {
-    
+
     private static SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy",
             Locale.ENGLISH);
-    
+
     /**
      * Check if the given file exists
      *
@@ -162,14 +162,21 @@
             ZipEntry ze = new ZipEntry(path);
             try {
                 zipOutputStream.putNextEntry(ze);
-                InputStream is = new BufferedInputStream(new FileInputStream(f));
-                byte[] buffer = new byte[4096];
-                int bytesRead = is.read(buffer);
-                while (bytesRead > 0) {
-                    zipOutputStream.write(buffer, 0, bytesRead);
-                    bytesRead = is.read(buffer);
+                InputStream is = null;
+                try {
+                    is = new BufferedInputStream(new FileInputStream(f));
+                    byte[] buffer = new byte[4096];
+                    int bytesRead = is.read(buffer);
+                    while (bytesRead > 0) {
+                        zipOutputStream.write(buffer, 0, bytesRead);
+                        bytesRead = is.read(buffer);
+                    }
+                    zipOutputStream.closeEntry();
+                } finally {
+                    if (is != null) {
+                        is.close();
+                    }
                 }
-                zipOutputStream.closeEntry();
             } catch (IOException e) {
                 ok = false;
                 caughtException = e;
@@ -281,10 +288,10 @@
 
         return fmt.toString();
     }
-    
+
     /**
      * Convert the given byte array into a lowercase hex string.
-     * 
+     *
      * @param arr The array to convert.
      * @return The hex encoded string.
      */
@@ -295,7 +302,7 @@
         }
         return buf.toString();
     }
-    
+
     /**
      * Strip control characters from the given string.
      */
@@ -307,7 +314,7 @@
     public static Date dateFromString(String s) throws ParseException {
         return dateFormat.parse(s);
     }
-    
+
     public static String dateToString(Date d) {
         return dateFormat.format(d);
     }
diff --git a/tools/host/src/com/android/cts/ReferenceAppTestPackage.java b/tools/host/src/com/android/cts/ReferenceAppTestPackage.java
index 1b32576..0b6aa50 100644
--- a/tools/host/src/com/android/cts/ReferenceAppTestPackage.java
+++ b/tools/host/src/com/android/cts/ReferenceAppTestPackage.java
@@ -16,8 +16,10 @@
 
 package com.android.cts;
 
+import com.android.ddmlib.AdbCommandRejectedException;
 import com.android.ddmlib.MultiLineReceiver;
 import com.android.ddmlib.RawImage;
+import com.android.ddmlib.TimeoutException;
 import com.android.ddmlib.log.LogReceiver.ILogListener;
 import com.android.ddmlib.log.LogReceiver.LogEntry;
 
@@ -199,6 +201,10 @@
                     }
                 } catch (IOException e) {
                     Log.e("Error taking snapshot! " + cmdArgs, e);
+                } catch (TimeoutException e) {
+                    Log.e("Error taking snapshot! " + cmdArgs, e);
+                } catch (AdbCommandRejectedException e) {
+                    Log.e("Error taking snapshot! " + cmdArgs, e);
                 }
             }
         });
diff --git a/tools/host/src/com/android/cts/TestDevice.java b/tools/host/src/com/android/cts/TestDevice.java
index 987fd96..672ed37 100644
--- a/tools/host/src/com/android/cts/TestDevice.java
+++ b/tools/host/src/com/android/cts/TestDevice.java
@@ -16,6 +16,7 @@
 
 package com.android.cts;
 
+import com.android.ddmlib.AdbCommandRejectedException;
 import com.android.ddmlib.Client;
 import com.android.ddmlib.ClientData;
 import com.android.ddmlib.IDevice;
@@ -23,13 +24,16 @@
 import com.android.ddmlib.MultiLineReceiver;
 import com.android.ddmlib.NullOutputReceiver;
 import com.android.ddmlib.RawImage;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.SyncException;
 import com.android.ddmlib.SyncService;
+import com.android.ddmlib.TimeoutException;
 import com.android.ddmlib.SyncService.ISyncProgressMonitor;
-import com.android.ddmlib.SyncService.SyncResult;
 import com.android.ddmlib.log.LogReceiver;
 import com.android.ddmlib.log.LogReceiver.ILogListener;
 
 import java.io.BufferedReader;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -101,6 +105,8 @@
             try {
                 mDevice.runLogService("main", logReceiver);
             } catch (IOException e) {
+            } catch (TimeoutException e) {
+            } catch (AdbCommandRejectedException e) {
             }
         }
 
@@ -136,6 +142,10 @@
             mSyncService = mDevice.getSyncService();
         } catch (IOException e) {
             // FIXME: handle failed connection.
+        } catch (TimeoutException e) {
+            // FIXME: handle failed connection.
+        } catch (AdbCommandRejectedException e) {
+            // FIXME: handle failed connection.
         }
         mBatchModeResultParser = null;
         mUninstallObserver = new PackageActionObserver(ACTION_UNINSTALL);
@@ -391,9 +401,11 @@
      * Store the build information of a device
      */
     public static final class DeviceParameterCollector{
-        public static final String PRODUCT_NAME = "product_name";
-        public static final String BUILD_VERSION = "version_release";
-        public static final String BUILD_ID = "build_id";
+        // the device info keys expected to be sent from device info instrumentation
+        // these constants should match exactly with those defined in DeviceInfoInstrument.jaa
+        public static final String PRODUCT_NAME = "buildName";
+        public static final String BUILD_VERSION = "buildVersion";
+        public static final String BUILD_ID = "buildID";
         public static final String BUILD_FINGERPRINT = "build_fingerprint";
         public static final String BUILD_TAGS = "build_tags";
         public static final String BUILD_TYPE = "build_type";
@@ -410,11 +422,11 @@
         public static final String SCREEN_DENSITY = "screen_density";
         public static final String SCREEN_DENSITY_BUCKET = "screen_density_bucket";
         public static final String SERIAL_NUMBER = "serialNumber";
-        public static final String VERSION_SDK = "version_sdk";
+        public static final String VERSION_SDK = "androidPlatformVersion";
         public static final String LOCALES = "locales";
-        public static final String SCREEN_Y_DENSITY = "screen_Y_density";
-        public static final String SCREEN_X_DENSITY = "screen_X_density";
-        public static final String TOUCH_SCREEN = "touch_screen";
+        public static final String SCREEN_Y_DENSITY = "Ydpi";
+        public static final String SCREEN_X_DENSITY = "Xdpi";
+        public static final String TOUCH_SCREEN = "touch";
         public static final String NAVIGATION = "navigation";
         public static final String KEYPAD = "keypad";
         public static final String NETWORK = "network";
@@ -423,7 +435,8 @@
         public static final String PHONE_NUMBER = "phoneNumber";
         public static final String FEATURES = "features";
         public static final String PROCESSES = "processes";
-        public static final String OPEN_GL_ES_VERSION = "opengles_version";
+        public static final String OPEN_GL_ES_VERSION = "openGlEsVersion";
+        public static final String PARTITIONS = "partitions";
 
         private HashMap<String, String> mInfoMap;
 
@@ -849,6 +862,15 @@
         public String getOpenGlEsVersion() {
             return mInfoMap.get(OPEN_GL_ES_VERSION);
         }
+
+        /**
+         * Get partitions.
+         *
+         * @return partitions or error message.
+         */
+        public String getPartitions() {
+            return mInfoMap.get(PARTITIONS);
+        }
     }
 
     /**
@@ -957,10 +979,16 @@
      * @param remotePath The remote path.
      */
     public void pushFile(String localPath, String remotePath) {
-        SyncResult result = mSyncService.pushFile(localPath, remotePath,
-                new PushMonitor());
-        if (result.getCode() != SyncService.RESULT_OK) {
-            Log.e("Uploading file failed: " + result.getMessage(), null);
+        try {
+            mSyncService.pushFile(localPath, remotePath, new PushMonitor());
+        } catch (TimeoutException e) {
+            Log.e("Uploading file failed: timeout", null);
+        } catch (SyncException e) {
+            Log.e("Uploading file failed: " + e.getMessage(), null);
+        } catch (FileNotFoundException e) {
+            Log.e("Uploading file failed: " + e.getMessage(), null);
+        } catch (IOException e) {
+            Log.e("Uploading file failed: " + e.getMessage(), null);
         }
     }
 
@@ -1262,7 +1290,7 @@
             mResultLines = new ArrayList<String>();
             mStackTrace = null;
             mFailedMsg = null;
-            mResultCode = CtsTestResult.CODE_PASS;
+            mResultCode = CtsTestResult.CODE_FAIL;
         }
 
         /** {@inheritDoc} */
@@ -1458,10 +1486,6 @@
             case STATUS_ERROR:
                 mResultCode = CtsTestResult.CODE_FAIL;
                 break;
-
-            case STATUS_OMITTED:
-                mResultCode = CtsTestResult.CODE_OMITTED;
-                break;
             }
         }
 
@@ -1527,10 +1551,6 @@
                 case STATUS_PASS:
                     mResultCode = CtsTestResult.CODE_PASS;
                     break;
-
-                case STATUS_OMITTED:
-                    mResultCode = CtsTestResult.CODE_OMITTED;
-                    break;
                 }
                 resultLines.removeAll(resultLines);
             }
@@ -1582,10 +1602,6 @@
                     mTest.setResult(new CtsTestResult(
                             CtsTestResult.CODE_FAIL, mFailedMsg, mStackTrace));
                     break;
-
-                case STATUS_OMITTED:
-                    mTest.setResult(new CtsTestResult(CtsTestResult.CODE_OMITTED));
-                    break;
                 }
             }
             // report status even if no matching test was found
@@ -1715,10 +1731,19 @@
             @Override
             public void run() {
                 try {
-                    mDevice.executeShellCommand(cmd, receiver);
+                    mDevice.executeShellCommand(cmd, receiver, 0);
                 } catch (IOException e) {
                     Log.e(String.format("Failed to execute shell command %s on device %s", cmd,
                             mDevice.getSerialNumber()), e);
+                } catch (TimeoutException e) {
+                    Log.e(String.format("Failed to execute shell command %s on device %s", cmd,
+                            mDevice.getSerialNumber()), e);
+                } catch (AdbCommandRejectedException e) {
+                    Log.e(String.format("Failed to execute shell command %s on device %s", cmd,
+                            mDevice.getSerialNumber()), e);
+                } catch (ShellCommandUnresponsiveException e) {
+                    Log.e(String.format("Failed to execute shell command %s on device %s", cmd,
+                            mDevice.getSerialNumber()), e);
                 }
             }
         }.start();
@@ -1936,8 +1961,11 @@
      *
      * @return the screenshot
      * @throws IOException
+     * @throws AdbCommandRejectedException
+     * @throws TimeoutException
      */
-    public RawImage getScreenshot() throws IOException {
+    public RawImage getScreenshot() throws IOException, TimeoutException,
+            AdbCommandRejectedException {
         return mDevice.getScreenshot();
     }
 }
diff --git a/tools/host/src/com/android/cts/TestHost.java b/tools/host/src/com/android/cts/TestHost.java
index e18bc79..52a0553 100644
--- a/tools/host/src/com/android/cts/TestHost.java
+++ b/tools/host/src/com/android/cts/TestHost.java
@@ -362,7 +362,7 @@
                 exit();
             }
 
-            Log.initLog(sConfig.getConfigRoot());
+            Log.initLog(sConfig.getLogRoot());
             sConfig.loadRepositories();
         } catch (Exception e) {
             Log.e("Error while parsing cts config file", e);
diff --git a/tools/host/src/com/android/cts/TestSession.java b/tools/host/src/com/android/cts/TestSession.java
index fedd756..e3693d8 100644
--- a/tools/host/src/com/android/cts/TestSession.java
+++ b/tools/host/src/com/android/cts/TestSession.java
@@ -485,15 +485,13 @@
         private void displayTestResultSummary() {
             int passNum = mSessionLog.getTestList(CtsTestResult.CODE_PASS).size();
             int failNum = mSessionLog.getTestList(CtsTestResult.CODE_FAIL).size();
-            int omittedNum = mSessionLog.getTestList(CtsTestResult.CODE_OMITTED).size();
             int notExecutedNum = mSessionLog.getTestList(CtsTestResult.CODE_NOT_EXECUTED).size();
             int timeOutNum = mSessionLog.getTestList(CtsTestResult.CODE_TIMEOUT).size();
-            int total = passNum + failNum + omittedNum + notExecutedNum + timeOutNum;
+            int total = passNum + failNum + notExecutedNum + timeOutNum;
 
             println("Test summary:   pass=" + passNum
                     + "   fail=" + failNum
                     + "   timeOut=" + timeOutNum
-                    + "   omitted=" + omittedNum
                     + "   notExecuted=" + notExecutedNum
                     + "   Total=" + total);
         }
diff --git a/tools/host/src/com/android/cts/TestSessionLog.java b/tools/host/src/com/android/cts/TestSessionLog.java
index cc59a3e..bf5b3e8 100644
--- a/tools/host/src/com/android/cts/TestSessionLog.java
+++ b/tools/host/src/com/android/cts/TestSessionLog.java
@@ -46,7 +46,7 @@
     private static final String ATTRIBUTE_KNOWN_FAILURE = "KnownFailure";
 
     public static final String CTS_RESULT_FILE_NAME = "testResult.xml";
-    private static final String CTS_RESULT_FILE_VERSION = "1.10";
+    private static final String CTS_RESULT_FILE_VERSION = "1.11";
 
     static final String ATTRIBUTE_STARTTIME = "starttime";
     static final String ATTRIBUTE_ENDTIME = "endtime";
@@ -73,11 +73,11 @@
     static final String ATTRIBUTE_TYPE = "type";
     static final String ATTRIBUTE_UID = "uid";
     static final String ATTRIBUTE_OPEN_GL_ES_VERSION = "openGlEsVersion";
+    static final String ATTRIBUTE_PARTITIONS = "partitions";
 
     static final String ATTRIBUTE_PASS = "pass";
     static final String ATTRIBUTE_FAILED = "failed";
     static final String ATTRIBUTE_TIMEOUT = "timeout";
-    static final String ATTRIBUTE_OMITTED = "omitted";
     static final String ATTRIBUTE_NOT_EXECUTED = "notExecuted";
 
     static final String TAG_DEVICEINFO = "DeviceInfo";
@@ -327,6 +327,8 @@
                 setAttribute(doc, devInfoNode, ATTRIBUTE_IMSI, bldInfo.getIMSI());
                 setAttribute(doc, devInfoNode, ATTRIBUTE_OPEN_GL_ES_VERSION,
                         bldInfo.getOpenGlEsVersion());
+                setAttribute(doc, devInfoNode, ATTRIBUTE_PARTITIONS,
+                        bldInfo.getPartitions());
 
                 setAttribute(doc, devInfoNode,
                         DeviceParameterCollector.BUILD_FINGERPRINT, bldInfo.getBuildFingerPrint());
@@ -382,14 +384,12 @@
 
             int passNum = getTestList(CtsTestResult.CODE_PASS).size();
             int failNum = getTestList(CtsTestResult.CODE_FAIL).size();
-            int omittedNum = getTestList(CtsTestResult.CODE_OMITTED).size();
             int notExecutedNum = getTestList(CtsTestResult.CODE_NOT_EXECUTED).size();
             int timeOutNum = getTestList(CtsTestResult.CODE_TIMEOUT).size();
             Node summaryNode = doc.createElement(TAG_SUMMARY);
             root.appendChild(summaryNode);
             setAttribute(doc, summaryNode, ATTRIBUTE_PASS, passNum);
             setAttribute(doc, summaryNode, ATTRIBUTE_FAILED, failNum);
-            setAttribute(doc, summaryNode, ATTRIBUTE_OMITTED, omittedNum);
             setAttribute(doc, summaryNode, ATTRIBUTE_NOT_EXECUTED, notExecutedNum);
             setAttribute(doc, summaryNode, ATTRIBUTE_TIMEOUT, timeOutNum);
 
diff --git a/tools/host/src/com/android/cts/Version.java b/tools/host/src/com/android/cts/Version.java
index 5410ed3..d919b20 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 = "2.2_r6";
+    private static final String version = "2.3_r5";
 
     private Version() {
         // no instances allowed
diff --git a/tools/host/src/res/cts_result.xsd b/tools/host/src/res/cts_result.xsd
index 51bad35..ad8205c 100644
--- a/tools/host/src/res/cts_result.xsd
+++ b/tools/host/src/res/cts_result.xsd
@@ -74,6 +74,7 @@
         <xs:attribute name="network" type="xs:string"/>
         <xs:attribute name="touch" type="xs:string"/>
         <xs:attribute name="openGlEsVersion" type="xs:string"/>
+        <xs:attribute name="partitions" type="xs:string"/>
         <xs:attribute name="build_abi" type="xs:string"/>
         <xs:attribute name="build_abi2" type="xs:string"/>
       </xs:complexType>
diff --git a/tools/host/src/res/cts_result.xsl b/tools/host/src/res/cts_result.xsl
index 4317655..b6c9acc 100644
--- a/tools/host/src/res/cts_result.xsl
+++ b/tools/host/src/res/cts_result.xsl
@@ -252,6 +252,19 @@
                                                 </UL>
                                             </TD>
                                         </TR>
+                                        <TR>
+                                            <TD class="rowtitle">Partitions</TD>
+                                            <TD>
+                                                <UL>
+                                                    <pre>
+                                                        <xsl:call-template name="formatDelimitedString">
+                                                            <xsl:with-param name="string" select="TestResult/DeviceInfo/BuildInfo/@partitions" />
+                                                            <xsl:with-param name="numTokensPerRow" select="1" />
+                                                        </xsl:call-template>
+                                                    </pre>
+                                                </UL>
+                                            </TD>
+                                        </TR>
                                     </TABLE>
                                 </div>
                             </TD>
diff --git a/tools/spec-progress/Android.mk b/tools/spec-progress/Android.mk
deleted file mode 100644
index e4026e9..0000000
--- a/tools/spec-progress/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_IS_HOST_MODULE := true
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE := spec-progress
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/spec-progress | $(ACP)
-	@echo "Copy: $(PRIVATE_MODULE) ($@)"
-	$(copy-file-to-new-target)
-	$(hide) chmod 755 $@
-
-include $(LOCAL_PATH)/src/Android.mk
diff --git a/tools/spec-progress/etc/spec-progress b/tools/spec-progress/etc/spec-progress
deleted file mode 100644
index a8f14d7..0000000
--- a/tools/spec-progress/etc/spec-progress
+++ /dev/null
@@ -1,154 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-while [ -h "${prog}" ]; do
-    newProg=`/bin/ls -ld "${prog}"`
-    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
-    if expr "x${newProg}" : 'x/' >/dev/null; then
-        prog="${newProg}"
-    else
-        progdir=`dirname "${prog}"`
-        prog="${progdir}/${newProg}"
-    fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-cd "${oldwd}"
-
-libdir=`dirname $progdir`/framework
-
-javaOpts=""
-while expr "x$1" : 'x-J' >/dev/null; do
-    opt=`expr "$1" : '-J\(.*\)'`
-    javaOpts="${javaOpts} -${opt}"
-    shift
-done
-
-#exec java $javaOpts -jar $libdir/hat.jar "$@"
-
-#######################################################################
-# Original content of invocation script follows. Uses values cleverly
-# deduced by the above code. If you want to use this for a different
-# set of packages, adjust both the list of source directories and the
-# list of packages.
-#######################################################################
-export CLASSES=$progdir/../framework/spec-progress.jar
-export INPUT=$ANDROID_BUILD_TOP
-export OUTPUT=$ANDROID_BUILD_TOP/out/target/common/cts/spec-progress
-
-if [ "$1" != "" ]; then
-   export OUTPUT=$1
-fi
-
-javadoc -J-Xmx512m -docletpath $CLASSES -doclet SpecProgressDoclet -d $OUTPUT -sourcepath \
-$INPUT/dalvik/libcore/dalvik/src/main/java:\
-$INPUT/dalvik/libcore/annotation/src/main/java:\
-$INPUT/dalvik/libcore/archive/src/main/java:\
-$INPUT/dalvik/libcore/auth/src/main/java:\
-$INPUT/dalvik/libcore/awt-kernel/src/main/java:\
-$INPUT/dalvik/libcore/beans/src/main/java:\
-$INPUT/dalvik/libcore/crypto/src/main/java:\
-$INPUT/dalvik/libcore/logging/src/main/java:\
-$INPUT/dalvik/libcore/luni/src/main/java:\
-$INPUT/dalvik/libcore/luni-kernel/src/main/java:\
-$INPUT/dalvik/libcore/math/src/main/java:\
-$INPUT/dalvik/libcore/nio/src/main/java:\
-$INPUT/dalvik/libcore/nio_char/src/main/java:\
-$INPUT/dalvik/libcore/prefs/src/main/java:\
-$INPUT/dalvik/libcore/regex/src/main/java:\
-$INPUT/dalvik/libcore/security/src/main/java:\
-$INPUT/dalvik/libcore/security-kernel/src/main/java:\
-$INPUT/dalvik/libcore/sql/src/main/java:\
-$INPUT/dalvik/libcore/text/src/main/java:\
-$INPUT/dalvik/libcore/xml/src/main/java:\
-$INPUT/dalvik/libcore/x-net/src/main/java:\
- \
-dalvik.annotation \
-dalvik.bytecode \
-dalvik.system \
-java.io \
-java.lang \
-java.lang.annotation \
-java.lang.ref \
-java.lang.reflect \
-java.math \
-java.net \
-java.nio \
-java.nio.channels \
-java.nio.channels.spi \
-java.nio.charset \
-java.nio.charset.spi \
-java.security \
-java.security.acl \
-java.security.cert \
-java.security.interfaces \
-java.security.spec \
-java.sql \
-java.text \
-java.util \
-java.util.jar \
-java.util.logging \
-java.util.prefs \
-java.util.regex \
-java.util.zip \
-javax.crypto \
-javax.crypto.interfaces \
-javax.crypto.spec \
-javax.net \
-javax.net.ssl \
-javax.security.auth \
-javax.security.auth.callback \
-javax.security.auth.login \
-javax.security.auth.x500 \
-javax.security.cert \
-javax.sql \
-javax.xml.parsers \
-org.w3c.dom \
-org.xml.sax \
-org.xml.sax.ext \
-org.xml.sax.helpers \
-
-# Not part of core libs any more:
-# java.lang.instrument \
-# javax.sound.midi \
-# javax.sound.midi.spi \
-# javax.sound.sampled \
-# javax.sound.sampled.spi \
-# java.awt \
-# java.awt.color \
-# java.awt.event \
-# java.awt.font \
-# java.awt.geom \
-# java.awt.im \
-# java.awt.im.spi \
-# java.awt.image \
-# java.awt.image.renderable \
-# javax.imageio \
-# javax.imageio.event \
-# javax.imageio.metadata \
-# javax.imageio.plugins.bmp \
-# javax.imageio.plugins.jpeg \
-# javax.imageio.spi \
-# javax.imageio.stream \
-# java.util.concurrent \
-# java.util.concurrent.atomic \
-# java.util.concurrent.locks \
diff --git a/tools/spec-progress/src/Android.mk b/tools/spec-progress/src/Android.mk
deleted file mode 100644
index 8ab5a7b..0000000
--- a/tools/spec-progress/src/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-#LOCAL_MODULE_TAGS := cts
-
-LOCAL_SRC_FILES := \
-	SpecProgressDoclet.java
-
-LOCAL_CLASSPATH := \
-	$(HOST_JDK_TOOLS_JAR)
-
-LOCAL_MODULE:= spec-progress
-
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/spec-progress/src/SpecProgressDoclet.java b/tools/spec-progress/src/SpecProgressDoclet.java
deleted file mode 100644
index 4c5b6cc..0000000
--- a/tools/spec-progress/src/SpecProgressDoclet.java
+++ /dev/null
@@ -1,1074 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.ConstructorDoc;
-import com.sun.javadoc.Doc;
-import com.sun.javadoc.ExecutableMemberDoc;
-import com.sun.javadoc.FieldDoc;
-import com.sun.javadoc.LanguageVersion;
-import com.sun.javadoc.MemberDoc;
-import com.sun.javadoc.MethodDoc;
-import com.sun.javadoc.PackageDoc;
-import com.sun.javadoc.ParamTag;
-import com.sun.javadoc.Parameter;
-import com.sun.javadoc.RootDoc;
-import com.sun.javadoc.Tag;
-import com.sun.javadoc.ThrowsTag;
-import com.sun.javadoc.TypeVariable;
-
-/**
- * Provides a Doclet for checking the correctness and completeness of the
- * Android core library JavaDoc (aka "the spec"). It generates an HTML-based
- * report vaguely similar to the standard JavaDoc output. The following rules
- * are currently implemented:
- * 
- * Each package must have a package.html doc, and all classes must be documented
- * as described below.
- * 
- * Each class must have an individual doc and all members (fields, constructors,
- * methods) must be documented as described below. All type parameters on class 
- * level need to be documented.
- * 
- * Each member must have an individual doc.
- * 
- * Each executable member (constructor or method) must have a "@param" tag
- * describing each declared parameter. "@param" tags for non-existing parameters
- * are not allowed.
- * 
- * Each method that has a non-void return type must have at least one "@return"
- * tag. A method that has a void return type must not have a "@return" tag.
- * 
- * Each executable member must have a "@throws" tag for each declared exception
- * that does not extend java.lang.RuntimeException or java.lang.Error. This
- * tag may refer to a superclass of the exception actually being thrown. Each
- * exception specified by a "@throws" tag must actually be declared by the
- * member, unless it extends java.lang.RuntimeException or java.lang.Error.
- * Again, the exception being thrown might be more specific than the one
- * documented.
- * 
- * Methods that override or implement another method are allowed to be
- * undocumented, resulting in the inherited documentation being used. If such a
- * method is documented anyway, it must have the complete documentation as
- * described above.
- * 
- * Elements that have a "@hide" JavaDoc tag are not considered part of the
- * official API and hence are not required to be documented.
- * 
- * Based on checking the above rules, the Doclet assigns statuses to individual
- * documentation elements as follows:
- * 
- * Red: the element violates at least one of the above rules.
- * 
- * Yellow: the element fulfills all the above rules, but neither it nor one of
- * its parent elements (class, package) has been marked with the
- * "@since Android-1.0" tag.
- * 
- * Green: the element fulfills all the above rules, it does not have any "@cts"
- * tags, and either it or one if its parent elements (class, package) has been
- * marked with the "@since Android-1.0" tag.
- * 
- * These colors propagate upwards in the hierarchy. Parent elements are assigned
- * colors as follows:
- * 
- * Red: At least on the children is red.
- * 
- * Yellow: None of the children are red and at least one of the children is
- * yellow.
- * 
- * Green: All of the children are green.
- * 
- * The ultimate goal, of course, is to get the summary for the complete API
- * green.
- */
-public class SpecProgressDoclet {
-
-    public static final int TYPE_FIELD = 0;
-
-    public static final int TYPE_METHOD = 1;
-
-    public static final int TYPE_CLASS = 2;
-
-    public static final int TYPE_PACKAGE = 3;
-
-    public static final int TYPE_ROOT = 4;
-
-    public static final int VALUE_RED = 0;
-
-    public static final int VALUE_YELLOW = 1;
-
-    public static final int VALUE_GREEN = 2;
-
-    public static final String[] COLORS = { "#ffa0a0", "#ffffa0", "#a0ffa0" };
-
-    public static final String[] TYPES = { "Field", "Method", "Class",
-                                           "Package", "All packages" };
-
-    /**
-     * Holds our basic output directory.
-     */
-    private File directory;
-
-    /**
-     * Holds a reference to the doc for java.lang.RuntimeException, so we can
-     * compare against it later.
-     */
-    private ClassDoc runtimeException;
-
-    /**
-     * Holds a reference to the doc for java.lang.Error, so we can
-     * compare against it later.
-     */
-    private ClassDoc error;
-
-    /**
-     * States whether to check type parameters on class level. 
-     * To enable these checks use the option: '-Xgeneric' 
-     */
-    private static boolean checkTypeParameters;
-
-    /**
-     * Helper class for comparing element with each other, in oder to determine
-     * an order. Uses lexicographic order of names.
-     */
-    private class DocComparator implements Comparator<Doc> {
-        public int compare(Doc elem1, Doc elem2) {
-            return elem1.name().compareTo(elem2.name());
-        }
-
-        public boolean equals(Doc elem) {
-            return this == elem;
-        }
-    }
-
-    /**
-     * Class for collecting stats and propagating them upwards in the element
-     * hierarchy.
-     */
-    class Stats {
-
-        /**
-         * Holds the element type.
-         */
-        int type;
-
-        /**
-         * Holds the name of the element.
-         */
-        String name;
-
-        /**
-         * Holds information that is sufficient for building a hyperlink.
-         */
-        String link;
-
-        /**
-         * Holds the total number of elements per type (package, class, etc.).
-         */
-        private int[] numbersPerType = new int[4];
-
-        /**
-         * Holds the total number of elements per status value (red, yellow,
-         * green).
-         */
-        private int[] numbersPerValue = new int[3];
-
-        /**
-         * Holds the total number of "@cts" comments.
-         */
-        private int numberOfComments;
-
-        /**
-         * Creates a new Stats instance.
-         */
-        public Stats(int type, String name, String link) {
-            this.type = type;
-            this.name = name;
-            this.link = link;
-        }
-
-        /**
-         * Adds the contents of a single child element to this instance,
-         * propagating values up in the hierachy
-         */
-        public void add(int type, int status, int comments) {
-            numbersPerType[type]++;
-            numbersPerValue[status]++;
-            numberOfComments += comments;
-        }
-
-        /**
-         * Adds the contents of a child Stats instance to this instance,
-         * propagating values up in the hierachy
-         */
-        public void add(Stats stats) {
-            for (int i = 0; i < numbersPerType.length; i++) {
-                numbersPerType[i] += stats.numbersPerType[i];
-            }
-
-            for (int i = 0; i < numbersPerValue.length; i++) {
-                numbersPerValue[i] += stats.numbersPerValue[i];
-            }
-
-            numberOfComments += stats.numberOfComments;
-        }
-
-        /**
-         * Returns the link.
-         */
-        public String getLink() {
-            return link;
-        }
-
-        /**
-         * Returns the name.
-         */
-        public String getName() {
-            return name;
-        }
-
-        /**
-         * Returns the number of elements per element type.
-         */
-        public int getNumbersPerType(int type) {
-            return numbersPerType[type];
-        }
-
-        /**
-         * Returns the number of elements per status value.
-         */
-        public int getNumbersPerValue(int type) {
-            return numbersPerValue[type];
-        }
-
-        /**
-         * Returns the number of comments.
-         */
-        public int getNumberOfComments() {
-            return numberOfComments;
-        }
-
-        /**
-         * Returns the type of the element to which this Stats instance belongs.
-         */
-        public int getType() {
-            return type;
-        }
-
-        /**
-         * Returns the accumulated status value.
-         */
-        public int getValue() {
-            if (numbersPerValue[VALUE_RED] != 0) {
-                return VALUE_RED;
-            } else if (numbersPerValue[VALUE_YELLOW] != 0) {
-                return VALUE_YELLOW;
-            } else {
-                return VALUE_GREEN;
-            }
-        }
-
-    }
-
-    /**
-     * Holds our comparator instance for everything.
-     */
-    private DocComparator comparator = new DocComparator();
-
-    /**
-     * Creates a new instance of the SpecProgressDoclet for a given target
-     * directory.
-     */
-    public SpecProgressDoclet(String directory) {
-        this.directory = new File(directory);
-    }
-
-    /**
-     * Opens a new output file and writes the usual HTML header. Directories
-     * are created on demand.
-     */
-    private PrintWriter openFile(String name, String title) throws IOException {
-        System.out.println("Writing file \"" + name + "\"...");
-        
-        File file = new File(directory, name);
-        File parent = file.getParentFile();
-        parent.mkdirs();
-
-        OutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
-        PrintWriter printer = new PrintWriter(stream);
-
-        printer.println("<html>");
-        printer.println("  <head>");
-        printer.println("    <title>" + title + "</title>");
-        printer.println("  <head>");
-        printer.println("  <body>");
-        printer.println("    <h1>" + title + "</h1>");
-
-        return printer;
-    }
-
-    /**
-     * Closes the given output file, writing the usual HTML footer before.
-     */
-    private void closeFile(PrintWriter printer) {
-        printer.println("  </body>");
-        printer.println("</html>");
-
-        printer.flush();
-        printer.close();
-    }
-
-    /**
-     * Processes the whole list of classes that JavaDoc knows about.
-     */
-    private void process(RootDoc root) throws IOException {
-        runtimeException = root.classNamed("java.lang.RuntimeException");
-        error = root.classNamed("java.lang.Error");
-
-        PrintWriter printer = openFile("index.html", "All packages");
-
-        printer.println("Generated " + new Date().toString());
-        
-        Stats derived = new Stats(TYPE_ROOT, "All packages", null);
-
-        printer.println("      <h2>Children</h2>");
-        printer.println("      <table width=\"100%\">");
-        printStatsHeader(printer);
-
-        PackageDoc[] packages = root.specifiedPackages();
-        Arrays.sort(packages, comparator);
-        for (PackageDoc pack : packages) {
-            if (pack.allClasses().length != 0 && !isHidden(pack)) {
-                Stats subStats = processPackage(pack);
-                printStats(printer, subStats, true);
-                derived.add(subStats);
-            }
-        }
-
-        printer.println("      </table>");
-
-        printer.println("      <p>");
-
-        printer.println("      <h2>Summary</h2>");
-        printer.println("      <table width=\"100%\">");
-        printStatsHeader(printer);
-        printStats(printer, derived, false);
-        printer.println("      </table>");
-
-        closeFile(printer);
-    }
-
-    /**
-     * Processes the details of a single package.
-     */
-    private Stats processPackage(PackageDoc pack) throws IOException {
-        String file = getPackageDir(pack) + "/package.html";
-        PrintWriter printer = openFile(file, "Package " + pack.name());
-
-        Stats derived = new Stats(TYPE_PACKAGE, pack.name(), file);
-
-        printer.println("      <h2>Elements</h2>");
-        printer.println("      <table width=\"100%\">");
-
-        printElementHeader(printer);
-        processElement(printer, pack, TYPE_PACKAGE, derived);
-
-        printer.println("      </table>");
-
-        printer.println("      <p>");
-
-        printer.println("      <h2>Children</h2>");
-        printer.println("      <table width=\"100%\">");
-
-        printStatsHeader(printer);
-
-        ClassDoc[] classes = pack.allClasses();
-        Arrays.sort(classes, comparator);
-        for (ClassDoc clazz : classes) {
-            if (!isHidden(clazz)) {
-                Stats subStats = processClass(clazz);
-                printStats(printer, subStats, true);
-                derived.add(subStats);
-            }
-        }
-
-        printer.println("      </table>");
-
-        printer.println("      <h2>Summary</h2>");
-        printer.println("      <table width=\"100%\">");
-        printStatsHeader(printer);
-        printStats(printer, derived, false);
-        printer.println("      </table>");
-
-        closeFile(printer);
-
-        return derived;
-    }
-
-    /**
-     * Processes the details of a single class.
-     */
-    private Stats processClass(ClassDoc clazz) throws IOException {
-        String file = getPackageDir(clazz.containingPackage()) + "/" + clazz.name() + ".html";
-        PrintWriter printer = openFile(file, "Class " + clazz.name());
-
-        Stats derived = new Stats(TYPE_CLASS, clazz.name(), clazz.name() + ".html");
-
-        printer.println("      <h2>Elements</h2>");
-        printer.println("      <table width=\"100%\">");
-
-        printElementHeader(printer);
-
-        processElement(printer, clazz, TYPE_CLASS, derived);
-
-        if(clazz.isEnum()){
-            FieldDoc[] enums = clazz.enumConstants();
-            Arrays.sort(enums, comparator);
-            for(FieldDoc e : enums) {
-                processElement(printer, e, TYPE_FIELD, derived);
-            }
-        }
-
-        FieldDoc[] fields = clazz.fields();
-        Arrays.sort(fields, comparator);
-        for (FieldDoc field : fields) {
-            processElement(printer, field, TYPE_FIELD, derived);
-        }
-
-        ConstructorDoc[] constructors = clazz.constructors();
-        Arrays.sort(constructors, comparator);
-        for (ConstructorDoc constructor : constructors) {
-            if (constructor.position() != null) {
-                String constPos = constructor.position().toString();
-                String classPos = constructor.containingClass().position()
-                        .toString();
-
-                if (!constPos.equals(classPos)) {
-                    processElement(printer, constructor, TYPE_METHOD, derived);
-                }
-            }
-        }
-
-        HashSet<MethodDoc> methodSet = new HashSet<MethodDoc>();
-        
-        
-        ClassDoc superClass = clazz.superclass();
-        MethodDoc[] methods = null;
-        if (superClass != null && superClass.isPackagePrivate())
-        {
-            MethodDoc[] classMethods = clazz.methods();
-            for (int i = 0; i < classMethods.length; i++) {
-                methodSet.add(classMethods[i]);
-            }
-
-
-            while (superClass != null && superClass.isPackagePrivate()) {
-                classMethods = superClass.methods();
-                for (int i = 0; i < classMethods.length; i++) {
-                    methodSet.add(classMethods[i]);
-                }
-                superClass = superClass.superclass();
-            }
-
-            methods = new MethodDoc[methodSet.size()];
-            methodSet.toArray(methods);
-        }
-        else
-        {
-            methods = clazz.methods();
-        }
-
-        Arrays.sort(methods, comparator);
-        for (MethodDoc method : methods) {
-            if (!(clazz.isEnum() && ("values".equals(method.name()) ||
-                                     "valueOf".equals(method.name())))) {
-                processElement(printer, method, TYPE_METHOD, derived);
-            }
-        }
-
-        printer.println("      </table>");
-
-        printer.println("      <p>");
-
-        printer.println("      <h2>Summary</h2>");
-        printer.println("      <table width=\"100%\">");
-        printStatsHeader(printer);
-        printStats(printer, derived, false);
-        printer.println("      </table>");
-
-        closeFile(printer);
-
-        return derived;
-    }
-
-    /**
-     * Processes a single element.
-     */
-    private void processElement(PrintWriter printer, Doc doc, int type, Stats derived) {
-        if (isHidden(doc)) {
-            return;
-        }
-
-        List<String> errors = new ArrayList<String>();
-        
-        boolean documented = isValidComment(doc.commentText());
-        boolean inherited = false;
-
-        if(checkTypeParameters && (doc.isClass() || doc.isInterface())){
-            boolean typeParamsOk = hasAllTypeParameterDocs((ClassDoc)doc, errors);
-            documented = documented && typeParamsOk;
-        }
-
-        if (doc.isMethod()) {
-            MethodDoc method = (MethodDoc) doc;
-            
-            if ("".equals(method.commentText().trim())) {
-                inherited = method.overriddenMethod() != null || 
-                            implementedMethod(method) != null;
-                documented = inherited;
-            }
-        }
-
-        if (!documented) {
-            errors.add("Missing or insufficient doc.");
-        }
-        
-        if (!inherited) {
-            if (doc.isMethod() || doc.isConstructor()) {
-                ExecutableMemberDoc executable = (ExecutableMemberDoc) doc;
-                boolean paramsOk = hasAllParameterDocs(executable, errors);
-                boolean exceptionsOk = hasAllExceptionDocs(executable, errors);
-
-                documented = documented && paramsOk && exceptionsOk;
-            }
-
-            if (doc.isMethod()) {
-                MethodDoc method = (MethodDoc) doc;
-                boolean resultOk = hasReturnDoc(method, errors);
-                documented = documented && resultOk;
-            }
-        }
-        
-        boolean reviewed = hasSinceTag(doc);
-        Tag[] comments = doc.tags("cts");
-
-        int status = getStatus(documented, reviewed || inherited, comments);
-
-        printer.println("        <tr bgcolor=\"" + COLORS[status] + "\">");
-        printer.println("          <td>" + TYPES[type] + "</td>");
-        
-        if (doc instanceof PackageDoc) {
-            printer.println("          <td>" + doc.toString() + "</td>");
-        } else {
-            String s = doc.name();
-            String t = doc.toString();
-            
-            int i = t.indexOf(s);
-            
-            if (i != -1) {
-                t = t.substring(i);
-            }
-            
-            printer.println("          <td>" + t + "</td>");
-        }
-        
-        printer.println("          <td>" + getFirstSentence(doc) + "</td>");
-        printer.println("          <td>" + (documented ? "Yes" : "No") + "</td>");
-        printer.println("          <td>" + (reviewed ? "Yes" : "No") + "</td>");
-        printer.println("          <td>");
-
-        if (comments.length != 0 || errors.size() != 0) {
-            printer.println("            </ul>");
-
-            for (int i = 0; i < comments.length; i++) {
-                printer.print("              <li>");
-                printer.print(comments[i].text());
-                printer.println("</li>");
-            }
-            
-            for (int i = 0; i < errors.size(); i++) {
-                printer.print("              <li>");
-                printer.print(errors.get(i));
-                printer.println("</li>");
-            }
-
-            printer.println("            </ul>");
-        } else {
-            printer.println("&nbsp;");
-        }
-
-        printer.println("          </td>");
-        printer.println("        </tr>");
-
-        derived.add(type, status, comments.length);
-    }
-
-    /**
-     * Print the table header for an element table.
-     */
-    private void printElementHeader(PrintWriter printer) {
-        printer.println("        <tr>");
-        printer.println("          <td>Type</td>");
-        printer.println("          <td>Name</td>");
-        printer.println("          <td>First sentence</td>");
-        printer.println("          <td>Doc'd</td>");
-        printer.println("          <td>Rev'd</td>");
-        printer.println("          <td>Comments</td>");
-        printer.println("        </tr>");
-    }
-
-    /**
-     * Print the table header for stats table table.
-     */
-    private void printStatsHeader(PrintWriter printer) {
-        printer.println("        <tr>");
-        printer.println("          <td>Type</td>");
-        printer.println("          <td>Name</td>");
-        printer.println("          <td>#Classes</td>");
-        printer.println("          <td>#Fields</td>");
-        printer.println("          <td>#Methods</td>");
-        printer.println("          <td>#Red</td>");
-        printer.println("          <td>#Yellow</td>");
-        printer.println("          <td>#Green</td>");
-        printer.println("          <td>#Comments</td>");
-        printer.println("        </tr>");
-    }
-
-    /**
-     * Prints a single row to a stats table.
-     */
-    private void printStats(PrintWriter printer, Stats info, boolean wantLink) {
-        printer.println("        <tr bgcolor=\"" + COLORS[info.getValue()] + "\">");
-        printer.println("          <td>" + TYPES[info.getType()] + "</td>");
-
-        printer.print("          <td>");
-        String link = info.getLink();
-        if (wantLink && link != null) {
-            printer.print("<a href=\"" + link + "\">" + info.getName() + "</a>");
-        } else {
-            printer.print(info.getName());
-        }
-        printer.println("</td>");
-
-        printer.println("          <td>" + info.getNumbersPerType(TYPE_CLASS) + "</td>");
-        printer.println("          <td>" + info.getNumbersPerType(TYPE_FIELD) + "</td>");
-        printer.println("          <td>" + info.getNumbersPerType(TYPE_METHOD) + "</td>");
-        printer.println("          <td>" + info.getNumbersPerValue(VALUE_RED) + "</td>");
-        printer.println("          <td>" + info.getNumbersPerValue(VALUE_YELLOW) + "</td>");
-        printer.println("          <td>" + info.getNumbersPerValue(VALUE_GREEN) + "</td>");
-        printer.println("          <td>" + info.getNumberOfComments() + "</td>");
-        printer.println("        </tr>");
-    }
-
-    /**
-     * Returns the directory for a given package. Basically converts embedded
-     * dots in the name into slashes. 
-     */
-    private File getPackageDir(PackageDoc pack) {
-        if (pack == null || pack.name() == null || "".equals(pack.name())) {
-            return new File(".");
-        } else {
-            return new File(pack.name().replace('.', '/'));
-        }
-    }
-
-    /**
-     * Checks whether the given comment is not null and not of length 0.
-     */
-    private boolean isValidComment(String comment) {
-        return comment != null && comment.length() > 0;
-    }
-
-    /**
-     * Checks whether the given interface or class has documentation for
-     * all declared type parameters (no less, no more). 
-     */
-    private boolean hasAllTypeParameterDocs(ClassDoc doc, List<String> errors) {
-        boolean result = true;
-        
-        TypeVariable[] params = doc.typeParameters();
-        Set<String> paramsSet = new HashSet<String>();
-        for (TypeVariable param : params) {
-            paramsSet.add(param.typeName()); 
-        }  
-
-        ParamTag[] paramTags = doc.typeParamTags();
-        Map<String, String> paramTagsMap = new HashMap<String, String>();
-        for (ParamTag paramTag : paramTags) {
-            if (!paramsSet.contains(paramTag.parameterName())) {
-                errors.add("Unknown type parameter \"" + paramTag.parameterName() + "\"");
-                result = false;
-            }
-            paramTagsMap.put(paramTag.parameterName(), paramTag.parameterComment());
-        }
-
-        for (TypeVariable param : params) {
-            if (!isValidComment(paramTagsMap.get(param.typeName()))) {
-                errors.add("Undocumented type parameter \"" + param.typeName() + "\"");
-                result = false;
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Checks whether the given executable member has documentation for
-     * all declared parameters (no less, no more).  
-     */
-    private boolean hasAllParameterDocs(ExecutableMemberDoc doc, List<String> errors) {
-        boolean result = true;
-        
-        Parameter params[] = doc.parameters();
-        Set<String> paramsSet = new HashSet<String>();
-        for (int i = 0; i < params.length; i++) {
-            Parameter param = params[i];
-            paramsSet.add(param.name());
-        }
-
-        ParamTag[] paramTags = doc.paramTags();
-        Map<String, String> paramTagsMap = new HashMap<String, String>();
-        for (int i = 0; i < paramTags.length; i++) {
-            ParamTag paramTag = paramTags[i];
-
-            if (!paramsSet.contains(paramTag.parameterName())) {
-                errors.add("Unknown parameter \"" + paramTag.parameterName() + "\"");
-                result = false;
-            }
-
-            paramTagsMap.put(paramTag.parameterName(), paramTag.parameterComment());
-        }
-
-        for (int i = 0; i < params.length; i++) {
-            Parameter param = params[i];
-
-            if (!isValidComment(paramTagsMap.get(param.name()))) {
-                errors.add("Undocumented parameter \"" + param.name() + "\"");
-                result = false;
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Checks whether the given executable member has documentation for
-     * all non-runtime exceptions. Runtime exceptions may or may not be
-     * documented.   
-     */
-    private boolean hasAllExceptionDocs(ExecutableMemberDoc doc, List<String> errors) {
-        boolean result = true;
-        
-        ClassDoc exceptions[] = doc.thrownExceptions();
-        Set<ClassDoc> exceptionSet = new HashSet<ClassDoc>();
-        for (int i = 0; i < exceptions.length; i++) {
-            ClassDoc exception = exceptions[i];
-            if (isRelevantException(exception)) {
-                exceptionSet.add(exception);
-            }
-        }
-
-        ThrowsTag[] throwsTags = doc.throwsTags();
-        Map<ClassDoc, String> throwsTagsMap = new HashMap<ClassDoc, String>();
-        for (int i = 0; i < throwsTags.length; i++) {
-            ThrowsTag throwsTag = throwsTags[i];
-
-            if (throwsTag.exception() == null) {
-                errors.add("Unknown exception \"" + throwsTag.exceptionName() + "\"");
-                result = false;
-            } else if (isRelevantException(throwsTag.exception())) {
-
-                ClassDoc exception = throwsTag.exception();
-                while (exception != null && !exceptionSet.contains(exception)) {
-                    exception = exception.superclass();
-                }
-                if (exception == null) {
-                    errors.add("Unknown exception \"" + throwsTag.exceptionName() + "\"");
-                    result = false;
-                }
-            }
-            
-            throwsTagsMap.put(throwsTag.exception(), throwsTag.exceptionComment());
-        }
-
-        for (int i = 0; i < exceptions.length; i++) {
-            ClassDoc exception = exceptions[i];
-            boolean found = false;
-            
-            for (int j = 0; j < throwsTags.length && !found; j++) {
-                ThrowsTag throwsTag = throwsTags[j];
-                
-                ClassDoc candidate = throwsTag.exception();
-                if (candidate != null) {
-                    if (candidate.equals(exception) || candidate.subclassOf(exception)) {
-                        if (isValidComment(throwsTag.exceptionComment())) {
-                            found = true;
-                        }
-                    }
-                }
-            }
-            
-            if (!found) {
-                errors.add("Undocumented exception \"" + exception.name() + "\"");
-                result = false;
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Checks whether an exception needs to be documented. Runtime exceptions
-     * and errors don't necessarily need documentation (although it doesn't
-     * hurt to have it).
-     */
-    private boolean isRelevantException(ClassDoc clazz) {
-        return !(clazz.subclassOf(runtimeException) || clazz.subclassOf(error));
-    }
-    
-    /**
-     * Checks whether the given method has documentation for the return value.
-     */
-    private boolean hasReturnDoc(MethodDoc method, List<String> errors) {
-        boolean result = true;
-        
-        if (!"void".equals(method.returnType().typeName())) {
-            Tag[] returnTags = method.tags("return");
-
-            if (returnTags.length == 0) {
-                errors.add("Missing result.");
-                result = false;
-            }
-
-            for (int i = 0; i < returnTags.length; i++) {
-                Tag tag = returnTags[i];
-                if (!isValidComment(tag.text())) {
-                    errors.add("Insufficient result.");
-                    result = false;
-                }
-            }
-        } else {
-            Tag[] returnTags = method.tags("return");
-            if (returnTags.length != 0) {
-                errors.add("Unknown result.");
-                result = false;
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Returns the first sentence for the given documentation element.
-     */
-    private String getFirstSentence(Doc doc) {
-        StringBuilder builder = new StringBuilder();
-
-        Tag[] tags = doc.firstSentenceTags();
-        for (int i = 0; i < tags.length; i++) {
-            Tag tag = tags[i];
-
-            if ("Text".equals(tag.kind())) {
-                builder.append(tag.text());
-            } else {
-                builder.append("{" + tag.toString() + "}");
-            }
-        }
-
-        return builder.toString();
-    }
-
-    /**
-     * Returns the interface method that a given method implements, or null if
-     * the method does not implement any interface method.
-     */
-    private MethodDoc implementedMethod(MethodDoc doc) {
-        ClassDoc clazz = doc.containingClass();
-        MethodDoc myDoc = null;
-        while(clazz != null && myDoc == null){
-        ClassDoc[] interfaces = clazz.interfaces();
-            myDoc = implementedMethod0(doc, interfaces);
-            clazz = clazz.superclass();
-        }
-        return myDoc;
-    }
-
-    /**
-     * Recursive helper method for finding out which interface method a given
-     * method implements.
-     */
-    private MethodDoc implementedMethod0(MethodDoc doc, ClassDoc[] interfaces) {
-        for (int i = 0; i < interfaces.length; i++) {
-            ClassDoc classDoc = interfaces[i];
-
-            MethodDoc[] methods = classDoc.methods();
-            for (int j = 0; j < methods.length; j++) {
-                MethodDoc methodDoc = methods[j];
-                if (doc.overrides(methodDoc)) {
-                    return methodDoc;
-                }
-            }
-        }
-
-        for (int i = 0; i < interfaces.length; i++) {
-            MethodDoc myDoc = implementedMethod0(doc, interfaces[i].interfaces());
-            if (myDoc != null) {
-                return myDoc;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Checks whether the given documentation element has a "@since" tag for
-     * Android.
-     */
-    private boolean hasSinceTag(Doc doc) {
-        Tag[] tags = doc.tags("since");
-
-        for (int i = 0; i < tags.length; i++) {
-            if ("Android 1.0".equals(tags[i].text())) {
-                return true;
-            }
-        }
-
-        if (doc instanceof MemberDoc) {
-            return hasSinceTag(((MemberDoc)doc).containingClass());
-        }
-        
-        if (doc instanceof ClassDoc) {
-            return hasSinceTag(((ClassDoc)doc).containingPackage());
-        }
-        
-        return false;
-    }
-
-    /**
-     * Checks whether the given documentation element has a "@hide" tag that
-     * excludes it from the official API.
-     */
-    private boolean isHidden(Doc doc) {
-        Tag[] tags = doc.tags("hide");
-
-        return tags != null && tags.length != 0;
-    }
-    
-    /**
-     * Determines the status of an element based on the existence of
-     * documentation, the review status, and any comments it might have.
-     */
-    private int getStatus(boolean documented, boolean reviewed, Tag[] comments) {
-        if (!documented) {
-            return VALUE_RED;
-        } else if (reviewed && comments.length == 0) {
-            return VALUE_GREEN;
-        } else {
-            return VALUE_YELLOW;
-        }
-    }
-
-    /**
-     * Called by JavaDoc to find our which command line arguments are supported
-     * and how many parameters they take. Part of the JavaDoc API.
-     */
-    public static int optionLength(String option) {
-        if ("-d".equals(option)) {
-            return 2;
-        } else if("-Xgeneric".equals(option)){
-            return 1;
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Returns a particular command line argument for a given option.
-     */
-    private static String getOption(RootDoc root, String option, int index, String defValue) {
-        String[][] allOptions = root.options();
-        for (int i = 0; i < allOptions.length; i++) {
-            if (allOptions[i][0].equals(option)) {
-                return allOptions[i][index];
-            }
-        }
-
-        return defValue;
-    }
-    
-    /**
-     * Returns whether the specified option is present.
-     */
-    private static boolean isOptionSet(RootDoc root, String option){
-        String[][] allOptions = root.options();
-        for (int i = 0; i < allOptions.length; i++) {
-            if (allOptions[i][0].equals(option)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Called by JavaDoc to find out which Java version we claim to support.
-     * Part of the JavaDoc API.
-     */
-    public static LanguageVersion languageVersion() {
-        return LanguageVersion.JAVA_1_5;
-    }
-
-    /**
-     * The main entry point called by JavaDoc after all required information has
-     * been collected. Part of the JavaDoc API.
-     */
-    public static boolean start(RootDoc root) {
-        try {
-            String target = getOption(root, "-d", 1, ".");
-            checkTypeParameters = isOptionSet(root, "-Xgeneric");
-
-            SpecProgressDoclet doclet = new SpecProgressDoclet(target);
-            doclet.process(root);
-
-            File file = new File(target, "index.html");
-            System.out.println("Please see complete report in " + 
-                    file.getAbsolutePath());
-            
-        } catch (Exception ex) {
-            ex.printStackTrace();
-            return false;
-        }
-
-        return true;
-    }
-
-}
diff --git a/tools/test-progress-new/Android.mk b/tools/test-progress-new/Android.mk
deleted file mode 100644
index 2ee261d..0000000
--- a/tools/test-progress-new/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (C) 2008 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_IS_HOST_MODULE := true
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE := test-progress-new
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/test-progress | $(ACP)
-	@echo "Copy: $(PRIVATE_MODULE) ($@)"
-	$(copy-file-to-new-target)
-	$(hide) chmod 755 $@
-
-include $(LOCAL_PATH)/src/Android.mk
diff --git a/tools/test-progress-new/etc/test-progress b/tools/test-progress-new/etc/test-progress
deleted file mode 100644
index af7eec4..0000000
--- a/tools/test-progress-new/etc/test-progress
+++ /dev/null
@@ -1,393 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-while [ -h "${prog}" ]; do
-    newProg=`/bin/ls -ld "${prog}"`
-    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
-    if expr "x${newProg}" : 'x/' >/dev/null; then
-        prog="${newProg}"
-    else
-        progdir=`dirname "${prog}"`
-        prog="${progdir}/${newProg}"
-    fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-cd "${oldwd}"
-
-libdir=`dirname $progdir`/framework
-
-# Parse the test type, by default it will run against all tests
-# FIXME:
-# This should be removed after vm-tests and core-tests have been integrated
-# in CTS.
-testType="all"
-if [ ! -z $1 ]; then
-    if [ $1 == "all" -o $1 == "dalvik" -o $1 == "android" ]; then
-        testType=$1
-        shift
-    fi
-fi
-
-javaOpts=""
-while expr "x$1" : 'x-J' >/dev/null; do
-    opt=`expr "$1" : '-J\(.*\)'`
-    javaOpts="${javaOpts} -${opt}"
-    shift
-done
-
-#exec java $javaOpts -jar $libdir/hat.jar "$@"
-
-#######################################################################
-# Original content of invocation script follows. Uses values cleverly
-# deduced by the above code. If you want to use this for a different
-# set of packages, adjust both the list of source directories and the
-# list of packages.
-#######################################################################
-export CLASSES=$progdir/../framework/test-progress-new.jar
-export INPUT=$ANDROID_BUILD_TOP
-export OUTPUT=$ANDROID_BUILD_TOP/out/target/common/cts/test-progress-new
-
-TESTCOVERAGE_FLAGS="countdisabled,acceptcandx"
-
-if [[ ("$1" == "-f") && ("$2" != "") ]]; then
-	TESTCOVERAGE_FLAGS=$2
-	echo "running with flag '$TESTCOVERAGE_FLAGS'"
-else
-	if [ "$1" != "" ]; then
-	   export OUTPUT=$1
-	fi
-fi
-
-DALVIK_SOURCE_PATH="\
-$INPUT/dalvik/libcore/annotation/src/main/java:\
-$INPUT/dalvik/libcore/archive/src/main/java:\
-$INPUT/dalvik/libcore/auth/src/main/java:\
-$INPUT/dalvik/libcore/awt-kernel/src/main/java:\
-$INPUT/dalvik/libcore/concurrent/src/main/java:\
-$INPUT/dalvik/libcore/crypto/src/main/java:\
-$INPUT/dalvik/libcore/dalvik/src/main/java:\
-$INPUT/dalvik/libcore/icu/src/main/java:\
-$INPUT/dalvik/libcore/json/src/main/java:\
-$INPUT/dalvik/libcore/junit/src/main/java:\
-$INPUT/dalvik/libcore/logging/src/main/java:\
-$INPUT/dalvik/libcore/luni-kernel/src/main/java:\
-$INPUT/dalvik/libcore/luni/src/main/java:\
-$INPUT/dalvik/libcore/math/src/main/java:\
-$INPUT/dalvik/libcore/nio_char/src/main/java:\
-$INPUT/dalvik/libcore/nio/src/main/java:\
-$INPUT/dalvik/libcore/openssl/src/main/java:\
-$INPUT/dalvik/libcore/prefs/src/main/java:\
-$INPUT/dalvik/libcore/regex/src/main/java:\
-$INPUT/dalvik/libcore/security-kernel/src/main/java:\
-$INPUT/dalvik/libcore/security/src/main/java:\
-$INPUT/dalvik/libcore/sql/src/main/java:\
-$INPUT/dalvik/libcore/suncompat/src/main/java:\
-$INPUT/dalvik/libcore/text/src/main/java:\
-$INPUT/dalvik/libcore/xml/src/main/java:\
-$INPUT/dalvik/libcore/x-net/src/main/java:\
-$INPUT/tests/HttpClient:\
-$INPUT/dalvik/libcore/annotation/src/test/java:\
-$INPUT/dalvik/libcore/archive/src/test/java:\
-$INPUT/dalvik/libcore/crypto/src/test/java:\
-$INPUT/dalvik/libcore/crypto/src/test/java/support/common/java:\
-$INPUT/dalvik/libcore/dalvik/src/test/java:\
-$INPUT/dalvik/libcore/logging/src/test/java:\
-$INPUT/dalvik/libcore/luni-kernel/src/test/java:\
-$INPUT/dalvik/libcore/luni/src/test/java:\
-$INPUT/dalvik/libcore/math/src/test/java:\
-$INPUT/dalvik/libcore/nio_char/src/test/java:\
-$INPUT/dalvik/libcore/nio/src/test/java:\
-$INPUT/dalvik/libcore/prefs/src/test/java:\
-$INPUT/dalvik/libcore/regex/src/test/java:\
-$INPUT/dalvik/libcore/security/src/test/java:\
-$INPUT/dalvik/libcore/sql/src/test/java:\
-$INPUT/dalvik/libcore/support/src/test/java:\
-$INPUT/dalvik/libcore/text/src/test/java:\
-$INPUT/dalvik/libcore/xml/src/test/java:\
-$INPUT/dalvik/libcore/x-net/src/test/java"
-
-DALVIK_PACKAGE="\
-junit.framework \
-org.apache.harmony.annotation.tests.java.lang.annotation \
-org.apache.harmony.archive.tests.java.util.jar \
-org.apache.harmony.archive.tests.java.util.zip \
-org.apache.harmony.crypto.tests.javax.crypto \
-org.apache.harmony.crypto.tests.javax.crypto.spec \
-org.apache.harmony.crypto.tests.javax.crypto.interfaces \
-org.apache.harmony.logging.tests.java.util.logging \
-org.apache.harmony.luni.tests.internal.net.www.protocol.http \
-org.apache.harmony.luni.tests.internal.net.www.protocol.https \
-org.apache.harmony.luni.tests.java.io \
-org.apache.harmony.luni.tests.java.lang \
-org.apache.harmony.luni.tests.java.net \
-org.apache.harmony.luni.tests.java.util \
-org.apache.harmony.luni.tests.util \
-org.apache.harmony.math.tests.java.math \
-org.apache.harmony.nio_char.tests.java.nio.charset \
-org.apache.harmony.nio_char.tests.java.nio.charset.spi \
-org.apache.harmony.nio.tests.java.nio \
-org.apache.harmony.nio.tests.java.nio.channels \
-org.apache.harmony.nio.tests.java.nio.channels.spi \
-org.apache.harmony.prefs.tests.java.util.prefs \
-org.apache.harmony.regex.tests.java.util.regex \
-org.apache.harmony.security.tests.java.security \
-org.apache.harmony.sql.tests.java.sql \
-org.apache.harmony.sql.tests.javax.sql \
-tests.SQLite \
-org.apache.harmony.testframework.serialization \
-org.apache.harmony.text.tests.java.text \
-targets \
-tests.api.java.io \
-tests.api.java.lang \
-tests.api.java.lang.ref \
-tests.api.java.lang.reflect \
-tests.api.java.math \
-tests.api.java.net \
-tests.api.java.nio.charset \
-tests.api.java.security \
-tests.api.java.util \
-tests.api.javax.net \
-tests.api.javax.net.ssl \
-tests.api.javax.security.auth \
-tests.api.javax.security.cert \
-tests.api.javax.xml.parsers \
-tests.api.org.xml.sax \
-tests.api.org.xml.sax.ext \
-tests.api.org.xml.sax.helpers \
-tests.api.org.apache.harmony.kernel.dalvik \
-tests.java.security \
-tests.java.sql \
-tests.javax.sql \
-tests.org.w3c.dom \
-tests.security \
-tests.security.acl \
-tests.security.cert \
-tests.security.interfaces \
-tests.security.permissions \
-tests.security.spec \
-tests.sql \
-tests.support \
-tests.targets.security \
-tests.targets.security.cert \
-tests.xml \
-java.io \
-java.lang \
-java.lang.annotation \
-java.lang.ref \
-java.lang.reflect \
-java.math \
-java.net \
-java.nio \
-java.nio.channels \
-java.nio.channels.spi \
-java.nio.charset \
-java.nio.charset.spi \
-java.security \
-java.security.acl \
-java.security.cert \
-java.security.interfaces \
-java.security.spec \
-java.sql \
-java.text \
-java.util \
-java.util.jar \
-java.util.logging \
-java.util.prefs \
-java.util.regex \
-java.util.zip \
-javax.crypto \
-javax.crypto.interfaces \
-javax.crypto.spec \
-javax.net \
-javax.net.ssl \
-javax.security.auth \
-javax.security.auth.callback \
-javax.security.auth.login \
-javax.security.auth.x500 \
-javax.security.cert \
-javax.sql \
-javax.xml.parsers \
-org.xml.sax \
-org.xml.sax.ext \
-org.xml.sax.helpers"
-
-ANDROID_SOURCE_PATH="\
-${INPUT}/frameworks/base/core/java:\
-${INPUT}/frameworks/base/graphics/java:\
-${INPUT}/frameworks/base/location/java:\
-${INPUT}/frameworks/base/media/java:\
-${INPUT}/frameworks/base/opengl/java:\
-${INPUT}/frameworks/base/sax/java:\
-${INPUT}/frameworks/base/telephony/java:\
-${INPUT}/frameworks/base/wifi/java:\
-${INPUT}/cts/tests/tests/app/src:\
-${INPUT}/cts/tests/tests/content/src:\
-${INPUT}/cts/tests/tests/database/src:\
-${INPUT}/cts/tests/tests/graphics/src:\
-${INPUT}/cts/tests/tests/hardware/src:\
-${INPUT}/cts/tests/tests/location/src:\
-${INPUT}/cts/tests/tests/media/src:\
-${INPUT}/cts/tests/tests/net/src:\
-${INPUT}/cts/tests/tests/opengl/src:\
-${INPUT}/cts/tests/tests/os/src:\
-${INPUT}/cts/tests/tests/preference/src:\
-${INPUT}/cts/tests/tests/provider/src:\
-${INPUT}/cts/tests/tests/sax/src:\
-${INPUT}/cts/tests/tests/security/src:\
-${INPUT}/cts/tests/tests/speech/src:\
-${INPUT}/cts/tests/tests/telephony/src:\
-${INPUT}/cts/tests/tests/text/src:\
-${INPUT}/cts/tests/tests/util/src:\
-${INPUT}/cts/tests/tests/view/src:\
-${INPUT}/cts/tests/tests/webkit/src:\
-${INPUT}/cts/tests/tests/widget/src:\
-${INPUT}/cts/tests/src:\
-${INPUT}/frameworks/base/test-runner:\
-${INPUT}/dalvik/libcore/dalvik/src/main/java:\
-${INPUT}/dalvik/libcore/xml/src/main/java:\
-${INPUT}/dalvik/libcore/junit/src/main/java:"
-
-ANDROID_PACKAGE="\
-android.accessibilityservice \
-android.accessibilityservice.cts \
-android.accounts \
-android.accounts.cts \
-android.app \
-android.app.cts \
-android.bluetooth \
-android.content \
-android.content.cts \
-android.content.pm \
-android.content.pm.cts \
-android.content.res \
-android.content.res.cts \
-android.database \
-android.database.cts \
-android.database.sqlite \
-android.database.sqlite.cts \
-android.gesture \
-android.gesture.cts \
-android.graphics \
-android.graphics.cts \
-android.graphics.drawable \
-android.graphics.drawable.cts \
-android.graphics.drawable.shapes \
-android.graphics.drawable.shapes.cts \
-android.hardware \
-android.hardware.cts \
-android.location \
-android.location.cts \
-android.media \
-android.media.cts \
-android.net \
-android.net.cts \
-android.net.http \
-android.net.http.cts \
-android.net.wifi \
-android.net.wifi.cts \
-android.opengl \
-android.opengl.cts \
-android.os \
-android.os.cts \
-android.preference \
-android.preference.cts \
-android.provider \
-android.provider.cts \
-android.sax \
-android.sax.cts \
-android.security \
-android.security.cts \
-android.speech.srec \
-android.speech.srec.cts \
-android.speech.tts \
-android.speech.tts.cts \
-android.telephony \
-android.telephony.cdma \
-android.telephony.cts \
-android.telephony.gsm \
-android.telephony.gsm.cts \
-android.text \
-android.text.cts \
-android.text.format \
-android.text.format.cts \
-android.text.method \
-android.text.method.cts \
-android.text.style \
-android.text.style.cts \
-android.text.util \
-android.text.util.cts \
-android.util \
-android.util.cts \
-android.view \
-android.view.cts \
-android.view.accessibility \
-android.view.accessibility.cts \
-android.view.animation \
-android.view.animation.cts \
-android.view.inputmethod \
-android.view.inputmethod.cts \
-android.webkit \
-android.webkit.cts \
-android.widget \
-android.widget.cts"
-
-ALL_SOURCE_PATH="${DALVIK_SOURCE_PATH}:${ANDROID_SOURCE_PATH}"
-ALL_PACKAGE="${DALVIK_PACKAGE} ${ANDROID_PACKAGE}"
-
-SOURCE_PATH=""
-if [ ${testType} == "all" ]; then
-    SOURCE_PATH="${ALL_SOURCE_PATH} ${ALL_PACKAGE}"
-elif [ ${testType} == "dalvik" ]; then
-    SOURCE_PATH="${DALVIK_SOURCE_PATH} ${DALVIK_PACKAGE}"
-elif [ ${testType} == "android" ]; then
-    SOURCE_PATH="${ANDROID_SOURCE_PATH} ${ANDROID_PACKAGE}"
-fi
-
-javadoc -J-Xmx1024M -docletpath $CLASSES -doclet testprogress2.TestCoverageDoclet -f $TESTCOVERAGE_FLAGS -d $OUTPUT -sourcepath $SOURCE_PATH
-
-# left out on purpose:
-# org.w3c.dom packages: the tests in the xml module are superseded by the
-# official w3c test suite located in the dom module.
-#
-
-# left out on purpose:
-# concurrent packages: a full test suite exists (JCP JSR166 test suite) and it located at 
-# device/dalvik/libcore/concurrent/src/test/java/tests/api/java/util/concurrent
-#
-#java.util.concurrent \
-#java.util.concurrent.atomic \
-#java.util.concurrent.locks \
-#
-
-# left out on purpose: (100% coverage/jsr)
-# tests/api/java/util/concurrent
-# tests.api.java.util.concurrent
-
-
-# all test source path found by:
-# find . -type d | sed -e '/.svn/d' | grep "src/test/java" | sed -e 's/test\/java.*/test\/java/g' | sort -u
-
-# the list of packages (test only) was taken from:
-#  egrep -R "[ \.]TestCase" dalvik/* | sed -e '/.svn/d' | cut -d':' -f 1 | cut -d'.' -f1 | sed -e's/.*src\/test\/java\///g;s/\/[^\/]*$//g' | sort -u
-
-
diff --git a/tools/test-progress-new/src/Android.mk b/tools/test-progress-new/src/Android.mk
deleted file mode 100644
index 00d5904..0000000
--- a/tools/test-progress-new/src/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2008 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 := cts
-
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_CLASSPATH := \
-	$(HOST_JDK_TOOLS_JAR)
-
-LOCAL_MODULE:= test-progress-new
-
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/test-progress-new/src/testprogress2/AnnotationPointer.java b/tools/test-progress-new/src/testprogress2/AnnotationPointer.java
deleted file mode 100644
index 8f466de..0000000
--- a/tools/test-progress-new/src/testprogress2/AnnotationPointer.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2008 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 testprogress2;
-
-import com.sun.javadoc.ExecutableMemberDoc;
-
-import java.util.ArrayList;
-import java.util.List;
-
-// points from one targetMethod to 0..n testMethods which test the target method
-public class AnnotationPointer {
-    final ExecutableMemberDoc targetMethod;
-
-    private List<TestTargetNew> targets = new ArrayList<TestTargetNew>();
-
-    AnnotationPointer(ExecutableMemberDoc targetMethod) {
-        this.targetMethod = targetMethod;
-    }
-
-    public void addTestTargetNew(TestTargetNew testMethodInfo) {
-        /*
-         * if (testMethods.contains(testMethodInfo)) { throw new
-         * RuntimeException("warn: testMethod refers more than once to the
-         * targetMethod, testMethod="+testMethodInfo.getMethodDoc());
-         * //System.out.println("warn: testMethod refers more than once to the
-         * targetMethod, testMethod="+testMethod); } else {
-         */
-        targets.add(testMethodInfo);
-        // }
-    }
-
-    public List<TestTargetNew> getTargets() {
-        return targets;
-    }
-
-    public void addProxiesFrom(AnnotationPointer ap) {
-        List<TestTargetNew> t = ap.targets;
-        // clone the TestTargetNew and add to it a note from which
-        // target method it orignally stems
-        for (TestTargetNew ttn : t) {
-            TestTargetNew tnew = ttn.cloneMe("<b>(INDIRECTLY tested)</b>");
-            targets.add(tnew);
-        }
-    }
-}
diff --git a/tools/test-progress-new/src/testprogress2/ClassOriginator.java b/tools/test-progress-new/src/testprogress2/ClassOriginator.java
deleted file mode 100644
index 378cec7..0000000
--- a/tools/test-progress-new/src/testprogress2/ClassOriginator.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2008 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 testprogress2;
-
-import com.sun.javadoc.ClassDoc;
-
-/**
- *
- */
-public class ClassOriginator implements Originator {
-    private final ClassDoc mClassDoc;
-
-    private final String mComment;
-
-    ClassOriginator(ClassDoc classDoc, String comment) {
-        mClassDoc = classDoc;
-        mComment = comment;
-    }
-
-    /*
-     * (non-Javadoc)
-     * @see testprogress.Originator#asString()
-     */
-    public String asString() {
-        return (mComment != null ? mComment + " - " : "") + " -class- "
-                + mClassDoc.qualifiedName();
-    }
-
-    public boolean isDisabled() {
-        return false;
-    }
-
-    /**
-     * a reference from a class is never a to be fixed
-     */
-    public String getToBeFixed() {
-        return null;
-    }
-
-    /**
-     * a reference from a class is never a broken tests
-     */
-    public String getBrokenTest() {
-        return null;
-    }
-
-    /**
-     * a reference from a class is never a failure
-     */
-    public String getKnownFailure() {
-        return null;
-    }
-
-}
diff --git a/tools/test-progress-new/src/testprogress2/MethodOriginator.java b/tools/test-progress-new/src/testprogress2/MethodOriginator.java
deleted file mode 100644
index e46b436..0000000
--- a/tools/test-progress-new/src/testprogress2/MethodOriginator.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2008 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 testprogress2;
-
-import com.sun.javadoc.AnnotationDesc;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.MethodDoc;
-
-/**
- *
- */
-public class MethodOriginator implements Originator {
-    private final MethodDoc mMethod;
-
-    private final String mComment;
-
-    private final ClassDoc mClass;
-
-    private String knownFailure = null;
-
-    private String brokenTest = null;
-
-    private String toBeFixed = null;
-
-    /**
-     * @param testMethod
-     * @param clazz we need to provide the clazz since junit collects the
-     *            testMethods from all super classes - and thus MethodDoc's
-     *            class can point to a superclass. However, we want to know the
-     *            class where the TestTargetClass is pointing to the API class.
-     * @param comment
-     */
-    MethodOriginator(MethodDoc testMethod, ClassDoc clazz, String comment) {
-        mMethod = testMethod;
-        mComment = comment;
-        mClass = clazz;
-
-        AnnotationDesc[] annots = testMethod.annotations();
-        for (AnnotationDesc annot : annots) {
-            if (annot.annotationType().qualifiedName().equals(
-                    "dalvik.annotation.KnownFailure")) {
-                knownFailure = "<b>@KnownFailure:</b>"
-                        + (String)annot.elementValues()[0].value().value();
-            } else if (annot.annotationType().qualifiedName().equals(
-                    "dalvik.annotation.BrokenTest")) {
-                brokenTest = "<b>@BrokenTest:</b>"
-                        + (String)annot.elementValues()[0].value().value();
-            } else if (annot.annotationType().qualifiedName().equals(
-                    "dalvik.annotation.ToBeFixed")) {
-                String info = "N/A";
-                if (annot.elementValues().length > 0) {
-                    info = (String)annot.elementValues()[0].value().value();
-                }
-
-                toBeFixed = "<b>@ToBeFixed:</b>" + info;
-            }
-            // else some other annotation - ignore
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * @see testprogress.Originator#asString()
-     */
-    public String asString() {
-        return (mComment != null ? "comment:" + mComment + " - " : "")
-                + mClass.qualifiedName() + ": <b>" + mMethod.name() + "</b>"
-                + mMethod.signature()
-                + (brokenTest != null ? " [" + brokenTest + "]" : "")
-                + (toBeFixed != null ? " [" + toBeFixed + "]" : "")
-                + (knownFailure != null ? " [" + knownFailure + "]" : "");
-    }
-
-    public boolean isDisabled() {
-        return mMethod.name().startsWith("_test");
-    }
-
-    public String getBrokenTest() {
-        return brokenTest;
-    }
-
-    public String getToBeFixed() {
-        return toBeFixed;
-    }
-
-    public String getKnownFailure() {
-        return knownFailure;
-    }
-
-}
diff --git a/tools/test-progress-new/src/testprogress2/Originator.java b/tools/test-progress-new/src/testprogress2/Originator.java
deleted file mode 100644
index a6e593c..0000000
--- a/tools/test-progress-new/src/testprogress2/Originator.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2008 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 testprogress2;
-
-public interface Originator {
-
-    /**
-     * @return
-     */
-    String asString();
-
-    /**
-     * whether the originating test is disabled
-     *
-     * @return true if the test is disabled (starts with _test...)
-     */
-    boolean isDisabled();
-
-    /**
-     * indicates if the test is to be fixed (annotated with @ToBeFixed
-     *
-     * @return a string containing the annotation, null otherwise
-     */
-    String getToBeFixed();
-
-    /**
-     * indicates if the test is broken (annotated with @BrokenTest
-     *
-     * @return a string containing the annotation, null otherwise
-     */
-    String getBrokenTest();
-
-    /**
-     * indicates if the test is a known failure (runs fine on a RI, annotated
-     * with @KnownFailure)
-     *
-     * @return a string containing the annotation, null otherwise
-     */
-    String getKnownFailure();
-
-}
diff --git a/tools/test-progress-new/src/testprogress2/TestCoverageDoclet.java b/tools/test-progress-new/src/testprogress2/TestCoverageDoclet.java
deleted file mode 100644
index a08a09b..0000000
--- a/tools/test-progress-new/src/testprogress2/TestCoverageDoclet.java
+++ /dev/null
@@ -1,1365 +0,0 @@
-/*
- * Copyright (C) 2008 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 testprogress2;
-
-import com.sun.javadoc.AnnotationDesc;
-import com.sun.javadoc.AnnotationTypeDoc;
-import com.sun.javadoc.AnnotationValue;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.ConstructorDoc;
-import com.sun.javadoc.Doc;
-import com.sun.javadoc.ExecutableMemberDoc;
-import com.sun.javadoc.LanguageVersion;
-import com.sun.javadoc.MethodDoc;
-import com.sun.javadoc.PackageDoc;
-import com.sun.javadoc.ParameterizedType;
-import com.sun.javadoc.RootDoc;
-import com.sun.javadoc.Tag;
-import com.sun.javadoc.AnnotationDesc.ElementValuePair;
-
-import testprogress2.TestMethodInformation.Color;
-import testprogress2.TestMethodInformation.Level;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * this doclet generates a .html report about the test coverage of the core api
- * reading test method's annotations.
- */
-public class TestCoverageDoclet {
-
-    /**
-     * color for the stats: green, yellow, red
-     */
-    public static final String[] COLORS = {
-            "#a0ffa0", "#ffffa0", "#ff8080"
-    };
-
-    // debugging output
-    private static final boolean DEBUG = false;
-
-    /**
-     * Holds our basic output directory.
-     */
-    private File directory;
-
-    private boolean _ignoreInterfacesAndAbstractMethods = false;
-
-    private boolean _doIncludeDisabledTests = false;
-
-    private boolean _acceptCompleteWithOtherStatus = false;
-
-    private Map<ExecutableMemberDoc, AnnotationPointer> resolved =
-        new HashMap<ExecutableMemberDoc, AnnotationPointer>(8192);
-
-    /**
-     * Helper class for comparing element with each other, in oder to determine
-     * an order. Uses lexicographic order of names.
-     */
-    private class DocComparator implements Comparator<Doc> {
-        public int compare(Doc elem1, Doc elem2) {
-            return elem1.name().compareTo(elem2.name());
-        }
-
-        public boolean equals(Doc elem) {
-            return this == elem;
-        }
-    }
-
-    private class MemberComparator implements Comparator<ExecutableMemberDoc> {
-        public int compare(ExecutableMemberDoc mem1, ExecutableMemberDoc mem2) {
-            return mem1.toString().compareTo(mem2.toString());
-        }
-    }
-
-    /**
-     * Holds our comparator instance for everything.
-     */
-    private DocComparator classComparator = new DocComparator();
-
-    private MemberComparator memberComparator = new MemberComparator();
-
-    private Map<ClassDoc, List<TestTargetNew>> classToSpecialTargets =
-        new HashMap<ClassDoc, List<TestTargetNew>>();
-
-    /**
-     * Creates a new instance of the TestProgressDoclet for a given target
-     * directory.
-     *
-     * @param directory
-     */
-    public TestCoverageDoclet(String directory) {
-        this.directory = new File(directory);
-    }
-
-    /**
-     * Opens a new output file and writes the usual HTML header. Directories are
-     * created on demand.
-     */
-    private PrintWriter openFile(String filename, String title) {
-        File file = new File(directory, filename);
-        File parent = file.getParentFile();
-        parent.mkdirs();
-
-        PrintWriter printer;
-        try {
-            printer = new PrintWriter(new FileOutputStream(file));
-        } catch (FileNotFoundException e) {
-            throw new RuntimeException("file not found:" + e.getMessage());
-        }
-
-        printer.println("<html>");
-        printer.println("  <head>");
-        printer.println("    <title>" + title + "</title>");
-        printer.println("<style type=\"text/css\">\n"
-                + "body, body table tr td { font-size:10pt;font-family: "
-                + " sans-serif; }\n" + "li { padding-bottom:2px }\n"
-                + "table { width:100%; border-width: 0px; border: solid; "
-                + "border-collapse: collapse;}\n"
-                + "table tr td { vertical-align:top; padding:3px; border: "
-                + "1px solid black;}\n" + "</style>");
-        printer.println("  </head>");
-        printer.println("  <body>");
-        printer.println("    <h1>" + title + "</h1>");
-
-        return printer;
-    }
-
-    /**
-     * Closes the given output file, writing the usual HTML footer before.
-     */
-    private void closeFile(PrintWriter printer) {
-        printer.println("  </body>");
-        printer.println("</html>");
-        printer.flush();
-        printer.close();
-    }
-
-    private class TablePrinter {
-        private PrintWriter pr;
-
-        public TablePrinter(PrintWriter pr) {
-            this.pr = pr;
-        }
-
-        public void printRow(String... columns) {
-            pr.print("<tr style=\"background-color:white\">");
-            for (String col : columns) {
-                pr.print("<td>" + col + "</td>");
-            }
-            pr.print("</tr>");
-        }
-
-        public void printPlain(String val) {
-            pr.print(val);
-        }
-
-        public void printTableStart() {
-            pr.print("<table>");
-        }
-
-        public void printTableEnd() {
-            pr.print("</table>");
-        }
-
-        public void printPlainLn(String val) {
-            printPlain(val);
-            pr.print("<br>");
-        }
-    }
-
-    /**
-     * Processes the whole list of classes that JavaDoc knows about.
-     */
-    private void process(RootDoc root) {
-        System.out.println("V 0.9a");
-        String mode = getOption(root, "-f", 1, "dummy-see bash script");
-        System.out.println("mode: " + mode);
-
-        // standard mode is to include disabled tests
-        _doIncludeDisabledTests = mode.contains("countdisabled");
-        _acceptCompleteWithOtherStatus = mode.contains("acceptcandx");
-        if (_doIncludeDisabledTests) {
-            System.out.println("- including disabled tests");
-        } else {
-            System.out.println("- excluding disabled tests");
-        }
-        if (_acceptCompleteWithOtherStatus) {
-            System.out.println("- accepting complete tests with partial tests");
-        } else {
-            System.out.println("- not accepting complete tests with partial "
-                    + "tests");
-        }
-
-        // 1. traverse all test-classes (those extending JUnit's TestCase)
-        // and collect the annotation info.
-        // ===================================================================
-        System.out.println("stage 1 - get targets from all junit test methods");
-        PrintWriter pr = openFile("testcoverage/test-annotation.html",
-                "test class annotation coverage");
-        TablePrinter printer = new TablePrinter(pr);
-        printer.printTableStart();
-        printer.printRow("Test package name", "JUnit classes", "Meth", "Unt",
-                "Part", "Compl", "Disab", "Broken", "ToBeFixed", "KnownFail");
-
-        ColorStat totalStats = new ColorStat("All test packages", null);
-        PackageDoc[] packages = root.specifiedPackages();
-        Arrays.sort(packages, classComparator);
-        for (PackageDoc pack : packages) {
-            if (pack.allClasses().length != 0) {
-                ColorStat packageStat = processTestPackage(pack);
-                if (!packageStat.ignored) {
-                    // ignore those packages which have 0 tests in it
-                    printTestStats(printer, packageStat, true);
-                    totalStats.add(packageStat);
-                }
-            }
-        }
-        printer.printTableEnd();
-
-        printer.printPlainLn("<h2>Summary of all test packages</h2>");
-        printer.printTableStart();
-        printTestStats(printer, totalStats, false);
-        printer.printTableEnd();
-        closeFile(pr);
-
-        System.out.println("stage 2 - proxy test targets to abstract classes"
-                + "and interfaces");
-        // 1/2 - traverse all normal (non-junit) classes for interface
-        // and abstract methods implementation tests.
-        ClassDoc[] classes = root.classes();
-        for (ClassDoc classDoc : classes) {
-            if (!extendsJUnitTestCase(classDoc)) {
-                MethodDoc[] methods = classDoc.methods();
-                for (MethodDoc methodDoc : methods) {
-                    AnnotationPointer ap = getAnnotationPointer(methodDoc,
-                            false);
-                    if (ap != null) {
-                        // if there are already tests targeting this method,
-                        // search for abstract/interface methods which this
-                        // method
-                        // implements, and mark them as tested by this method,
-                        // too.
-                        List<MethodDoc> impls = implementingMethods(methodDoc);
-                        for (MethodDoc impl : impls) {
-                            AnnotationPointer apImpl = getAnnotationPointer(
-                                    impl, true);
-                            // add all tests which pointed to the original
-                            // method.
-                            apImpl.addProxiesFrom(ap);
-                        }
-                    }
-                }
-            }
-        }
-
-        // 2. traverse all "normal" (non-junit) source files
-        // ===================================================================
-        System.out.println("stage 3 - generating report for target api");
-        pr = openFile("index.html", "All target api packages");
-        printer = new TablePrinter(pr);
-        printer.printPlainLn("Generated " + new Date().toString()
-                + " - V0.9a<br>");
-        printer.printPlainLn("<a href=\"testcoverage/test-annotation.html\">"
-                + "annotation progress of test classes</a><br><br>");
-
-        printer.printTableStart();
-        printer.printRow("Package", "Classes", "Methods", "Untested",
-                "Partial", "Complete", "Disabled", "Broken", "ToBeFixed", "KnownFail");
-
-        totalStats = new ColorStat("All target packages", null);
-        packages = root.specifiedPackages();
-        Arrays.sort(packages, classComparator);
-
-        int classCount = 0;
-        for (PackageDoc pack : packages) {
-            if (pack.allClasses().length != 0) {
-                ColorStat packageStat = processPackage(pack);
-
-                if (!packageStat.ignored) {
-                    printStats(printer, packageStat, true);
-                    totalStats.add(packageStat);
-
-                    classCount += Integer.parseInt(packageStat.getExtra());
-                }
-            }
-        }
-        printer.printTableEnd();
-
-        totalStats.setExtra("" + classCount);
-        printer.printPlainLn("<h2>Summary of all target packages</h2>");
-        printer.printTableStart();
-        printStats(printer, totalStats, false);
-        printer.printTableEnd();
-        closeFile(pr);
-    }
-
-    /**
-     * Returns the interface(s) method(s) and the abstract method(s) that a
-     * given method implements, or an empty list if it does not implement
-     * anything.
-     */
-    private List<MethodDoc> implementingMethods(MethodDoc doc) {
-        List<MethodDoc> resultmethods = new ArrayList<MethodDoc>();
-        ClassDoc clazz = doc.containingClass();
-        implementedMethod0(resultmethods, doc, clazz, false);
-        return resultmethods;
-    }
-
-    /**
-     * Recursive helper method for finding out which interface methods or
-     * abstract methods a given method implements.
-     */
-    private void implementedMethod0(List<MethodDoc> resultmethods,
-            MethodDoc doc, ClassDoc testClass, boolean testMethods) {
-
-        // test all methods of this class
-        if (testMethods) {
-            MethodDoc[] methods = testClass.methods();
-            for (int j = 0; j < methods.length; j++) {
-                MethodDoc methodDoc = methods[j];
-                if ((methodDoc.isAbstract() || testClass.isInterface())
-                        && doc.overrides(methodDoc)) {
-                    resultmethods.add(methodDoc);
-                }
-            }
-        }
-
-        // test all implementing interfaces
-        ClassDoc[] ifs = testClass.interfaces();
-        for (int i = 0; i < ifs.length; i++) {
-            ClassDoc iface = ifs[i];
-            implementedMethod0(resultmethods, doc, iface, true);
-        }
-
-        // test the superclass
-        ClassDoc superclass = testClass.superclass();
-        if (superclass != null) {
-            implementedMethod0(resultmethods, doc, superclass, true);
-        }
-    }
-
-    private ColorStat processTestPackage(PackageDoc packageDoc) {
-        ColorStat stats = new ColorStat(packageDoc.name(),
-                getPackageBaseLink(packageDoc) + "/package.html");
-        if (hasHideFlag(packageDoc)) {
-            stats.ignored = true;
-            return stats;
-        }
-        String file = getPackageDir("testcoverage", packageDoc)
-                + "/package.html";
-        PrintWriter pr = openFile(file, "Test package " + packageDoc.name());
-        TablePrinter printer = new TablePrinter(pr);
-        printer.printTableStart();
-        printer.printRow("Class", "Extra", "Meth", "Unt", "Part", "Compl",
-                "Disab", "Broken", "ToBeFixed", "KnownFail");
-
-        ClassDoc[] classes = packageDoc.allClasses();
-        Arrays.sort(classes, classComparator);
-        int junitCnt = 0;
-        for (ClassDoc clazz : classes) {
-            if (extendsJUnitTestCase(clazz)) {
-                junitCnt++;
-                ColorStat subStats = processTestClass(clazz);
-                printTestStats(printer, subStats, true);
-                stats.add(subStats);
-            } else {
-                printer.printRow(clazz.name() + " ignored (no junit class): ",
-                        "", "", "", "", "", "", "", "");
-            }
-        }
-        printer.printTableEnd();
-
-        printer.printPlainLn("<h2>Test package summary</h2>");
-        printer.printTableStart();
-        printStats(printer, stats, false);
-        printer.printTableEnd();
-
-        closeFile(pr);
-        if (junitCnt == 0) {
-            if ((packageDoc.name().contains("tests.")
-                    || packageDoc.name().contains("junit.") || packageDoc
-                    .name().contains(".testframework"))
-                    && !(packageDoc.name().equals("junit.framework"))) {
-                System.err.println("warning!: no junit classes in package '"
-                        + packageDoc.name() + "' even though package name "
-                        + "contains tests.,junit. or .testframework");
-            }
-            stats = new ColorStat(packageDoc.name(),
-                    getPackageBaseLink(packageDoc) + "/package.html");
-            stats.incColor(TestMethodInformation.Color.GREEN);
-            stats.setExtra("Ignored since no Junit test and suites");
-            stats.ignored = true;
-        } else {
-            stats.setExtra("" + junitCnt);
-        }
-        return stats;
-    }
-
-    private ColorStat processPackage(PackageDoc packageDoc) {
-        ColorStat stats = new ColorStat(packageDoc.name(),
-                getPackageBaseLink(packageDoc) + "/package.html");
-        if (hasHideFlag(packageDoc)) {
-            stats.ignored = true;
-            return stats;
-        }
-        String file = getPackageDir("", packageDoc) + "/package.html";
-        PrintWriter pr = openFile(file, "Package " + packageDoc.name());
-        TablePrinter printer = new TablePrinter(pr);
-        printer.printTableStart();
-        printer.printRow("Class", "Extra", "Meth", "Unt", "Part", "Compl",
-                "Disab", "Broken", "ToBeFixed", "KnownFail");
-
-        ClassDoc[] classes = packageDoc.allClasses();
-        Arrays.sort(classes, classComparator);
-        int cnt = 0;
-        int junitCnt = 0;
-        for (ClassDoc clazz : classes) {
-            cnt++;
-            if (hasHideFlag(clazz)) {
-                // ignored since it has a @hide in the javadoc on the class
-                // level
-            } else if (extendsJUnitTestCase(clazz)) {
-                printer.printRow("ignored (junit class): " + clazz.name());
-                junitCnt++;
-            } else if (clazz.name().equals("AllTests")) {
-                printer.printRow("ignored (junit test suite class): "
-                        + clazz.name());
-                junitCnt++;
-            } else {
-                ColorStat subStats = processClass(clazz);
-                printStats(printer, subStats, true);
-                stats.add(subStats);
-            }
-        }
-        printer.printTableEnd();
-
-        printer.printPlainLn("<h2>Target package summary</h2>");
-        printer.printTableStart();
-        printStats(printer, stats, false);
-        printer.printTableEnd();
-
-        closeFile(pr);
-        if (junitCnt == cnt || packageDoc.name().contains("tests.")
-                || packageDoc.name().contains("junit.")
-                || packageDoc.name().contains(".testframework")
-                || packageDoc.name().endsWith(".cts")) {
-            // we only have junit classes -> mark green
-            stats = new ColorStat(packageDoc.name(),
-                    getPackageBaseLink(packageDoc) + "/package.html");
-            stats.incColor(TestMethodInformation.Color.GREEN);
-            stats
-                    .setExtra(junitCnt == cnt ? "Ignored since only Junit test and "
-                            + "suites"
-                            : "Ignored since \"tests.\" in name - recheck");
-            stats.ignored = true;
-        } else {
-            stats.setExtra("" + cnt);
-        }
-        return stats;
-    }
-
-    private boolean hasHideFlag(Doc doc) {
-        // workaround for the non-recognized @hide tag in package.html
-        if (doc instanceof PackageDoc) {
-            String comment = doc.getRawCommentText();
-            return comment != null && comment.contains("@hide");
-        } else {
-            Tag[] hideTags = doc.tags("hide");
-            return hideTags.length > 0;
-        }
-    }
-
-    private ColorStat processTestClass(ClassDoc clazz) {
-        String file = getPackageDir("testcoverage", clazz.containingPackage())
-                + "/" + clazz.name() + ".html";
-        PrintWriter pr = openFile(file, "Test class " + clazz.qualifiedName());
-        TablePrinter printer = new TablePrinter(pr);
-        ColorStat classStat = new ColorStat(clazz.name(), clazz.name()
-                + ".html");
-
-        TestTargetClass testTargetClass = getTargetClass(clazz);
-        ClassDoc targetClass = testTargetClass.targetClass;
-
-        String note = "Note:";
-        if (targetClass == null) {
-            note += "<br>targetClass annotation missing!<br>";
-        } else {
-            // add untested[] annotations to statistics
-            ClassOriginator co = new ClassOriginator(clazz, null);
-
-            AnnotationDesc[] annotsC = testTargetClass.untestedMethods
-                    .toArray(new AnnotationDesc[] {});
-            if (annotsC.length > 0) {
-                // we have "untested" refs
-                ColorStat classLevelStat = new ColorStat(clazz.name(), null);
-                TestMethodInformation tminfo = new TestMethodInformation(co,
-                        annotsC, targetClass);
-                if (tminfo.getError() != null) {
-                    printer.printPlainLn("<b>Error:</b>" + tminfo.getError());
-                    classLevelStat.incColor(Color.RED);
-                } else {
-                    linkTargets(tminfo.getTargets());
-                    classLevelStat.incColor(Color.GREEN);
-                }
-                classStat.add(classLevelStat);
-            }
-        }
-
-        printer.printPlainLn(note);
-
-        printer.printTableStart();
-        printer.printRow("Method", "Note", "Meth", "Unt", "Part", "Compl",
-                "Disab", "Broken", "ToBeFixed", "KnownFail");
-
-        int methodCnt = 0;
-        // also collects test... methods from all superclasses below TestCase,
-        // since those are called as well
-        List<MethodDoc> testMethods = collectAllTestMethods(clazz);
-        Collections.sort(testMethods, memberComparator);
-
-        for (MethodDoc testMethod : testMethods) {
-            methodCnt++;
-
-            // Make disabled tests visible in the report so we don't forget
-            // them.
-            boolean disTest = testMethod.name().startsWith("_test");
-
-            ColorStat methodStat = new ColorStat(testMethod.name(), null);
-            if (disTest) {
-                methodStat.incDisabledTestsCnt();
-            }
-            String comments = disTest ? "<b><span style=\"background:red\">"
-                    + "DISABLED</span></b>" : null;
-
-            MethodOriginator mo = new MethodOriginator(testMethod, clazz,
-                    comments);
-            AnnotationDesc[] annots = testMethod.annotations();
-            TestMethodInformation minfo = new TestMethodInformation(mo, annots,
-                    targetClass);
-            linkTargets(minfo.getTargets());
-
-            String extra = null;
-            if (comments != null) {
-                if (extra == null)
-                    extra = "";
-                extra += comments;
-            }
-
-            if (minfo.getError() != null) { // error case
-                if (extra == null)
-                    extra = "";
-                extra += "<b>Error:</b> " + minfo.getError() + "<br>";
-                methodStat.addMethodInfo(minfo);
-            } else {
-                if (mo.getKnownFailure() != null) {
-                    methodStat.incKnownFailureCnt();
-                    if (extra == null)
-                        extra = "";
-                    extra += mo.getKnownFailure();
-                }
-
-                // check for @BrokenTest
-                if (mo.getBrokenTest() != null) {
-                    // override with warning
-                    methodStat.incBrokenTestCnt();
-                    methodStat.incColor(Color.YELLOW);
-                    if (extra == null)
-                        extra = "";
-                    extra += mo.getBrokenTest();
-                }
-
-                // check for @ToBeFixed
-                if (mo.getToBeFixed() != null) {
-                    // override with warning
-                    methodStat.incToBeFixedCnt();
-                    methodStat.incColor(Color.YELLOW);
-                    if (extra == null) {
-                        extra = "";
-                    }
-
-                    extra += mo.getToBeFixed();
-                } else { // add regular stats
-                    methodStat.addMethodInfo(minfo);
-                }
-            }
-            if (extra != null) {
-                methodStat.setExtra(extra);
-            }
-
-            printTestStats(printer, methodStat, false);
-            classStat.add(methodStat);
-        }
-        printer.printTableEnd();
-
-        printer.printPlainLn("<h2>Test class summary</h2>");
-        printer.printTableStart();
-        printStats(printer, classStat, false);
-        printer.printTableEnd();
-
-        closeFile(pr);
-        classStat.setExtra("#methods: " + testMethods.size());
-        return classStat;
-    }
-
-    private void linkTargets(List<TestTargetNew> targets) {
-        for (TestTargetNew ttn : targets) {
-            if (ttn.getTargetMethod() != null) {
-                AnnotationPointer tar = getAnnotationPointer(ttn
-                        .getTargetMethod(), true);
-                tar.addTestTargetNew(ttn);
-            } else if (ttn.getTargetClass() != null) {
-                // some special target only pointing to a class, not a method.
-                addToClassTargets(ttn.getTargetClass(), ttn);
-            }
-        }
-    }
-
-    private boolean isGreen(TestMethodInformation.Level level) {
-        boolean lComplete = level == TestMethodInformation.Level.COMPLETE;
-        boolean lSufficient = level == TestMethodInformation.Level.SUFFICIENT;
-        boolean lPartialOk = level == TestMethodInformation.Level.PARTIAL_COMPLETE;
-        boolean lPartial = level == TestMethodInformation.Level.PARTIAL;
-        boolean lTodo = level == TestMethodInformation.Level.TODO;
-        boolean lNotFeasible = level == TestMethodInformation.Level.NOT_FEASIBLE;
-        boolean lNotNecessary = level == TestMethodInformation.Level.NOT_NECESSARY;
-
-        return lComplete || lPartialOk || lSufficient || lNotFeasible
-                || lNotNecessary;
-    }
-
-    private ColorStat processClass(ClassDoc clazz) {
-        String file = getPackageDir("", clazz.containingPackage()) + "/"
-                + clazz.name() + ".html";
-        String classDesc = getClassString(clazz);
-        PrintWriter pr = openFile(file, classDesc);
-        TablePrinter printer = new TablePrinter(pr);
-        printer.printPlain("<b>package " + clazz.containingPackage() + "</b>");
-        ColorStat classStats = new ColorStat(classDesc, clazz.name() + ".html");
-
-        // list special targets
-        List<TestTargetNew> classTargets = getTargetsFor(clazz);
-        if (classTargets != null) {
-            printer.printPlain("<h3>Class level tests</h3>");
-            printer.printPlain("<ul>");
-            for (TestTargetNew ttn : classTargets) {
-                String line = "<li>" + ttn.getOriginator().asString();
-                Level lev = ttn.getLevel();
-                line += " <font color=\""
-                        + (isGreen(lev) ? "green" : "red")
-                        + "\"><b>"
-                        + lev.name()
-                        + "</b></font>"
-                        + (ttn.getNotes() != null ? "<br>Notes: "
-                                + ttn.getNotes() : "") + "</li>";
-                printer.printPlain(line);
-            }
-            printer.printPlainLn("</ul>");
-        }
-
-        printer.printPlain("<h3>Method level tests</h3>");
-        printer.printTableStart();
-        printer.printRow("Method", "Tested by", "Meth", "Unt", "Part", "Compl",
-                "Disab", "Broken", "ToBeFixed", "KnownFail");
-        ConstructorDoc[] constructors = clazz.constructors();
-        Arrays.sort(constructors, classComparator);
-        int cnt = 0;
-        for (ConstructorDoc constructor : constructors) {
-            if (!hasHideFlag(constructor) && !hasHideFlag(clazz)) {
-                cnt++;
-                ColorStat memberStat = processElement(constructor);
-                printStats(printer, memberStat, false);
-                classStats.add(memberStat);
-            }
-        }
-
-        MethodDoc[] methods = clazz.methods();
-        Arrays.sort(methods, classComparator);
-        for (MethodDoc method : methods) {
-            if (!hasHideFlag(method) && !hasHideFlag(clazz)) {
-                cnt++;
-                ColorStat subStat = processElement(method);
-                printStats(printer, subStat, false);
-                classStats.add(subStat);
-            }
-        }
-        printer.printTableEnd();
-
-        printer.printPlainLn("<h2>Target class summary</h2>");
-        printer.printTableStart();
-        printStats(printer, classStats, false);
-        printer.printTableEnd();
-
-        closeFile(pr);
-        classStats.setExtra("#methods: " + cnt);
-
-        // mark as green
-        if (_ignoreInterfacesAndAbstractMethods && clazz.isInterface()) {
-            classStats = new ColorStat(clazz.name()
-                    + (clazz.isInterface() ? " (Interface)" : ""), clazz.name()
-                    + ".html");
-            int mcnt = clazz.methods().length;
-            // fake all methods to green
-            for (int i = 0; i < mcnt; i++) {
-                classStats.incColor(TestMethodInformation.Color.GREEN);
-            }
-            classStats.setExtra("Ignored since interface");
-        }
-        return classStats;
-    }
-
-    private class TestTargetClass {
-        ClassDoc targetClass;
-
-        List<AnnotationDesc> untestedMethods = new ArrayList<AnnotationDesc>();
-        // a List of @TestTargetNew annotations
-    }
-
-    private TestTargetClass getTargetClass(ClassDoc classDoc) {
-        // get the class annotation which names the default test target class
-        TestTargetClass ttc = new TestTargetClass();
-        ClassDoc targetClass = null;
-        AnnotationDesc[] cAnnots = classDoc.annotations();
-        for (AnnotationDesc cAnnot : cAnnots) {
-            AnnotationTypeDoc atype = cAnnot.annotationType();
-            if (atype.toString().equals("dalvik.annotation.TestTargetClass")) {
-                ElementValuePair[] cpairs = cAnnot.elementValues();
-                for (int i = 0; i < cpairs.length; i++) {
-                    ElementValuePair ev = cpairs[i];
-                    String elName = ev.element().name();
-                    if (elName.equals("value")) {
-                        // the target class
-                        AnnotationValue av = ev.value();
-                        Object obj = av.value();
-                        // value must be a class doc
-                        if (obj instanceof ClassDoc) {
-                            targetClass = (ClassDoc)obj;
-                        } else if (obj instanceof ParameterizedType) {
-                            targetClass = ((ParameterizedType)obj).asClassDoc();
-                        } else
-                            throw new RuntimeException(
-                                    "annotation elem value is of type "
-                                            + obj.getClass().getName());
-                    } else if (elName.equals("untestedMethods")) {
-                        // TestTargetNew[] untestedMethods() default {};
-                        AnnotationValue[] targets = (AnnotationValue[])ev
-                                .value().value();
-                        for (AnnotationValue ttn : targets) {
-                            // each untested method must be a TestTargetNew
-                            AnnotationDesc ttnd = (AnnotationDesc)ttn.value();
-                            ttc.untestedMethods.add(ttnd);
-                        }
-                    }
-                }
-            }
-        }
-        ttc.targetClass = targetClass;
-        return ttc;
-    }
-
-    private List<MethodDoc> collectAllTestMethods(ClassDoc classDoc) {
-        List<MethodDoc> m = new ArrayList<MethodDoc>();
-
-        ClassDoc curCl = classDoc;
-        do {
-            m.addAll(getJunitTestMethods(curCl));
-        } while ((curCl = curCl.superclass()) != null
-                && !curCl.qualifiedName().equals("junit.framework.TestCase"));
-        return m;
-    }
-
-    private List<MethodDoc> getJunitTestMethods(ClassDoc classDoc) {
-        List<MethodDoc> cl = new ArrayList<MethodDoc>();
-        for (MethodDoc methodDoc : classDoc.methods()) {
-            if (methodDoc.isPublic()
-                    && (methodDoc.name().startsWith("test") || methodDoc.name()
-                            .startsWith("_test"))) {
-                cl.add(methodDoc);
-            }
-        }
-        return cl;
-    }
-
-    private class ColorStat {
-        private String name;
-
-        private String link;
-
-        private String extra;
-
-        public boolean ignored;
-
-        private int[] cntCol = new int[4];
-
-        private int disabledTestsCnt = 0;
-
-        private int brokenTestCnt = 0;
-
-        private int toBeFixedCnt = 0;
-
-        private int knownFailureCnt = 0;
-
-        public String getName() {
-            return name;
-        }
-
-        public String getLink() {
-            return link;
-        }
-
-        public ColorStat(String name, String link) {
-            this.name = name;
-            this.link = link;
-        }
-
-        public void add(ColorStat subStat) {
-            for (int i = 0; i < cntCol.length; i++) {
-                cntCol[i] += subStat.cntCol[i];
-            }
-            disabledTestsCnt += subStat.disabledTestsCnt;
-            brokenTestCnt += subStat.brokenTestCnt;
-            toBeFixedCnt += subStat.toBeFixedCnt;
-            knownFailureCnt += subStat.knownFailureCnt;
-        }
-
-        public void incDisabledTestsCnt() {
-            disabledTestsCnt++;
-        }
-
-        public void incBrokenTestCnt() {
-            brokenTestCnt++;
-        }
-
-        public void incToBeFixedCnt() {
-            toBeFixedCnt++;
-        }
-
-        public void incKnownFailureCnt() {
-            knownFailureCnt++;
-        }
-
-        public void incColor(TestMethodInformation.Color color) {
-            cntCol[color.ordinal()]++;
-        }
-
-        public int getColorCnt(TestMethodInformation.Color color) {
-            return cntCol[color.ordinal()];
-        }
-
-        public void addMethodInfo(TestMethodInformation minfo) {
-            TestMethodInformation.Color c = minfo.getColor();
-            int ord = c.ordinal();
-            cntCol[ord]++;
-        }
-
-        public void setExtra(String extra) {
-            this.extra = extra;
-        }
-
-        public String getExtra() {
-            return extra;
-        }
-
-        public int getDisabledTestsCnt() {
-            return disabledTestsCnt;
-        }
-
-        public int getBrokenTestCnt() {
-            return brokenTestCnt;
-        }
-
-        public int getToBeFixedCnt() {
-            return toBeFixedCnt;
-        }
-
-        public int getKnownFailureCnt() {
-            return knownFailureCnt;
-        }
-    }
-
-    private AnnotationPointer getAnnotationPointer(
-            ExecutableMemberDoc targetMethod, boolean create) {
-        AnnotationPointer ap = resolved.get(targetMethod);
-        if (create && ap == null) {
-            ap = new AnnotationPointer(targetMethod);
-            resolved.put(targetMethod, ap);
-        }
-        return ap;
-    }
-
-    private void addToClassTargets(ClassDoc targetClass, TestTargetNew ttn) {
-        List<TestTargetNew> targets = classToSpecialTargets.get(targetClass);
-        if (targets == null) {
-            targets = new ArrayList<TestTargetNew>();
-            classToSpecialTargets.put(targetClass, targets);
-        }
-        targets.add(ttn);
-    }
-
-    private List<TestTargetNew> getTargetsFor(ClassDoc targetClass) {
-        return classToSpecialTargets.get(targetClass);
-    }
-
-    private boolean extendsJUnitTestCase(ClassDoc classDoc) {
-        // junit.framework.TestCase.java
-        ClassDoc curClass = classDoc;
-        while ((curClass = curClass.superclass()) != null) {
-            if (curClass.toString().equals("junit.framework.TestCase")) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Processes a single method/constructor.
-     */
-    private ColorStat processElement(ExecutableMemberDoc method) {
-        if (DEBUG) System.out.println("Processing " + method);
-        ColorStat memberStats = new ColorStat(getMethodString(method), null);
-        TestMethodInformation.Color c = TestMethodInformation.Color.RED;
-        // if flagged, consider abstract method ok also without tests
-        if (_ignoreInterfacesAndAbstractMethods && method instanceof MethodDoc
-                && ((MethodDoc)method).isAbstract()) {
-            c = TestMethodInformation.Color.GREEN;
-            memberStats.setExtra("ignored since abstract");
-        } else {
-            AnnotationPointer ap = getAnnotationPointer(method, false);
-            int testedByCnt = 0;
-            if (ap != null) {
-                List<TestTargetNew> targets = ap.getTargets();
-                testedByCnt = targets.size();
-                if (testedByCnt == 0) {
-                    throw new RuntimeException(
-                            "existing annotation pointer with no entries!, "
-                                    + "method:" + method);
-                }
-                // at least tested by one method
-                String by = "<ul>";
-                int completeTestCnt = 0;
-                int partialOkTestCnt = 0;
-                int partialTestCnt = 0;
-                int todoTestCnt = 0;
-                int notFeasableTestCnt = 0;
-                int notNecessaryTestCnt = 0;
-                int sufficientTestCnt = 0;
-                // int totalCnt = targets.size();
-
-                for (TestTargetNew target : targets) {
-                    Originator originator = target.getOriginator();
-                    boolean disabledTest = originator.isDisabled();
-                    boolean brokenTest = originator.getBrokenTest() != null;
-                    boolean toBeFixed = originator.getToBeFixed() != null;
-                    boolean knownFailure = originator.getKnownFailure() != null;
-                    by += "<li>" + originator.asString();
-                    TestMethodInformation.Level lev;
-                    if (target.isHavingProblems()) {
-                        lev = TestMethodInformation.Level.TODO;
-                    } else {
-                        lev = target.getLevel();
-                    }
-                    // TODO: improve: avoid c&p by adding ColorStat.addInfo
-                    // (originator) or similar
-                    if (disabledTest) {
-                        memberStats.incDisabledTestsCnt();
-                    }
-                    if (brokenTest) {
-                        memberStats.incBrokenTestCnt();
-                    }
-                    if (toBeFixed) {
-                        memberStats.incToBeFixedCnt();
-                    }
-                    if (knownFailure) {
-                        memberStats.incKnownFailureCnt();
-                    }
-
-                    boolean lComplete = lev == TestMethodInformation.Level.COMPLETE;
-                    boolean lSufficient = lev == TestMethodInformation.Level.SUFFICIENT;
-                    boolean lPartialOk = lev == TestMethodInformation.Level.PARTIAL_COMPLETE;
-                    boolean lPartial = lev == TestMethodInformation.Level.PARTIAL;
-                    boolean lTodo = lev == TestMethodInformation.Level.TODO;
-                    boolean lNotFeasible = lev == TestMethodInformation.Level.NOT_FEASIBLE;
-                    boolean lNotNecessary = lev == TestMethodInformation.Level.NOT_NECESSARY;
-
-                    by += " <font color=\""
-                            + (lComplete || lPartialOk || lSufficient
-                                    || lNotFeasible || lNotNecessary ? "green"
-                                    : "red")
-                            + "\"><b>"
-                            + lev.name()
-                            + "</b></font>"
-                            + (target.getNotes() != null ? "<br>Notes: "
-                                    + target.getNotes() : "");
-
-                    // only count tests when they are either ok (not disabled)
-                    // or if the doIncludeDisabledTests flag is set
-                    if ((_doIncludeDisabledTests || !disabledTest)
-                            && (!brokenTest) && (!toBeFixed)) {
-                        if (lComplete) {
-                            completeTestCnt++;
-                        } else if (lPartialOk) {
-                            partialOkTestCnt++;
-                        } else if (lPartial) {
-                            partialTestCnt++;
-                        } else if (lTodo) {
-                            todoTestCnt++;
-                        } else if (lSufficient) {
-                            sufficientTestCnt++;
-                        } else if (lNotFeasible) {
-                            notFeasableTestCnt++;
-                        } else if (lNotNecessary) {
-                            notNecessaryTestCnt++;
-                        }
-                    }
-
-                    if (toBeFixed) {
-                        partialTestCnt++;
-                    }
-
-                    if (DEBUG) {
-                        System.out.println("completeTestCnt: " + completeTestCnt
-                            + ", partialOkTestCnt: " + partialOkTestCnt
-                            + ", partialTestCnt: " + partialTestCnt
-                            + ", todoTestCnt: " + todoTestCnt
-                            + ", sufficientTestCnt: " + sufficientTestCnt
-                            + ", notFeasableTestCnt: " + notFeasableTestCnt
-                            + ", notNecessaryTestCnt: " + notNecessaryTestCnt);
-                    }
-                    by += "</li>";
-                }
-
-                String warnings = "";
-                // calculate the color
-                int singularTestCnt = notFeasableTestCnt + notNecessaryTestCnt;
-                boolean isAbstract = (method.containingClass().isInterface() ||
-                        (method instanceof MethodDoc) && ((MethodDoc)method).isAbstract());
-
-                if (_acceptCompleteWithOtherStatus
-                        && (completeTestCnt > 0 || sufficientTestCnt > 0)) {
-                    c = TestMethodInformation.Color.GREEN;
-                } else if (_acceptCompleteWithOtherStatus
-                        && (partialOkTestCnt > 1)) {
-                    c = TestMethodInformation.Color.GREEN;
-                } else {
-                    if (singularTestCnt > 0) {
-                        // we have tests which claim not_neccessary or
-                        // not_feasible
-                        if (targets.size() > singularTestCnt) {
-                            // other tests exist
-                            c = TestMethodInformation.Color.RED;
-                            warnings += "<b>WARNING:</b>NOT_FEASIBLE or "
-                                    + "NOT_NECESSARY together with other "
-                                    + "status!<br>";
-                        } else {
-                            // a collection of not_necessary and/or not_feasible
-                            if (notNecessaryTestCnt > 0
-                                    && notFeasableTestCnt > 0) {
-                                // a blend of both -> error
-                                warnings += "<b>WARNING:</b>both NOT_FEASIBLE "
-                                        + "and NOT_NECESSARY together!<br>";
-                                c = TestMethodInformation.Color.RED;
-                            } else { // just one sort of tests -> ok and
-                                // sufficent
-                                c = TestMethodInformation.Color.GREEN;
-                            }
-                        }
-                    } else if (todoTestCnt > 0) {
-                        c = TestMethodInformation.Color.RED;
-                    } else if (partialTestCnt > 0) {
-                        // at least one partial test
-                        c = TestMethodInformation.Color.YELLOW;
-                        if (completeTestCnt > 0 || sufficientTestCnt > 0) {
-                            if (_acceptCompleteWithOtherStatus) {
-                                // accept complete even if there are partials
-                                c = TestMethodInformation.Color.GREEN;
-                            } else if (completeTestCnt > 0) {
-                                // yellow+warning: mixed PARTIAL_COMPLETE and
-                                // COMPLETE status
-                                warnings += "<b>WARNING</b>: mixed PARTIAL "
-                                        + "and COMPLETE status<br>";
-                            }
-                        }
-                    } else if (partialOkTestCnt > 0 || sufficientTestCnt > 0) {
-                        // at least one partialOk test and no partial tests
-                        c = TestMethodInformation.Color.GREEN;
-                        if (partialOkTestCnt == 1) {
-                            // yellow: 1 PARTIAL_COMPLETE (we need either zero
-                            // or >=2 PARTIAL_OK tests)
-                            warnings += "<b>WARNING</b>: only one "
-                                    + "PARTIAL_COMPLETE status<br>";
-                            c = TestMethodInformation.Color.YELLOW;
-                        }
-                    } else if (completeTestCnt > 0 || singularTestCnt == 1) {
-                        // only complete tests
-                        c = TestMethodInformation.Color.GREEN;
-                    }
-
-                    if (completeTestCnt > 1 && !isAbstract
-                            && !_acceptCompleteWithOtherStatus) {
-                        // green+warning: >1 COMPLETE (we need either 0 or 1
-                        // COMPLETE, more would be strange, but possible)
-                        warnings += "<b>WARNING</b>: more than one "
-                                + "COMPLETE status<br>";
-                        if (c != TestMethodInformation.Color.RED) {
-                            c = TestMethodInformation.Color.YELLOW;
-                        }
-                    }
-                }
-                by = warnings + by;
-                memberStats.setExtra(by);
-            } else { // else this class has no single test that targets the
-                // current method
-                // handle special cases:
-
-                // can be ok if the current method is a default constructor
-                // which does not occur in the source code.
-                if (method.isConstructor() && method.signature().equals("()")) {
-                    if (method.position() != null) {
-                        // hacky stuff - the doclet should return null for a
-                        // default constructor,
-                        // but instead returns a source position pointing to the
-                        // same location as
-                        // the class.
-                        String constPos = method.position().toString();
-                        String classPos = method.containingClass().position()
-                                .toString();
-                        if (constPos.equals(classPos)) {
-                            // we have a default constructor not visible in
-                            // source code -> mark green, no testing needed
-                            c = TestMethodInformation.Color.GREEN;
-                            memberStats
-                                    .setExtra("automatically marked green "
-                                            + "since implicit default "
-                                            + "constructor");
-                        }
-                    } else {
-                        // should be called for default constructors according
-                        // to the doclet spec, but is never called.
-                        // spec:
-                        // "A default constructor returns null because it has no
-                        // location in the source file"
-                        // ??
-                        // what about default constructors being in the source
-                        // code?
-                        System.err.println("warning: doclet returned null for "
-                                + "source position: method:" + method);
-                    }
-                } else if (method.containingClass().superclass() != null
-                        && method.containingClass().superclass()
-                                .qualifiedName().equals("java.lang.Enum")) {
-                    // check enum types
-                    // <anyreturnclass> valueOf (java.lang.String) and
-                    // <anyreturnclass[]> values()
-                    // do not need to be tested, since they are autogenerated
-                    // by the compiler
-                    String sig = method.name() + method.signature();
-                    if (sig.equals("valueOf(java.lang.String)")
-                            || sig.equals("values()")) {
-                        c = TestMethodInformation.Color.GREEN;
-                        memberStats
-                                .setExtra("automatically marked green since "
-                                        + "generated by compiler for enums");
-                    }
-                }
-            }
-        }
-        memberStats.incColor(c);
-        return memberStats;
-    }
-
-    private String getMethodString(ExecutableMemberDoc method) {
-        String methodDesc = (method.isPublic() ? "public " : method
-                .isProtected() ? "protected " : method.isPrivate() ? "private "
-                : "");
-
-        return methodDesc + "<b>" + method.name() + "</b> "
-                + method.signature();
-    }
-
-    private String getClassString(ClassDoc clazz) {
-        return (clazz.isPublic() ? "public "
-                : clazz.isProtected() ? "protected "
-                        : clazz.isPrivate() ? "private " : "")
-                + (clazz.isInterface() ? "interface" : "class")
-                + " "
-                + clazz.name();
-    }
-
-    private void printTestStats(TablePrinter printer, ColorStat stat,
-            boolean wantLink) {
-        printStats(printer, stat, wantLink);
-    }
-
-    private void printStats(TablePrinter printer, ColorStat stat,
-            boolean wantLink) {
-        int redCnt = stat.getColorCnt(TestMethodInformation.Color.RED);
-        int yellowCnt = stat.getColorCnt(TestMethodInformation.Color.YELLOW);
-        int greenCnt = stat.getColorCnt(TestMethodInformation.Color.GREEN);
-        int disabledCnt = stat.getDisabledTestsCnt();
-        int brokenTestCnt = stat.getBrokenTestCnt();
-        int toBeFixedCnt = stat.getToBeFixedCnt();
-        int knownFailureCnt = stat.getKnownFailureCnt();
-
-        int total = redCnt + yellowCnt + greenCnt;
-
-        String link = stat.getLink();
-        String namePart;
-        if (wantLink && link != null) {
-            namePart = "<a href=\"" + link + "\">" + stat.getName() + "</a>";
-        } else {
-            namePart = stat.getName();
-        }
-
-        String extra = stat.getExtra() == null ? "" : stat.getExtra();
-
-        int totalDots = 120;
-
-        float toP = total == 0 ? 0 : (((float)totalDots) / total);
-
-        int redD = (int)(toP * redCnt);
-        if (redD == 0 && redCnt > 0) {
-            // never let red cut to zero;
-            redD = 1;
-        }
-        int yellowD = (int)(toP * yellowCnt);
-        if (yellowD == 0 && yellowCnt > 0) {
-            yellowD = 1;
-        }
-        int greenD = totalDots - redD - yellowD; // (int)(toP*greenCnt);
-
-        printer.printRow(namePart, extra, "" + total, "" + redCnt, ""
-                + yellowCnt, "" + greenCnt, "" + disabledCnt, ""
-                + brokenTestCnt, "" + toBeFixedCnt, "" + knownFailureCnt, ""
-                + (redCnt == 0 ? "" : "<span style=\"background:"
-                        + COLORS[TestMethodInformation.Color.RED.ordinal()]
-                        + "\">" + getDots(redD) + "</span>")
-                + (yellowCnt == 0 ? "" : "<span style=\"background:"
-                        + COLORS[TestMethodInformation.Color.YELLOW.ordinal()]
-                        + "\">" + getDots(yellowD) + "</span>")
-                + (greenCnt == 0 && total > 0 ? ""
-                        : "<span style=\"background:"
-                                + COLORS[TestMethodInformation.Color.GREEN
-                                        .ordinal()] + "\">" + getDots(greenD)
-                                + "</span>")
-                + "&nbsp;&nbsp;&nbsp;<span style=\"background:blue\">"
-                + getDots(total / 10) + "</span>");
-    }
-
-    private String getDots(int cnt) {
-        StringBuffer sb = new StringBuffer();
-        for (int i = 0; i < cnt; i++) {
-            sb.append("&nbsp;");
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Returns the directory for a given package. Basically converts embedded
-     * dots in the name into slashes.
-     */
-
-    private String getPackageBaseLink(PackageDoc pack) {
-        return pack.name().replace('.', '/');
-    }
-
-    private File getPackageDir(String prefix, PackageDoc pack) {
-        if (pack == null || pack.name() == null || "".equals(pack.name())) {
-            return new File(prefix + "/" + ".");
-        } else {
-            return new File(prefix + "/" + pack.name().replace('.', '/'));
-        }
-    }
-
-    /**
-     * Called by JavaDoc to find our which command line arguments are supported
-     * and how many parameters they take. Part of the JavaDoc API.
-     *
-     * @param option the options
-     * @return an int
-     */
-    public static int optionLength(String option) {
-        if ("-d".equals(option)) {
-            return 2;
-        }
-        if ("-f".equals(option)) {
-            return 2;
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Called by JavaDoc to query a specific command line argument. Part of the
-     * JavaDoc API.
-     */
-    private static String getOption(RootDoc root, String option, int index,
-            String defValue) {
-        String[][] allOptions = root.options();
-        for (int i = 0; i < allOptions.length; i++) {
-            if (allOptions[i][0].equals(option)) {
-                return allOptions[i][index];
-            }
-        }
-        return defValue;
-    }
-
-    /**
-     * Called by JavaDoc to find out which Java version we claim to support.
-     * Part of the JavaDoc API.
-     *
-     * @return the version of the language
-     */
-    public static LanguageVersion languageVersion() {
-        return LanguageVersion.JAVA_1_5;
-    }
-
-    /**
-     * The main entry point called by JavaDoc after all required information has
-     * been collected. Part of the JavaDoc API.
-     *
-     * @param root
-     * @return whether an error occurred
-     */
-    public static boolean start(RootDoc root) {
-        try {
-            String target = getOption(root, "-d", 1, ".");
-            TestCoverageDoclet doclet = new TestCoverageDoclet(target);
-            doclet.process(root);
-
-            File file = new File(target, "index.html");
-            System.out.println("Please see complete report in " + 
-                    file.getAbsolutePath());
-            
-        } catch (Exception ex) {
-            ex.printStackTrace();
-            return false;
-        }
-        return true;
-    }
-
-}
diff --git a/tools/test-progress-new/src/testprogress2/TestMethodInformation.java b/tools/test-progress-new/src/testprogress2/TestMethodInformation.java
deleted file mode 100644
index c609423..0000000
--- a/tools/test-progress-new/src/testprogress2/TestMethodInformation.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2008 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 testprogress2;
-
-import com.sun.javadoc.AnnotationDesc;
-import com.sun.javadoc.AnnotationValue;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.AnnotationDesc.ElementValuePair;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * represents a list of testtargets annotations all belonging to one test method
- * / one testclass together with its processed information.
- */
-public class TestMethodInformation {
-    private boolean annotationExists = false;
-
-    private List<TestTargetNew> targets = new ArrayList<TestTargetNew>();
-
-    private String error = null;
-
-    private Color color = Color.RED;
-
-    public enum Level {
-        TODO, PARTIAL, PARTIAL_COMPLETE, COMPLETE, ADDITIONAL, NOT_NECESSARY, NOT_FEASIBLE, SUFFICIENT
-    }
-
-    public enum Color {
-        GREEN /* ready */, YELLOW /* work */, RED
-        /* missing essential stuff */
-    }
-
-    public TestMethodInformation(Originator originator,
-            AnnotationDesc[] annots, ClassDoc targetClass) {
-        // System.out.println("looking at "+testMethodDoc);
-        if (targetClass == null) {
-            addError("target class annotation missing!");
-            return;
-        }
-
-        for (AnnotationDesc annot : annots) {
-            if (annot.annotationType().qualifiedName().equals(
-                    "dalvik.annotation.TestTargets")) {
-                // multi target case
-                annotationExists = true;
-                ElementValuePair[] pairs = annot.elementValues();
-                if (pairs.length != 1
-                        && !pairs[0].element().qualifiedName().equals(
-                                "dalvik.annotation.TestTargets.value")) {
-                    throw new RuntimeException("TestTargets has mismatched "
-                            + "attributes");
-                }
-                AnnotationValue[] targets = (AnnotationValue[])pairs[0].value()
-                        .value();
-                for (AnnotationValue ttn : targets) {
-                    // the test targets must be annotations themselves
-                    AnnotationDesc ttnd = (AnnotationDesc)ttn.value();
-                    handleTestTargetNew(originator, ttnd, targetClass);
-                }
-            } else if (annot.annotationType().qualifiedName().equals(
-                    "dalvik.annotation.TestTargetNew")) {
-                // singular case
-                annotationExists = true;
-                handleTestTargetNew(originator, annot, targetClass);
-            } // else some other annotation - ignore
-        }
-
-        boolean targetsCorrect = true;
-        for (TestTargetNew ttn : targets) {
-            targetsCorrect &= (ttn.getTargetMethod() != null || ttn
-                    .getTargetClass() != null);
-        }
-
-        // calculate color of test method
-        if (annotationExists) {
-            if (targetsCorrect) {
-                color = Color.GREEN;
-            } // else incorrect targets
-        } else {
-            addError("no annotation!");
-        }
-    }
-
-    private void handleTestTargetNew(Originator originator, AnnotationDesc ttn,
-            ClassDoc targetClass) {
-        TestTargetNew testTarget = new TestTargetNew(originator, ttn,
-                targetClass);
-        if (testTarget.isHavingProblems()) {
-            // add to overall message
-            addError(testTarget.getNotes());
-        }
-        targets.add(testTarget);
-    }
-
-    private void addError(String err) {
-        if (error == null)
-            error = "";
-        error += err + " ; ";
-    }
-
-    /**
-     * @return the error
-     */
-    public String getError() {
-        return error;
-    }
-
-    public List<TestTargetNew> getTargets() {
-        return targets;
-    }
-
-    public Color getColor() {
-        return color;
-    }
-
-}
diff --git a/tools/test-progress-new/src/testprogress2/TestTargetNew.java b/tools/test-progress-new/src/testprogress2/TestTargetNew.java
deleted file mode 100644
index 6102614..0000000
--- a/tools/test-progress-new/src/testprogress2/TestTargetNew.java
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright (C) 2008 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 testprogress2;
-
-import com.sun.javadoc.AnnotationDesc;
-import com.sun.javadoc.AnnotationValue;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.ExecutableMemberDoc;
-import com.sun.javadoc.FieldDoc;
-import com.sun.javadoc.Parameter;
-import com.sun.javadoc.ParameterizedType;
-import com.sun.javadoc.Type;
-import com.sun.javadoc.TypeVariable;
-import com.sun.javadoc.AnnotationDesc.ElementValuePair;
-
-import testprogress2.TestMethodInformation.Level;
-
-/**
- * holder for a TestTargetNew annotation
- */
-public class TestTargetNew {
-    private final Originator originator;
-
-    private Level level = null;
-
-    private String notes = null;
-
-    /*
-     * method or constructor of target class
-     */
-    private ExecutableMemberDoc targetMethod = null;
-
-    /*
-     * only set if the target points -only- to a class, not to a method. e.g for
-     * special "!..." targets
-     */
-    private ClassDoc targetClass = null;
-
-    /*
-     * read from annotation, e.g. foobar(java.lang.String)
-     */
-    private String readMethodSignature = null;
-
-    /*
-     * e.g. foobar
-     */
-    private String readMethodName = null;
-
-    /*
-     * read from annotation
-     */
-    private ClassDoc readTargetClass = null;
-
-    private boolean havingProblems = false;
-
-    private TestTargetNew(Originator originator) {
-        this.originator = originator;
-    }
-
-    /**
-     * @param originator the origin (class or method)
-     * @param ttn the annotation (testtargetnew)
-     * @param classLevelTargetClass the default target class as given in the
-     *            testtargetclass annotation
-     */
-    public TestTargetNew(Originator originator, AnnotationDesc ttn,
-            ClassDoc classLevelTargetClass) {
-        this.originator = originator;
-        parseTargetClassAndMethodSignature(ttn, classLevelTargetClass);
-        // post: readMethod, readMethodSignature and readTargetClass are now set
-
-        // test for artificial method targets
-        if (readMethodName.startsWith("!")) {
-            targetMethod = null;
-            targetClass = readTargetClass;
-            // level = Level.ADDITIONAL;
-            // notes already set
-            notes = "target: " + readMethodName
-                    + (notes != null ? ", " + "notes: " + notes : "");
-
-        } else if (level == Level.TODO) {
-            notes = "TODO :" + notes;
-            havingProblems = true;
-        } else {
-            // prepare method target:
-            // if the signature contains a "." then the prefix is used as a
-            // reference
-            // to an inner class. This is an alternative to using the clazz
-            // attribute in cases where the class is an inner protected class,
-            // because then the inner class is not visible for the compiler at
-            // the
-            // place of the annotation.
-            // e.g. clazz = Certificate.CertificateRep.class does not work,
-            // so we use clazz = Certificate.class (enclosing class), and method
-            // "Certificate.CertificateRep.<methodHere>", e.g.
-            // "CertificateRep.CertificateRep"
-            // to denote the constructor of the inner protected class
-            // CertificateRep
-            // within Certificate
-            int dotPos = readMethodName.lastIndexOf('.');
-            if (dotPos != -1) {
-                String prefixClassName = readMethodName.substring(0, dotPos);
-                readMethodName = readMethodName.substring(dotPos + 1);
-                ClassDoc[] iCs = readTargetClass.innerClasses();
-                for (ClassDoc iC : iCs) {
-                    if (iC.name().equals(prefixClassName)) {
-                        readTargetClass = iC;
-                        break;
-                    }
-                }
-            }
-
-            String methodAndSig = readMethodName + readMethodSignature;
-            ExecutableMemberDoc tmeth = findMethodSignatureIn(methodAndSig,
-                    readTargetClass);
-            // we need this double test for the note below
-            if (tmeth == null) {
-                // a) wrong signature or
-                // b) a testMethod in a superclass or superinterface, ok also
-                tmeth = findTargetMethodInSelfAndSupers(methodAndSig,
-                        readTargetClass);
-                if (tmeth != null) {
-                    if (notes == null)
-                        notes = "";
-                    notes += "- targetmethod (" + tmeth + ") was found in a "
-                            + "superclass/superinterface of the target<br>";
-                }
-            }
-            if (tmeth != null) {
-                // found
-                targetMethod = tmeth;
-            } else {
-                havingProblems = true;
-                notes = "From " + originator.asString()
-                        + " -> could not resolve " + "targetMethod for class "
-                        + readTargetClass + ", " + "annotation was:" + ttn
-                        + ", testMethodSig " + "= " + methodAndSig + "<br>";
-                System.err.println(">>> warning: " + notes);
-            }
-        }
-    }
-
-    private ExecutableMemberDoc findMethodSignatureIn(String sig,
-            ClassDoc targetClass) {
-        ExecutableMemberDoc targetMethod = null;
-        // find the matching method in the target class, check all methods
-        for (ExecutableMemberDoc mdoc : targetClass.methods()) {
-            if (equalsSignature(mdoc, sig)) {
-                return mdoc;
-            }
-        }
-        // check constructors, too
-        for (ExecutableMemberDoc mdoc : targetClass.constructors()) {
-            if (equalsSignature(mdoc, sig)) {
-                return mdoc;
-            }
-        }
-        return null;
-    }
-
-    private ExecutableMemberDoc findTargetMethodInSelfAndSupers(String sig,
-            ClassDoc targetClass) {
-        ExecutableMemberDoc mem = findMethodSignatureIn(sig, targetClass);
-        if (mem != null) {
-            return mem;
-        }
-
-        // else visit parent class or parent interface(s)
-        ClassDoc[] ifs = targetClass.interfaces();
-        for (int i = 0; i < ifs.length; i++) {
-            ClassDoc iface = ifs[i];
-            mem = findTargetMethodInSelfAndSupers(sig, iface);
-            if (mem != null) {
-                return mem;
-            }
-        }
-
-        ClassDoc superclass = targetClass.superclass();
-        if (superclass != null) {
-            mem = findTargetMethodInSelfAndSupers(sig, superclass);
-            if (mem != null) {
-                return mem;
-            }
-        }
-        return null;
-    }
-
-    private void parseTargetClassAndMethodSignature(AnnotationDesc targetAnnot,
-            ClassDoc targetClass) {
-        ElementValuePair[] pairs = targetAnnot.elementValues();
-        String methodName = null;
-        String args = "";
-        for (ElementValuePair kval : pairs) {
-            if (kval.element().name().equals("method")) {
-                methodName = (String)kval.value().value();
-            } else if (kval.element().name().equals("clazz")) {
-                // optional: a different target class than the test-class-level
-                // default.
-                Object obj = kval.value().value();
-                if (obj instanceof ClassDoc) {
-                    targetClass = (ClassDoc)obj;
-                } else if (obj instanceof ParameterizedType) {
-                    targetClass = ((ParameterizedType)obj).asClassDoc();
-                } else {
-                    throw new RuntimeException("annotation elem value is of "
-                            + "type " + obj.getClass().getName() + " target "
-                            + "annotation = " + targetAnnot);
-                }
-            } else if (kval.element().name().equals("args")) {
-                AnnotationValue[] vals = (AnnotationValue[])kval.value()
-                        .value();
-                for (int i = 0; i < vals.length; i++) {
-                    AnnotationValue arg = vals[i];
-                    String argV;
-                    // TODO: we should be able to use Type.asClassDoc() here
-                    if (arg.value() instanceof ClassDoc) {
-                        ClassDoc cd = (ClassDoc)arg.value();
-                        argV = cd.qualifiedName();
-                    } else { // primitive type or array type
-                        // is there a nicer way to do this?
-                        argV = arg.toString();
-                    }
-                    // strip .class out of args since signature does not contain
-                    // those
-                    if (argV.endsWith(".class")) {
-                        argV = argV.substring(0, argV.length() - 6);
-                    }
-                    args += (i > 0 ? "," : "") + argV;
-                }
-            } else if (kval.element().name().equals("level")) {
-                AnnotationValue lev = kval.value();
-                FieldDoc fd = (FieldDoc)lev.value();
-                String slevel = fd.name();
-
-                try {
-                    level = Enum.valueOf(Level.class, slevel);
-                } catch (IllegalArgumentException iae) {
-                    throw new RuntimeException("COMPILE ERROR!!! enum "
-                            + slevel + " used in targetMethod for class "
-                            + "\"+targetClass+\", "
-                            + "annotation was:\"+targetAnnot+\", "
-                            + "testMethod = \"+methodDoc.toString()");
-                }
-            } else if (kval.element().name().equals("notes")) {
-                notes = (String)kval.value().value();
-                if (notes.equals("")) {
-                    notes = null;
-                }
-            }
-        }
-
-        // String refSig = methodName + "(" + args + ")";
-        // both methodName and methodArgs != null because of Annotation
-        // definition
-        this.readTargetClass = targetClass;
-        this.readMethodSignature = "(" + args + ")";
-        this.readMethodName = methodName;
-    }
-
-    private boolean equalsSignature(ExecutableMemberDoc mdoc,
-            String refSignature) {
-        Parameter[] params = mdoc.parameters();
-        String targs = "";
-        for (int i = 0; i < params.length; i++) {
-            Parameter parameter = params[i];
-            // check for generic type types
-            Type ptype = parameter.type();
-
-            TypeVariable typeVar = ptype.asTypeVariable();
-            String ptname;
-            if (typeVar != null) {
-                ptname = "java.lang.Object"; // the default fallback
-                Type[] bounds = typeVar.bounds();
-                if (bounds.length > 0) {
-                    ClassDoc typeClass = bounds[0].asClassDoc();
-                    ptname = typeClass.qualifiedName();
-                }
-                String dim = ptype.dimension();
-                if (dim != null && dim.length() > 0) {
-                    ptname += dim;
-                }
-            } else {
-                // regular var
-                // ptname = parameter.type().qualifiedTypeName();
-                ptname = parameter.type().toString();
-
-                // System.out.println("quali:"+ptname);
-                // ptname = parameter.typeName();
-                // omit type signature
-                ptname = ptname.replaceAll("<.*>", "");
-            }
-            targs += (i > 0 ? "," : "") + ptname;
-        }
-
-        String methodName = mdoc.name();
-        int lastDot = methodName.lastIndexOf('.');
-        if (lastDot != -1) {
-            // we have a inner class constructor
-            // shrink the name to just name the constructor
-            methodName = methodName.substring(lastDot + 1);
-        }
-
-        String testSig = methodName + "(" + targs + ")";
-
-        // return testSig.equals(refSignature);
-        if (testSig.equals(refSignature)) {
-            // System.out.println("match!!!: ref = "+refSignature+",
-            // test = "+testSig);
-            return true;
-        } else {
-            // System.out.println("no match: ref = "+refSignature+",
-            // test = "+testSig);
-            return false;
-        }
-    }
-
-    public Level getLevel() {
-        return level;
-    }
-
-    public boolean isHavingProblems() {
-        return havingProblems;
-    }
-
-    public Originator getOriginator() {
-        return originator;
-    }
-
-    TestTargetNew cloneMe(String extraNote) {
-        TestTargetNew anew = new TestTargetNew(this.originator);
-        anew.level = this.level;
-        anew.notes = this.notes;
-        anew.targetMethod = this.targetMethod;
-        anew.readMethodSignature = this.readMethodSignature;
-        anew.readTargetClass = this.readTargetClass;
-
-        // mark indirectly tested method always as green, independent
-        // of the original status (to better estimate workload)
-        // anew.level = Level.COMPLETE;
-        anew.notes = extraNote + (notes != null ? ", " + notes : "");
-        return anew;
-    }
-
-    public ExecutableMemberDoc getTargetMethod() {
-        return targetMethod;
-    }
-
-    /**
-     * @return the class of the testtargetnew which method starts with "!", null
-     *         otherwise
-     */
-    public ClassDoc getTargetClass() {
-        return targetClass;
-    }
-
-    public String getNotes() {
-        return notes;
-    }
-}
diff --git a/tools/test-progress/Android.mk b/tools/test-progress/Android.mk
deleted file mode 100644
index 0cdab2e..0000000
--- a/tools/test-progress/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_IS_HOST_MODULE := true
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE := test-progress
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/test-progress | $(ACP)
-	@echo "Copy: $(PRIVATE_MODULE) ($@)"
-	$(copy-file-to-new-target)
-	$(hide) chmod 755 $@
-
-include $(LOCAL_PATH)/src/Android.mk
diff --git a/tools/test-progress/etc/test-progress b/tools/test-progress/etc/test-progress
deleted file mode 100644
index 5e42cc5..0000000
--- a/tools/test-progress/etc/test-progress
+++ /dev/null
@@ -1,324 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-while [ -h "${prog}" ]; do
-    newProg=`/bin/ls -ld "${prog}"`
-    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
-    if expr "x${newProg}" : 'x/' >/dev/null; then
-        prog="${newProg}"
-    else
-        progdir=`dirname "${prog}"`
-        prog="${progdir}/${newProg}"
-    fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-cd "${oldwd}"
-
-libdir=`dirname $progdir`/framework
-
-javaOpts=""
-while expr "x$1" : 'x-J' >/dev/null; do
-    opt=`expr "$1" : '-J\(.*\)'`
-    javaOpts="${javaOpts} -${opt}"
-    shift
-done
-
-#exec java $javaOpts -jar $libdir/hat.jar "$@"
-
-#######################################################################
-# Original content of invocation script follows. Uses values cleverly
-# deduced by the above code. If you want to use this for a different
-# set of packages, adjust both the list of source directories and the
-# list of packages.
-#######################################################################
-export CLASSES=$progdir/../framework/test-progress.jar
-export INPUT=$ANDROID_BUILD_TOP
-export OUTPUT=$ANDROID_BUILD_TOP/out/target/common/cts/test-progress
-
-if [ "$1" != "" ]; then
-   export OUTPUT=$1
-fi
-
-# These two variables are defined for run the coverage tool for Dalvik or CTS
-DALVIK_SOURCE_PATH="\
-${INPUT}/dalvik/libcore/junit/src/main/java:\
-${INPUT}/dalvik/libcore/icu/src/main/java:\
-${INPUT}/dalvik/libcore/openssl/src/main/java:\
-${INPUT}/dalvik/libcore/android/src/main/java:\
-${INPUT}/dalvik/libcore/annotation/src/main/java:\
-${INPUT}/dalvik/libcore/archive/src/main/java:\
-${INPUT}/dalvik/libcore/auth/src/main/java:\
-${INPUT}/dalvik/libcore/awt/src/main/java:\
-${INPUT}/dalvik/libcore/beans/src/main/java:\
-${INPUT}/dalvik/libcore/concurrent/src/main/java:\
-${INPUT}/dalvik/libcore/crypto/src/main/java:\
-${INPUT}/dalvik/libcore/instrument/src/main/java:\
-${INPUT}/dalvik/libcore/logging/src/main/java:\
-${INPUT}/dalvik/libcore/luni/src/main/java:\
-${INPUT}/dalvik/libcore/luni-kernel/src/main/java:\
-${INPUT}/dalvik/libcore/math/src/main/java:\
-${INPUT}/dalvik/libcore/nio/src/main/java:\
-${INPUT}/dalvik/libcore/nio_char/src/main/java:\
-${INPUT}/dalvik/libcore/prefs/src/main/java:\
-${INPUT}/dalvik/libcore/regex/src/main/java:\
-${INPUT}/dalvik/libcore/security/src/main/java:\
-${INPUT}/dalvik/libcore/security-kernel/src/main/java:\
-${INPUT}/dalvik/libcore/sound/src/main/java:\
-${INPUT}/dalvik/libcore/sql/src/main/java:\
-${INPUT}/dalvik/libcore/text/src/main/java:\
-${INPUT}/dalvik/libcore/xml/src/main/java:\
-${INPUT}/dalvik/libcore/x-net/src/main/java:\
-${INPUT}/java/android:\
-${INPUT}/tests/framework-tests/src:\
-${INPUT}/apps/AndroidTests/src:\
-${INPUT}/apps/TestHarness/tests:\
-${INPUT}/apps/TestHarness/src:\
-${INPUT}/java/tests:\
-${INPUT}/dalvik/libcore/text/src/test/java:\
-${INPUT}/dalvik/libcore/support/src/test/java:\
-${INPUT}/dalvik/libcore/nio_char/src/test/java:\
-${INPUT}/dalvik/libcore/nio/src/test/java:\
-${INPUT}/dalvik/libcore/regex/src/test/java:\
-${INPUT}/dalvik/libcore/xml/src/test/java:\
-${INPUT}/dalvik/libcore/prefs/src/test/java:\
-${INPUT}/dalvik/libcore/x-net/src/test/java:\
-${INPUT}/dalvik/libcore/luni-kernel/src/test/java:\
-${INPUT}/dalvik/libcore/sql/src/test/java:\
-${INPUT}/dalvik/libcore/security/src/test/java:\
-${INPUT}/dalvik/libcore/math/src/test/java:\
-${INPUT}/dalvik/libcore/logging/src/test/java:\
-${INPUT}/dalvik/libcore/luni/src/test/java:\
-${INPUT}/dalvik/libcore/archive/src/test/java:\
-${INPUT}/dalvik/libcore/annotation/src/test/java:\
-${INPUT}/dalvik/libcore/dalvik/src/main/java:\
-${INPUT}/tests/HttpClient:\
-${INPUT}/apps/Mms/tests:\
- \
-SQLite \
-junit.framework \
-org.apache.harmony.logging.tests.java.util.logging \
-org.apache.harmony.luni.tests.java.io \
-org.apache.harmony.luni.tests.java.lang \
-org.apache.harmony.luni.tests.java.lang.ref \
-org.apache.harmony.luni.tests.java.net \
-org.apache.harmony.luni.tests.java.util \
-org.apache.harmony.nio.tests.java.nio \
-org.apache.harmony.nio.tests.java.nio.channels \
-org.apache.harmony.nio.tests.java.nio.channels.spi \
-org.apache.harmony.nio_char.tests.java.nio.charset \
-org.apache.harmony.nio_char.tests.java.nio.charset.spi \
-org.apache.harmony.prefs.tests.java.util.prefs \
-org.apache.harmony.sql.tests.java.sql \
-org.apache.harmony.sql.tests.javax.sql \
-org.apache.harmony.tests.java.math \
-org.apache.harmony.tests.java.util.regex \
-org.apache.harmony.text.tests.java.text \
-tests.api.android.dalvik \
-tests.api.java.io \
-tests.api.java.lang \
-tests.api.java.lang.ref \
-tests.api.java.lang.reflect \
-tests.api.java.math \
-tests.api.java.net \
-tests.api.java.nio.charset \
-tests.api.java.util \
-tests.api.javax.net \
-tests.api.javax.net.ssl \
-tests.api.javax.xml.parsers \
-tests.java.lang.StrictMath \
-tests.java.sql \
-tests.sql \
-tests.xml \
-java.awt \
-java.awt.color \
-java.awt.event \
-java.awt.font \
-java.awt.geom \
-java.awt.im \
-java.awt.im.spi \
-java.awt.image \
-java.awt.image.renderable \
-javax.imageio \
-javax.imageio.event \
-javax.imageio.metadata \
-javax.imageio.plugins.bmp \
-javax.imageio.plugins.jpeg \
-javax.imageio.spi \
-javax.imageio.stream \
-java.io \
-java.lang \
-java.lang.annotation \
-java.lang.instrument \
-java.lang.ref \
-java.lang.reflect \
-java.math \ 
-java.net \
-java.nio \
-java.nio.channels \
-java.nio.channels.spi \
-java.nio.charset \
-java.nio.charset.spi \
-java.security \
-java.security.acl \
-java.security.cert \
-java.security.interfaces \
-java.security.spec \
-java.sql \
-java.text \
-java.util \
-java.util.concurrent \
-java.util.concurrent.atomic \
-java.util.concurrent.locks \
-java.util.jar \
-java.util.logging \
-java.util.prefs \
-java.util.regex \
-java.util.zip \
-javax.crypto \
-javax.crypto.interfaces \
-javax.crypto.spec \
-javax.net \
-javax.net.ssl \
-javax.security.auth \
-javax.security.auth.callback \
-javax.security.auth.login \
-javax.security.auth.x500 \
-javax.security.cert \
-javax.sound.midi \
-javax.sound.midi.spi \
-javax.sound.sampled \
-javax.sound.sampled.spi \
-javax.sql \
-javax.xml.parsers \
-org.w3c.dom \
-org.xml.sax \
-org.xml.sax.ext \
-org.xml.sax.helpers"
-
-CTS_SOURCE_PATH="\
-${INPUT}/frameworks/base/core/java:\
-${INPUT}/frameworks/base/graphics/java:\
-${INPUT}/frameworks/base/location/java:\
-${INPUT}/frameworks/base/media/java:\
-${INPUT}/frameworks/base/opengl/java:\
-${INPUT}/frameworks/base/sax/java:\
-${INPUT}/frameworks/base/telephony/java:\
-${INPUT}/frameworks/base/wifi/java:\
-${INPUT}/cts/tests/tests/app/src:\
-${INPUT}/cts/tests/tests/content/src:\
-${INPUT}/cts/tests/tests/database/src:\
-${INPUT}/cts/tests/tests/graphics/src:\
-${INPUT}/cts/tests/tests/hardware/src:\
-${INPUT}/cts/tests/tests/location/src:\
-${INPUT}/cts/tests/tests/media/src:\
-${INPUT}/cts/tests/tests/net/src:\
-${INPUT}/cts/tests/tests/os/src:\
-${INPUT}/cts/tests/tests/provider/src:\
-${INPUT}/cts/tests/tests/preference/src:\
-${INPUT}/cts/tests/tests/telephony/src:\
-${INPUT}/cts/tests/tests/text/src:\
-${INPUT}/cts/tests/tests/util/src:\
-${INPUT}/cts/tests/tests/view/src:\
-${INPUT}/cts/tests/tests/widget/src:\
-${INPUT}/cts/tests/src:\
-${INPUT}/frameworks/base/test-runner:\
-${INPUT}/dalvik/libcore/dalvik/src/main/java:\
-${INPUT}/dalvik/libcore/xml/src/main/java:\
-${INPUT}/dalvik/libcore/junit/src/main/java:\
- \
-android.app \
-android.app.cts \
-android.content \
-android.content.cts \
-android.content.pm \
-android.content.pm.cts \
-android.content.res \
-android.content.res.cts \
-android.location \
-android.location.cts \
-android.os \
-android.os.cts \
-android.text \
-android.text.cts \
-android.text.method \
-android.text.method.cts \
-android.text.style \
-android.text.style.cts \
-android.text.util \
-android.text.util.cts \
-android.util \
-android.util.cts \
-android.view \
-android.view.cts \
-android.view.animation \
-android.view.animation.cts \
-android.view.inputmethod \
-android.view.inputmethod.cts \
-android.widget \
-android.widget.cts \
-android.hardware \
-android.hardware.cts \
-android.net \
-android.net.cts \
-android.net.http \
-android.net.http.cts \
-android.net.wifi \
-android.net.wifi.cts \
-android.provider \
-android.provider.cts \
-android.bluetooth \
-android.bluetooth.cts \
-android.database \
-android.database.cts \
-android.database.sqlite \
-android.database.sqlite.cts \
-android.graphics \
-android.graphics.cts \
-android.graphics.drawable \
-android.graphics.drawable.cts \
-android.graphics.drawable.shapes \
-android.graphics.drawable.shapes.cts \
-android.media \
-android.media.cts \
-android.telephony \
-android.telephony.cts \
-android.telephony.gsm \
-android.telephony.gsm.cts \
-android.accounts \
-android.accounts.cts \
-android.opengl \
-android.opengl.cts \
-android.preference \
-android.preference.cts \
-android.sax \
-android.sax.cts \
-android.security \
-android.security.cts \
-android.speech.srec \
-android.speech.srec.cts \
-android.webkit \
-android.webkit.cts \
-android.webkit.gears \
-android.webkit.gears.cts"
-
-javadoc -J-Xmx512m -docletpath $CLASSES -doclet TestCoverageDoclet -d $OUTPUT -sourcepath ${CTS_SOURCE_PATH}
diff --git a/tools/test-progress/src/Android.mk b/tools/test-progress/src/Android.mk
deleted file mode 100644
index 0428e48..0000000
--- a/tools/test-progress/src/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-#LOCAL_MODULE_TAGS := cts
-
-LOCAL_SRC_FILES := \
-	TestCoverageDoclet.java
-
-LOCAL_CLASSPATH := \
-	$(HOST_JDK_TOOLS_JAR)
-
-LOCAL_MODULE:= test-progress
-
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/test-progress/src/TestCoverageDoclet.java b/tools/test-progress/src/TestCoverageDoclet.java
deleted file mode 100644
index c6bb7d0..0000000
--- a/tools/test-progress/src/TestCoverageDoclet.java
+++ /dev/null
@@ -1,823 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-import com.sun.javadoc.AnnotationDesc;
-import com.sun.javadoc.AnnotationTypeDoc;
-import com.sun.javadoc.AnnotationValue;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.ConstructorDoc;
-import com.sun.javadoc.Doc;
-import com.sun.javadoc.ExecutableMemberDoc;
-import com.sun.javadoc.LanguageVersion;
-import com.sun.javadoc.MethodDoc;
-import com.sun.javadoc.PackageDoc;
-import com.sun.javadoc.Parameter;
-import com.sun.javadoc.ParameterizedType;
-import com.sun.javadoc.RootDoc;
-import com.sun.javadoc.SourcePosition;
-import com.sun.javadoc.Tag;
-import com.sun.javadoc.Type;
-import com.sun.javadoc.TypeVariable;
-import com.sun.javadoc.AnnotationDesc.ElementValuePair;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/*
- */
-public class TestCoverageDoclet {
-
-    public static final int TYPE_FIELD = 0;
-    public static final int TYPE_METHOD = 1;
-    public static final int TYPE_CLASS = 2;
-    public static final int TYPE_PACKAGE = 3;
-    public static final int TYPE_ROOT = 4;
-    public static final int VALUE_RED = 0;
-    public static final int VALUE_YELLOW = 1;
-    public static final int VALUE_GREEN = 2;
-    public static final String[] COLORS = { "#ffa0a0", "#ffffa0", "#a0ffa0" };
-    public static final String[] TYPES = { "Field", "Method", "Class", "Package", "All packages" };
-
-    /**
-     * Holds our basic output directory.
-     */
-    private File directory;
-
-    private Map<ExecutableMemberDoc, AnnotationPointer> resolved =
-            new HashMap<ExecutableMemberDoc, AnnotationPointer>(8192);
-
-    /**
-     * Helper class for comparing element with each other, in oder to determine
-     * an order. Uses lexicographic order of names.
-     */
-    private class DocComparator implements Comparator<Doc> {
-        public int compare(Doc elem1, Doc elem2) {
-            return elem1.name().compareTo(elem2.name());
-        }
-
-        public boolean equals(Doc elem) {
-            return this == elem;
-        }
-    }
-
-    private class MemberComparator implements Comparator<ExecutableMemberDoc> {
-        public int compare(ExecutableMemberDoc mem1, ExecutableMemberDoc mem2) {
-            return mem1.toString().compareTo(mem2.toString());
-        }
-    }
-
-    class MyStats {
-        private String name;
-        private String link;
-        private int elemCnt = 0;
-        private int[] ryg = new int[3];
-        private String extra;
-
-        public MyStats(int type, String name, String link) {
-            this.name = name;
-            this.link = link;
-        }
-
-        public void add(MyStats subStats) {
-           elemCnt++;
-           for (int i = 0; i < ryg.length; i++) {
-               ryg[i]+= subStats.ryg[i];
-           }
-        }
-
-        public int getCountFor(int color) {
-            return ryg[color];
-        }
-
-        public String getStat() {
-            float coverage = (float)(ryg[1]+ryg[2]) / (float)(ryg[0]+ryg[1]+ryg[2]);
-            return "red: "+ryg[0]+", yellow:"+ryg[1]+", green:"+ryg[2]+",coverage:"+coverage;
-        }
-
-        public void inc(int color) {
-            ryg[color]++;
-        }
-
-        public String getLink() {
-            return link;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public String getExtra() {
-            return extra;
-        }
-
-        public void setExtra(String extra) {
-            this.extra = extra;
-        }
-    }
-
-    /**
-     * Holds our comparator instance for everything.
-     */
-    private DocComparator comparator = new DocComparator();
-    private MemberComparator membercomparator = new MemberComparator();
-
-    /**
-     * Creates a new instance of the TestProgressDoclet for a given target
-     * directory.
-     */
-    public TestCoverageDoclet(String directory) {
-        this.directory = new File(directory);
-    }
-
-    /**
-     * Opens a new output file and writes the usual HTML header. Directories
-     * are created on demand.
-     */
-    private PrintWriter openFile(String name, String title) throws IOException {
-        File file = new File(directory, name);
-        File parent = file.getParentFile();
-        parent.mkdirs();
-
-        PrintWriter printer = new PrintWriter(new FileOutputStream(file));
-
-        printer.println("<html>");
-        printer.println("  <head>");
-        printer.println("    <title>" + title + "</title>");
-        printer.println("<style type=\"text/css\">\n"+
-                "body { }\n"+
-                "table {border-width: 0px; border: solid; border-collapse: collapse;}\n"+
-                "table tr td { vertical-align:top; padding:3px; border: 1px solid black;}\n"+
-                "</style>");
-        printer.println("  </head>");
-        printer.println("  <body>");
-        printer.println("    <h1>" + title + "</h1>");
-
-        return printer;
-    }
-
-    /**
-     * Closes the given output file, writing the usual HTML footer before.
-     */
-    private void closeFile(PrintWriter printer) {
-        printer.println("  </body>");
-        printer.println("</html>");
-        printer.flush();
-        printer.close();
-    }
-
-    private class TablePrinter {
-        private PrintWriter pr;
-
-        public TablePrinter(PrintWriter pr) {
-            this.pr = pr;
-        }
-
-        public void printRow(int color, String... columns) {
-            String colo = COLORS[color];
-            pr.print("<tr style=\"background-color:"+colo+"\">");
-            for (String col : columns) {
-                pr.print("<td>"+col+"</td>");
-            }
-            pr.print("</tr>");
-        }
-
-        public void printRow(String... columns) {
-            printRow(1, columns);
-        }
-
-        public void printPlain(String val) {
-            pr.print(val);
-        }
-
-    }
-
-    /**
-     * Processes the whole list of classes that JavaDoc knows about.
-     */
-    private void process(RootDoc root) throws IOException {
-
-        // 1. traverse all test-classes (those extending JUnit's TestCase)
-        // and collect the annotation info. Print which test classes
-        // need annotating
-        PrintWriter pr = openFile("test-annotation.html", "test class annotation coverage");
-        TablePrinter printer = new TablePrinter(pr);
-        printer.printPlain("<table>");
-        printer.printRow("className", "annotated methods", "total methods", "percentage");
-
-        ClassDoc[] classes = root.classes();
-        Arrays.sort(classes, new Comparator<ClassDoc>() {
-            public int compare(ClassDoc c1, ClassDoc c2) {
-                return c1.toString().compareTo(c2.toString());
-            }});
-        for (ClassDoc classDoc : classes) {
-            if (extendsJUnitTestCase(classDoc)) {
-                processTestClass(classDoc, printer);
-            }
-        }
-        printer.printPlain("</table>");
-        closeFile(pr);
-        //dumpInfo();
-
-        // 2. traverse all "normal" (non-junit) source files, for each method
-        // get its status and propagate it up the tree
-        MyStats stats = new MyStats(TYPE_ROOT, "All", "aaa.html");
-        PrintWriter aprinter = openFile("index.html", "All packages");
-        aprinter.println("Generated " + new Date().toString());
-        aprinter.println("<br/><a href=\"test-annotation.html\">annotation progress of test classes</a><br/>");
-        aprinter.println("<br/><a href=\"hidden-doc.html\">hidden classes and methods</a><br/>");
-        aprinter.println("<br/><a href=\"interfaces.html\">interfaces</a><br/>");
-        aprinter.println("<h2>Packages</h2>");
-        aprinter.println("<table>");
-
-        PrintWriter hiddenDocPr = openFile("hidden-doc.html", "hidden classes and methods list");
-        TablePrinter hiddenDocPrinter = new TablePrinter(hiddenDocPr);
-        hiddenDocPrinter.printPlain("<table>");
-        hiddenDocPrinter.printRow("Package Name", "Class Name", "Method Name");
-
-        PrintWriter interfacePr = openFile("interfaces.html", "interface list");
-        TablePrinter interfacePrinter = new TablePrinter(interfacePr);
-        interfacePrinter.printPlain("<table>");
-        interfacePrinter.printRow("packageName", "className");
-
-        PackageDoc[] packages = root.specifiedPackages();
-        Arrays.sort(packages, comparator);
-        for (PackageDoc pack : packages) {
-            if (pack.allClasses().length != 0) {
-
-                if (pack.name().endsWith(".cts")) {
-                    // Skip the cts test packages
-//                    System.out.println(">>>>>>>>>>>Skip package: " + pack.name());
-                } else {
-                    MyStats subStat = processPackage(pack, hiddenDocPrinter, interfacePrinter);
-                    
-                    System.out.println("package " + pack.name() + " has " + subStat.getCountFor(0) + " red.");
-                    printStats(aprinter, subStat, true);
-                    stats.add(subStat);
-                }
-            }
-        }
-        
-
-        System.out.println("Total has " + stats.getCountFor(0) + " red.");
-
-        interfacePrinter.printPlain("</table>");
-        closeFile(interfacePr);
-
-        hiddenDocPrinter.printPlain("</table>");
-        closeFile(hiddenDocPr);
-
-        aprinter.println("</table>");
-        aprinter.println("<h2>Summary</h2>");
-        aprinter.println("<table>");
-        printStats(aprinter, stats, false);
-        aprinter.println("</table>");
-
-        closeFile(aprinter);
-    }
-
-    /*private void processTargetClass(ClassDoc classDoc) {
-        System.out.println("class:"+classDoc);
-        // show all public/protected constructors
-        for (ExecutableMemberDoc constr : classDoc.constructors()) {
-            if (constr.isPublic() || constr.isProtected()) {
-                processTargetMC(constr);
-            }
-        }
-        // show all public/protected methods
-        for (ExecutableMemberDoc method : classDoc.methods()) {
-            if (method.isPublic() || method.isProtected()) {
-                processTargetMC(method);
-            }
-        }
-    }*/
-
-    /*private void dumpInfo() {
-        for (Map.Entry<ExecutableMemberDoc, AnnotationPointer> entry : resolved.entrySet()) {
-            ExecutableMemberDoc mdoc = entry.getKey();
-            AnnotationPointer ap = entry.getValue();
-            System.out.println("----- entry -----------------------");
-            System.out.println("target:"+mdoc.toString());
-            System.out.println("=");
-            for (MethodDoc meth : ap.testMethods) {
-                System.out.println("test method:"+meth);
-            }
-        }
-    }*/
-
-    private void processTestClass(ClassDoc classDoc, TablePrinter printer) {
-        // System.out.println("Processing >>> " + classDoc);
-        // collects all testinfo-annotation info of this class
-        ClassDoc targetClass = null;
-        // get the class annotation which names the default test target class
-        AnnotationDesc[] cAnnots = classDoc.annotations();
-        for (AnnotationDesc cAnnot : cAnnots) {
-
-            AnnotationTypeDoc atype = cAnnot.annotationType();
-            if (atype.toString().equals("dalvik.annotation.TestTargetClass")) {
-                // single member annot with one child 'value'
-                ElementValuePair[] cpairs = cAnnot.elementValues();
-                ElementValuePair evp = cpairs[0];
-                AnnotationValue av = evp.value();
-                Object obj = av.value();
-
-                // value must be a class doc
-                if (obj instanceof ClassDoc) {
-                    targetClass = (ClassDoc) obj;
-                } else if (obj instanceof ParameterizedType) {
-                    targetClass = ((ParameterizedType)obj).asClassDoc();
-                }
-                else throw new RuntimeException("annotation elem value is of type "+obj.getClass().getName());
-            }
-        }
-
-        // now visit all methods (junit test methods - therefore we need not visit the constructors
-        AnnotStat ast = new AnnotStat();
-
-        //System.out.println("checking:"+classDoc.qualifiedName());
-
-        MethodDoc[] methods = classDoc.methods();
-        String note = "";
-        if (targetClass == null) {
-            note += "<br/>targetClass annotation missing!<br/>";
-        }
-
-        for (MethodDoc methodDoc : methods) {
-            // ignore if it is not a junit test method
-            if (!methodDoc.name().startsWith("test")) continue;
-            if (classDoc.qualifiedName().equals("tests.api.java.io.BufferedInputStreamTest")) {
-                //System.out.println("method: "+methodDoc.toString());
-            }
-
-            if (targetClass == null) {
-                // if the targetClass is missing, count all methods as non-annotated
-                ast.incMethodCnt(false);
-            } else {
-                String error = processTestMethod(methodDoc, ast, targetClass);
-                if (error != null) {
-                    note+="<br/><b>E:</b> "+error;
-                }
-            }
-        }
-
-        int man = ast.cntMethodWithAnnot;
-        int mto = ast.cntAllMethods;
-        float perc = mto==0? 100f : ((float)man)/mto * 100f;
-
-        printer.printRow(man==mto && note.equals("")? 2:0, classDoc.qualifiedName(), ""+ast.cntMethodWithAnnot, ""+ast.cntAllMethods,
-                ""+perc+ note);
-
-    }
-
-    private class AnnotStat {
-        int cntMethodWithAnnot = 0;
-        int cntAllMethods = 0;
-        /**
-         * @param correctAnnot
-         */
-        public void incMethodCnt(boolean correctAnnot) {
-            cntAllMethods++;
-            if (correctAnnot) {
-                cntMethodWithAnnot++;
-            }
-        }
-    }
-
-    // points from one targetMethod to 0..n testMethods which test the target method
-    private class AnnotationPointer {
-        AnnotationPointer(ExecutableMemberDoc targetMethod) {
-            this.targetMethod = targetMethod;
-        }
-
-        final ExecutableMemberDoc targetMethod;
-        List<MethodDoc> testMethods = new ArrayList<MethodDoc>();
-
-        public void addTestMethod(MethodDoc testMethod) {
-            if (testMethods.contains(testMethod)) {
-                System.out.println("warn: testMethod refers more than once to the targetMethod, testMethod="+testMethod);
-            } else {
-                testMethods.add(testMethod);
-            }
-        }
-    }
-
-    private String processTestMethod(MethodDoc methodDoc, AnnotStat ast, ClassDoc targetClass) {
-        //System.out.println("processing method: " + methodDoc);
-        // get all per-method-annotation
-        boolean correctAnnot = false;
-        AnnotationDesc[] annots = methodDoc.annotations();
-        for (AnnotationDesc annot : annots) {
-            if (annot.annotationType().toString().equals("dalvik.annotation.TestInfo")) {
-                ElementValuePair[] pairs = annot.elementValues();
-                for (ElementValuePair kv : pairs) {
-                    if (kv.element().qualifiedName().equals("dalvik.annotation.TestInfo.targets")) {
-                        // targets is an [] type
-                        AnnotationValue[] targets = (AnnotationValue[]) kv.value().value();
-                        for (AnnotationValue tval : targets) {
-                            // the test targets must be annotations themselves
-                            AnnotationDesc targetAnnot = (AnnotationDesc) tval.value();
-                            ExecutableMemberDoc targetMethod = getTargetMethod(targetAnnot, targetClass);
-                            if (targetMethod != null) {
-                                AnnotationPointer tar = getAnnotationPointer(targetMethod, true);
-                                tar.addTestMethod(methodDoc);
-                                correctAnnot = true;
-                            } else {
-                                ast.incMethodCnt(false);
-                                return "error: could not resolve targetMethod for class "+targetClass+", annotation was:"+targetAnnot+", testMethod = "+methodDoc.toString();
-                            }
-                        }
-                    }
-                }
-            } // else some other annotation
-        }
-        ast.incMethodCnt(correctAnnot);
-        return null;
-    }
-
-    private AnnotationPointer getAnnotationPointer(ExecutableMemberDoc targetMethod, boolean create) {
-        AnnotationPointer ap = resolved.get(targetMethod);
-        if (create && ap == null) {
-            ap = new AnnotationPointer(targetMethod);
-            resolved.put(targetMethod, ap);
-        }
-        return ap;
-    }
-
-    private ExecutableMemberDoc getTargetMethod(AnnotationDesc targetAnnot,
-            ClassDoc targetClass) {
-        // targetAnnot like @android.annotation.TestTarget(methodName="group", methodArgs=int.class)
-        ElementValuePair[] pairs = targetAnnot.elementValues();
-        String methodName = null;
-        String args = "";
-        for (ElementValuePair kval : pairs) {
-            if (kval.element().name().equals("methodName")) {
-                methodName = (String) kval.value().value();
-            } else if (kval.element().name().equals("methodArgs")) {
-                AnnotationValue[] vals = (AnnotationValue[]) kval.value().value();
-                for (int i = 0; i < vals.length; i++) {
-                    AnnotationValue arg = vals[i];
-                    String argV;
-                    if (arg.value() instanceof ClassDoc) {
-                       ClassDoc cd = (ClassDoc)arg.value();
-                       argV = cd.qualifiedName();
-                    } else { // primitive type or array type
-                        // is there a nicer way to do this?
-                        argV = arg.toString();
-                    }
-                    // strip .class out of args since signature does not contain those
-                    if (argV.endsWith(".class")) {
-                        argV = argV.substring(0, argV.length()-6);
-                    }
-                    args+= (i>0? ",":"") + argV;
-                }
-            }
-        }
-        // both methodName and methodArgs != null because of Annotation definition
-
-        String refSig = methodName+"("+args+")";
-        //System.out.println("Check " + refSig);
-        // find the matching method in the target class
-        // check all methods
-        for (ExecutableMemberDoc mdoc : targetClass.methods()) {
-            if (equalsSignature(mdoc, refSig)) {
-                return mdoc;
-            }
-        }
-        // check constructors, too
-        for (ExecutableMemberDoc mdoc : targetClass.constructors()) {
-            if (equalsSignature(mdoc, refSig)) {
-                return mdoc;
-            }
-        }
-        return null;
-    }
-
-    private boolean equalsSignature(ExecutableMemberDoc mdoc, String refSignature) {
-        Parameter[] params = mdoc.parameters();
-        String targs = "";
-        for (int i = 0; i < params.length; i++) {
-            Parameter parameter = params[i];
-            // check for generic type types
-            Type ptype = parameter.type();
-            TypeVariable typeVar = ptype.asTypeVariable();
-            String ptname;
-            if (typeVar != null) {
-                ptname = "java.lang.Object"; // the default fallback
-                Type[] bounds = typeVar.bounds();
-                if (bounds.length > 0) {
-                    ClassDoc typeClass = bounds[0].asClassDoc();
-                    ptname = typeClass.qualifiedName();
-                }
-            } else {
-                // regular var
-                //ptname = parameter.type().qualifiedTypeName();
-                ptname = parameter.type().toString();
-
-                //System.out.println("quali:"+ptname);
-                //ptname = parameter.typeName();
-                // omit type signature
-                ptname = ptname.replaceAll("<.*>","");
-            }
-            targs+= (i>0? ",":"") + ptname;
-        }
-        String testSig = mdoc.name()+"("+targs+")";
-
-        //return testSig.equals(refSignature);
-        if (testSig.equals(refSignature)) {
-            //System.out.println("found: Sig:"+testSig);
-            return true;
-        } else {
-            //System.out.println("no match: ref = "+refSignature+", test = "+testSig);
-            return false;
-        }
-    }
-
-    private boolean extendsJUnitTestCase(ClassDoc classDoc) {
-        //junit.framework.TestCase.java
-        ClassDoc curClass = classDoc;
-        while ((curClass = curClass.superclass()) != null) {
-            if (curClass.toString().equals("junit.framework.TestCase")) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Processes the details of a single package.
-     * @param hiddenDocPrinter
-     * @param excludedClassPrinter
-     * @param interfacePrinter
-     */
-    private MyStats processPackage(PackageDoc pack, TablePrinter hiddenDocPrinter,
-            TablePrinter interfacePrinter) throws IOException {
-        String file = getPackageDir(pack) + "/package.html";
-        PrintWriter printer = openFile(file, "Package " + pack.name());
-
-        MyStats stats = new MyStats(TYPE_PACKAGE, pack.name(), file);
-        printer.println("<table>");
-
-        ClassDoc[] classes = pack.allClasses();
-        Arrays.sort(classes, comparator);
-        for (ClassDoc clazz : classes) {
-            if (extendsJUnitTestCase(clazz)) {
-                printer.println("<tr><td>ignored(junit):"+clazz.name()+"</td></tr>");
-            } else if (isHiddenClass(clazz)) {
-                hiddenDocPrinter.printRow(pack.name(), clazz.name(), "*");
-            } else if (clazz.isInterface()) {
-                interfacePrinter.printRow(pack.name(), clazz.name());
-            } else {
-                MyStats subStats = processClass(clazz, hiddenDocPrinter);
-                printStats(printer, subStats, true);
-                stats.add(subStats);
-            }
-        }
-        printer.println("</table>");
-        closeFile(printer);
-        return stats;
-    }
-
-    private boolean isHiddenClass(ClassDoc clazz) {
-        if (clazz == null) {
-            return false;
-        }
-
-        if (isHiddenDoc(clazz)) {
-            return true;
-        }
-
-        // If outter class is hidden, this class should be hidden as well
-        return isHiddenClass(clazz.containingClass());
-    }
-
-    private boolean isHiddenDoc(Doc doc) {
-        // Since currently we have two kinds of annotations to mark a class as hide:
-        //  1. @hide
-        //  2. {@hide}
-        // So we should consider both conditions.
-        for (Tag t : doc.tags()) {
-            if (t.name().equals("@hide")) {
-                return true;
-            }
-        }
-
-        for (Tag t : doc.inlineTags()) {
-            if (t.name().equals("@hide")) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-    
-    private MyStats processClass(ClassDoc clazz, TablePrinter hiddenDocPrinter) throws IOException {
-        //System.out.println("Process source class: " + clazz);
-        String file = getPackageDir(clazz.containingPackage()) + "/" + clazz.name() + ".html";
-        PrintWriter printer = openFile(file, "Class " + clazz.name());
-
-        String packageName = clazz.containingPackage().name();
-        String className = clazz.name();
-        
-        MyStats stats = new MyStats(TYPE_CLASS, className, className+".html");
-        printer.println("<table><tr><td>name</td><td>tested by</td></tr>");
-        ConstructorDoc[] constructors = clazz.constructors();
-        Arrays.sort(constructors, comparator);
-        for (ConstructorDoc constructor : constructors) {
-            //System.out.println("constructor: " + constructor);
-            if (isHiddenDoc(constructor)) {
-                hiddenDocPrinter.printRow(packageName, className, constructor.name());
-            } else if (!isGeneratedConstructor(constructor)) {
-                MyStats subStat = processElement(constructor);
-                printStats(printer, subStat, false);
-                stats.add(subStat);
-            }
-        }
-
-        MethodDoc[] methods = clazz.methods();
-        Arrays.sort(methods, comparator);
-        for (MethodDoc method : methods) {
-            //System.out.println("method: " + method);
-            if ("finalize".equals(method.name())) {
-                // Skip finalize method
-            } else if (isHiddenDoc(method)) {
-                hiddenDocPrinter.printRow(packageName, className, method.name());
-            } else if (method.isAbstract()) {
-                // Skip abstract method
-            } else {
-                MyStats subStat = processElement(method);
-                printStats(printer, subStat, false);
-                stats.add(subStat);
-            }
-        }
-
-        printer.println("</table>");
-        closeFile(printer);
-        return stats;
-    }
-
-    /**
-     * Determines whether a constructor has been automatically generated and is
-     * thus not present in the original source. The only way to find out seems
-     * to compare the source position against the one of the class. If they're
-     * equal, the constructor does not exist. It's a bit hacky, but it works.
-     */
-    private boolean isGeneratedConstructor(ConstructorDoc doc) {
-        SourcePosition constPos = doc.position();
-        SourcePosition classPos = doc.containingClass().position();
-
-        return ("" + constPos).equals("" + classPos);
-    }
-
-    /**
-     * Processes a single method/constructor.
-     */
-    private MyStats processElement(ExecutableMemberDoc method) {
-        //int color = getColor(doc)
-        //derived.add(subStats)
-        AnnotationPointer ap = getAnnotationPointer(method, false);
-        MyStats stats = new MyStats(TYPE_METHOD, "<b>"+method.name() + "</b> "+method.signature(), null);
-        int refCnt = 0;
-        if (ap != null) {
-            refCnt = ap.testMethods.size();
-            String by = "";
-            List<MethodDoc> testM = ap.testMethods;
-            Collections.sort(testM, membercomparator);
-            for (MethodDoc teme : testM) {
-                by+= "<br/>"+teme.toString();
-            }
-            stats.setExtra(by);
-        } // else this class has no single test that targets one of its method
-
-        if (refCnt == 0) {
-            stats.inc(VALUE_RED);
-        } else if (refCnt == 1) {
-            stats.inc(VALUE_YELLOW);
-        } else {
-            stats.inc(VALUE_GREEN);
-        }
-        return stats;
-    }
-
-    /**
-     * Prints a single row to a stats table.
-     */
-    private void printStats(PrintWriter printer, MyStats info, boolean wantLink) {
-        int red = info.getCountFor(VALUE_RED);
-        int yellow = info.getCountFor(VALUE_YELLOW);
-
-        printer.println("<tr>");
-      
-        // rule for coloring:
-        // if red > 0 -> red
-        // if yellow > 0 -> yellow
-        // else green
-        int color;
-        if (red > 0) {
-            color = VALUE_RED;
-        } else if (yellow > 0) {
-            color = VALUE_YELLOW;
-        } else {
-            color = VALUE_GREEN;
-        }
-
-        printer.println("<td bgcolor=\""+COLORS[color]+"\">");
-        String link = info.getLink();
-        if (wantLink && link != null) {
-            printer.print("<a href=\"" + link + "\">" + info.getName() + "</a>");
-        } else {
-            printer.print(info.getName());
-        }
-        printer.println(" ("+info.getStat()+") </td>");
-        if (info.getExtra()!=null) {
-            printer.println("<td>"+info.getExtra()+"</td>");
-        }
-        printer.println("</tr>");
-    }
-
-    /**
-     * Returns the directory for a given package. Basically converts embedded
-     * dots in the name into slashes.
-     */
-    private File getPackageDir(PackageDoc pack) {
-        if (pack == null || pack.name() == null || "".equals(pack.name())) {
-            return new File(".");
-        } else {
-            return new File(pack.name().replace('.', '/'));
-        }
-    }
-
-    /**
-     * Called by JavaDoc to find our which command line arguments are supported
-     * and how many parameters they take. Part of the JavaDoc API.
-     */
-    public static int optionLength(String option) {
-        if ("-d".equals(option)) {
-            return 2;
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Called by JavaDoc to query a specific command line argument. Part of the
-     * JavaDoc API.
-     */
-    private static String getOption(RootDoc root, String option, int index, String defValue) {
-        String[][] allOptions = root.options();
-        for (int i = 0; i < allOptions.length; i++) {
-            if (allOptions[i][0].equals(option)) {
-                return allOptions[i][index];
-            }
-        }
-        return defValue;
-    }
-
-    /**
-     * Called by JavaDoc to find out which Java version we claim to support.
-     * Part of the JavaDoc API.
-     */
-    public static LanguageVersion languageVersion() {
-        return LanguageVersion.JAVA_1_5;
-    }
-
-    /**
-     * The main entry point called by JavaDoc after all required information has
-     * been collected. Part of the JavaDoc API.
-     */
-    public static boolean start(RootDoc root) {
-        try {
-            String target = getOption(root, "-d", 1, ".");
-            TestCoverageDoclet doclet = new TestCoverageDoclet(target);
-            doclet.process(root);
-
-        } catch (Exception ex) {
-            ex.printStackTrace();
-            return false;
-        }
-        return true;
-    }
-
-}
diff --git a/tools/tradefed-host/.classpath b/tools/tradefed-host/.classpath
new file mode 100644
index 0000000..a61e37c
--- /dev/null
+++ b/tools/tradefed-host/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="res"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/tradefederation"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/hosttestlib"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/tradefed-host/.project b/tools/tradefed-host/.project
new file mode 100644
index 0000000..990c63e
--- /dev/null
+++ b/tools/tradefed-host/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>cts-tradefed-host</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/apps/CtsVerifier/tests/Android.mk b/tools/tradefed-host/Android.mk
similarity index 68%
copy from apps/CtsVerifier/tests/Android.mk
copy to tools/tradefed-host/Android.mk
index b9572fb..c1e5c54 100644
--- a/apps/CtsVerifier/tests/Android.mk
+++ b/tools/tradefed-host/Android.mk
@@ -1,4 +1,3 @@
-#
 # Copyright (C) 2010 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,23 +11,21 @@
 # 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)
+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
-
+# Only compile source java files in this lib.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := res
 
-LOCAL_PACKAGE_NAME := CtsVerifierTests
+LOCAL_MODULE := cts-tradefed
+LOCAL_MODULE_TAGS := optional
+LOCAL_JAVA_LIBRARIES := ddmlib-prebuilt tradefed-prebuilt hosttestlib
 
-LOCAL_INSTRUMENTATION_FOR := CtsVerifier
+include $(BUILD_HOST_JAVA_LIBRARY)
 
-LOCAL_SDK_VERSION := current
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
 
-include $(BUILD_PACKAGE)
diff --git a/tools/tradefed-host/res/config/cts.xml b/tools/tradefed-host/res/config/cts.xml
new file mode 100644
index 0000000..d98c7fb
--- /dev/null
+++ b/tools/tradefed-host/res/config/cts.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<configuration
+    description="Runs a CTS plan from a pre-existing CTS installation">
+
+    <build_provider class="com.android.cts.tradefed.targetsetup.CtsBuildProvider" />
+    <device_recovery class="com.android.tradefed.device.WaitDeviceRecovery" />
+    <target_preparer class="com.android.cts.tradefed.targetsetup.CtsSetup" />
+    <test class="com.android.cts.tradefed.testtype.PlanTest" />
+    <logger class="com.android.tradefed.log.FileLogger" />
+    <result_reporter class="com.android.cts.tradefed.result.CtsXmlResultReporter" />
+
+</configuration>
diff --git a/tools/tradefed-host/res/result/cts_result.css b/tools/tradefed-host/res/result/cts_result.css
new file mode 100644
index 0000000..d7ce510
--- /dev/null
+++ b/tools/tradefed-host/res/result/cts_result.css
@@ -0,0 +1,234 @@
+/* Copyright (C) 2008 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.
+*/
+
+body {
+    font-family:arial,sans-serif;
+    color:#000;
+    font-size:13px;
+    color:#333;
+    padding:10;
+    margin:10;
+}
+
+table {
+    font-size:1em;
+    margin:0 0 1em;
+    padding:0;
+    border-collapse:collapse;
+    border-width:0;
+    empty-cells:show;
+    width: 95%;
+}
+
+/* Report logo and device name */
+#title table {
+    padding:5px;
+    border-width: 0px;
+    margin-left:auto;
+    margin-right:auto;
+    vertical-align:middle;
+}
+
+/* Device and test plan summary below the title */
+#summary table {
+    background-color: rgb(212, 233, 169);
+    -moz-border-radius:5px;
+    border-radius:5px;
+    padding:5px;
+    border-color: #A5C639 #A5C639 #A5C639 #A5C639;
+    border-width: 0px 0px 0px 0px;
+    margin-left:auto;
+    margin-right:auto;
+    width:80%;
+}
+
+#summary th {
+    background-color: #A5C639;
+    font-size:1.2em;
+    height: 2em;
+    width: 50%;
+}
+
+#summary td {
+    border-width: 0px 0px 0px 0px;
+    border-color: gray;
+    border-style: inset;
+    font-size:1em;
+    vertical-align: top;
+}
+
+#summaryinfo table {
+    background-color: rgb(212, 233, 169);
+    padding:5px;
+    border-width:0;
+    margin-left:auto;
+    margin-right:auto;
+}
+
+#summaryinfo td {
+    padding:1px;
+    border-width: 0px 0px 0px 0px;
+    vertical-align: top;
+}
+
+/* The test summary */
+#testsummary table {
+    background-color: rgb(212, 233, 169);
+    padding:5px;
+    border-width:1;
+    border-color: #A5C639;
+    margin-left:auto;
+    margin-right:auto;
+    width: 40%;
+}
+
+#testsummary th {
+    background-color: #A5C639;
+    border-width: 1px;
+    border-color: gray;
+    border-style: outset;
+    height: 2em;
+}
+
+#testsummary td {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: center;
+}
+
+/* The test details */
+#testdetail table {
+    background-color: rgb(212, 233, 169);
+    padding:5px;
+    border-width:1;
+    border-color: #A5C639;
+    margin-left:auto;
+    margin-right:auto;
+    width: 95%;
+    table-layout:fixed;
+    vertical-align: top;
+}
+
+#testdetail th {
+    background-color: #A5C639;
+    border-width: 1px;
+    border-color: gray;
+    border-style: outset;
+    height: 2em;
+}
+
+#testdetail td {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: left;
+    vertical-align: top;
+    padding:1;
+}
+
+/* The test package name */
+#none table {
+    border-width:0;
+    border-color: white;
+    background-color: white;
+    text-align:left;
+    border-collapse:collapse;
+}
+
+#none td {
+    border-width:0;
+    border-color: white;
+    background-color: white;
+    text-align:left;
+    border-collapse:collapse;
+    font-weight:bold;
+}
+
+/* Test cell details */
+td.failed {
+    background-color: #FA5858;
+    font-weight:bold;
+    vertical-align: top;
+    text-align: center;
+}
+
+td.failuredetails {
+    text-align: left;
+}
+
+td.pass {
+    text-align: center;
+    margin-left:auto;
+    margin-right:auto;
+}
+
+td.timeout, td.omitted, td.notExecuted {
+    background-color: #A5C639;
+    vertical-align: top;
+    text-align: center;
+}
+
+td.testname {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: left;
+    vertical-align: top;
+    padding:1;
+    overflow:hidden;
+}
+
+td.testcase {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: left;
+    vertical-align: top;
+    padding:1;
+    overflow:hidden;
+    font-weight:bold;
+}
+
+td.testcasespacer {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: left;
+    vertical-align: top;
+    padding:1;
+    overflow:hidden;
+    font-weight:bold;
+}
+
+td.testsuite {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: left;
+    vertical-align: top;
+    padding:1;
+    overflow:hidden;
+    font-weight:bold;
+}
+
+#details {
+    white-space: pre-wrap;       /* css-3 */
+    white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
+    white-space: -pre-wrap;      /* Opera 4-6 */
+    white-space: -o-pre-wrap;    /* Opera 7 */
+    word-wrap: break-word;       /* Internet Explorer 5.5+ */
+    overflow:auto;
+}
diff --git a/tools/tradefed-host/res/result/cts_result.xsd b/tools/tradefed-host/res/result/cts_result.xsd
new file mode 100644
index 0000000..f2fc3a8
--- /dev/null
+++ b/tools/tradefed-host/res/result/cts_result.xsd
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Copyright (C) 2009 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.
+ -->
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           targetNamespace="http://compatibility.android.com/cts_result/2.0"
+           xmlns="http://compatibility.android.com/cts_result/2.0"
+           elementFormDefault="qualified">
+
+<xs:element name="TestResult">
+  <xs:complexType>
+    <xs:sequence>
+      <xs:element name="DeviceInfo" type="deviceInfoType"/>
+      <xs:element name="HostInfo" type="hostInfoType"/>
+      <xs:element name="Summary" type="summaryType"/>
+      <xs:element name="TestPackage" type="testPackageType" maxOccurs="unbounded" minOccurs="1"/>
+    </xs:sequence>
+    <xs:attribute name="starttime" type="xs:string"/>
+    <xs:attribute name="endtime" type="xs:string"/>
+    <xs:attribute name="testPlan" type="xs:string"/>
+    <xs:attribute name="version" type="xs:string"/>
+    <xs:attribute name="profile" type="xs:string"/>
+  </xs:complexType>
+</xs:element>
+
+<xs:complexType name="deviceInfoType">
+  <xs:sequence>
+    <xs:element name="Screen">
+      <xs:complexType>
+        <xs:attribute name="resolution" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="PhoneSubInfo">
+      <xs:complexType>
+        <xs:attribute name="subscriberId" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="BuildInfo">
+      <xs:complexType>
+        <xs:attribute name="Xdpi" type="xs:decimal"/>
+        <xs:attribute name="Ydpi" type="xs:decimal"/>
+        <xs:attribute name="androidPlatformVersion" type="xs:integer"/>
+        <xs:attribute name="buildID" type="xs:string"/>
+        <xs:attribute name="buildName" type="xs:string"/>
+        <xs:attribute name="buildVersion" type="xs:string"/>
+        <xs:attribute name="build_board" type="xs:string"/>
+        <xs:attribute name="build_brand" type="xs:string"/>
+        <xs:attribute name="build_device" type="xs:string"/>
+        <xs:attribute name="build_fingerprint" type="xs:string"/>
+        <xs:attribute name="build_model" type="xs:string"/>
+        <xs:attribute name="build_type" type="xs:string"/>
+        <xs:attribute name="deviceID" type="xs:string"/>
+        <xs:attribute name="imei" type="xs:integer"/>
+        <xs:attribute name="imsi" type="xs:integer"/>
+        <xs:attribute name="keypad" type="xs:string"/>
+        <xs:attribute name="locales" type="xs:string"/>
+        <xs:attribute name="navigation" type="xs:string"/>
+        <xs:attribute name="network" type="xs:string"/>
+        <xs:attribute name="touch" type="xs:string"/>
+        <xs:attribute name="openGlEsVersion" type="xs:string"/>
+        <xs:attribute name="build_abi" type="xs:string"/>
+        <xs:attribute name="build_abi2" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="FeatureInfo" type="featureInfoType"/>
+    <xs:element name="ProcessInfo" type="processInfoType"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="hostInfoType">
+  <xs:sequence>
+    <xs:element name="Os">
+      <xs:complexType>
+        <xs:attribute name="arch" type="xs:string"/>
+        <xs:attribute name="name" type="xs:string"/>
+        <xs:attribute name="version" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="Java">
+      <xs:complexType>
+        <xs:attribute name="name" type="xs:string"/>
+        <xs:attribute name="version" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="Cts">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="IntValue" minOccurs="0" maxOccurs="unbounded">
+            <xs:complexType>
+              <xs:attribute name="name" type="xs:string"/>
+              <xs:attribute name="value" type="xs:integer"/>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+        <xs:attribute name="version" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+  </xs:sequence>
+  <xs:attribute name="name" type="xs:string"/>
+</xs:complexType>
+
+<xs:complexType name="featureInfoType">
+    <xs:sequence>
+        <xs:element name="Feature" minOccurs="0" maxOccurs="unbounded">
+            <xs:complexType>
+                <xs:attribute name="name" type="xs:string" />
+                <xs:attribute name="type" type="xs:string" />
+                <xs:attribute name="available" type="xs:string" />
+            </xs:complexType>
+        </xs:element>
+    </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="processInfoType">
+    <xs:sequence>
+        <xs:element name="Process" minOccurs="0" maxOccurs="unbounded">
+            <xs:complexType>
+                <xs:attribute name="name" type="xs:string" />
+                <xs:attribute name="uid" type="xs:integer" />
+            </xs:complexType>
+        </xs:element>
+    </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="summaryType">
+  <xs:attribute name="failed" type="xs:integer"/>
+  <xs:attribute name="notExecuted" type="xs:integer"/>
+  <xs:attribute name="pass" type="xs:integer"/>
+  <xs:attribute name="timeout" type="xs:integer"/>
+  <xs:attribute name="omitted" type="xs:integer"/>
+  <xs:attribute name="total" type="xs:integer"/>
+</xs:complexType>
+
+<xs:complexType name="testPackageType">
+    <xs:complexContent>
+        <xs:extension base="summaryType">
+            <xs:sequence>
+                <xs:element name="TestCase" type="testCaseType" />
+            </xs:sequence>
+            <xs:attribute name="digest" type="xs:hexBinary" />
+            <xs:attribute name="name" type="xs:string" use="required" />
+            <xs:attribute name="runtime" type="xs:string" />
+        </xs:extension>
+    </xs:complexContent>
+</xs:complexType>
+
+<xs:complexType name="testCaseType">
+  <xs:sequence>
+    <xs:element name="Test" type="testType" minOccurs="0" maxOccurs="unbounded"/>
+  </xs:sequence>
+  <xs:attribute name="name" type="xs:string" use="required"/>
+</xs:complexType>
+
+<xs:complexType name="testType">
+  <xs:sequence>
+    <xs:element name="FailedScene" minOccurs="0" maxOccurs="1">
+      <xs:complexType>
+          <xs:simpleContent>
+              <xs:extension base="xs:string">
+                  <xs:attribute name="message" type="xs:string" />
+              </xs:extension>
+          </xs:simpleContent>
+      </xs:complexType>
+    </xs:element>
+  </xs:sequence>
+  <xs:attribute name="name" type="xs:string" use="required"/>
+  <xs:attribute name="result" type="resultType" use="required"/>
+</xs:complexType>
+
+<xs:simpleType name="resultType">
+  <xs:restriction base="xs:string">
+    <xs:enumeration value="pass"/>
+    <xs:enumeration value="fail"/>
+    <xs:enumeration value="timeout"/>
+    <xs:enumeration value="notExecuted"/>
+    <xs:enumeration value="omitted"/>
+  </xs:restriction>
+</xs:simpleType>
+</xs:schema>
diff --git a/tools/tradefed-host/res/result/cts_result.xsl b/tools/tradefed-host/res/result/cts_result.xsl
new file mode 100644
index 0000000..cb220e1
--- /dev/null
+++ b/tools/tradefed-host/res/result/cts_result.xsl
@@ -0,0 +1,513 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#160;"> ]>
+<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+    <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
+
+    <xsl:template match="/">
+
+        <html>
+            <STYLE type="text/css">
+                @import "cts_result.css";
+            </STYLE>
+
+            <body>
+                <!-- Title of the Report -->
+                <DIV id="title">
+                    <TABLE>
+                        <TR>
+                            <TD width="40%" align="left"><img src="logo.gif"></img></TD>
+                            <TD width="60%" align="left">
+                                <h1>Test Report for <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@build_model"/> -
+                                <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@deviceID"/>
+                            </h1>
+                        </TD>
+                    </TR>
+                </TABLE>
+            </DIV>
+            <img src="newrule-green.png" align="left"></img>
+
+            <br></br>
+            <br></br>
+
+            <!-- Header with phone and plan information -->
+            <DIV id="summary">
+                <TABLE width="90%" frame="none">
+                    <TR>
+                        <TH>Device Information</TH>
+                        <TH>Test Summary</TH>
+                    </TR>
+
+                    <TR>
+                        <TD>
+                            <!-- Device information -->
+                            <div id="summaryinfo">
+                                <TABLE width="75%">
+                                    <TR>
+                                        <TD class="rowtitle">Build Model</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@build_model"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Build Name</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@buildName"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Device ID</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@deviceID"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Firmware Version</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@buildVersion"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Firmware Build Number</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@buildID"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Build Fingerprint</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@build_fingerprint"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Build ABI</TD>
+                                        <TD>
+                                            <xsl:value-of
+                                              select="TestResult/DeviceInfo/BuildInfo/@build_abi"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Build ABI2</TD>
+                                        <TD>
+                                            <xsl:value-of
+                                              select="TestResult/DeviceInfo/BuildInfo/@build_abi2"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Android Platform Version</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@androidPlatformVersion"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Supported Locales</TD>
+                                        <TD>
+                                            <xsl:call-template name="formatDelimitedString">
+                                                <xsl:with-param name="string" select="TestResult/DeviceInfo/BuildInfo/@locales"/>
+                                            </xsl:call-template>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Screen size</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/Screen/@resolution"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Phone number</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/PhoneSubInfo/@subscriberId"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">x dpi</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@Xdpi"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">y dpi</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@Ydpi"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Touch</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@touch"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Navigation</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@navigation"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Keypad</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@keypad"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Network</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@network"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">IMEI</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@imei"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">IMSI</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@imsi"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Open GL ES Version</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@openGlEsVersion"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Features</TD>
+                                        <TD>
+                                            <xsl:for-each select="TestResult/DeviceInfo/FeatureInfo/Feature[@type='sdk']">
+                                                <xsl:text>[</xsl:text>
+                                                <xsl:choose>
+                                                    <xsl:when test="@available = 'true'">
+                                                        <xsl:text>X</xsl:text>
+                                                    </xsl:when>
+                                                    <xsl:otherwise>
+                                                        <xsl:text>_</xsl:text>
+                                                    </xsl:otherwise>
+                                                </xsl:choose>
+                                                <xsl:text>] </xsl:text>
+
+                                                <xsl:value-of select="@name" />
+                                                <br />
+                                            </xsl:for-each>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Other Features</TD>
+                                        <TD>
+                                            <UL>
+                                                <xsl:for-each select="TestResult/DeviceInfo/FeatureInfo/Feature[@type='other']">
+                                                    <LI><xsl:value-of select="@name" /></LI>
+                                                </xsl:for-each>
+                                            </UL>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Root Processes</TD>
+                                        <TD>
+                                            <UL>
+                                                <xsl:for-each select="TestResult/DeviceInfo/ProcessInfo/Process[@uid='0']">
+                                                    <LI><xsl:value-of select="@name" /></LI>
+                                                </xsl:for-each>
+                                            </UL>
+                                        </TD>
+                                    </TR>
+                                </TABLE>
+                            </div>
+                        </TD>
+
+                        <!-- plan information -->
+                        <TD>
+                            <div id="summaryinfo">
+                                <TABLE width="75%">
+                                    <TR>
+                                        <TD class="rowtitle">CTS version</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/HostInfo/Cts/@version"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Test timeout</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/HostInfo/Cts/IntValue[@name='testStatusTimeoutMs']/@value" /> ms
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Host Info</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/HostInfo/@name"/>
+                                            (<xsl:value-of select="TestResult/HostInfo/Os/@name"/> - 
+                                              <xsl:value-of select="TestResult/HostInfo/Os/@version"/>)
+                                        </TD>
+                                    </TR>
+                                    <TR><TD><BR></BR></TD><TD></TD></TR>
+                                    <TR>
+                                        <TD class="rowtitle">Plan name</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/@testPlan"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Profile</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/@profile"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Start time</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/@starttime"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">End time</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/@endtime"/>
+                                        </TD>
+                                    </TR>
+
+                                    <!-- Test Summary -->
+                                    <TR><TD><BR></BR></TD><TD></TD></TR>
+                                    <TR>
+                                        <TD class="rowtitle">Tests Passed</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/Summary/@pass"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Tests Failed</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/Summary/@failed"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Tests Timed out</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/Summary/@timeout"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Tests Omitted</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/Summary/@omitted"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Tests Not Executed</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/Summary/@notExecuted"/>
+                                        </TD>
+                                    </TR>
+                                </TABLE>
+                            </div>
+                        </TD>
+                    </TR>
+                </TABLE>
+            </DIV>
+
+            <!-- High level summary of test execution -->
+            <h2 align="center">Test Summary by Package</h2>
+            <DIV id="testsummary">
+                <TABLE>
+                    <TR>
+                        <TH>Test Package</TH>
+                        <TH>Passed</TH>
+                        <TH>Failed</TH>
+                        <TH>Timed Out</TH>
+                        <TH>Total Tests</TH>
+                    </TR>
+                    <xsl:for-each select="TestResult/TestPackage">
+                        <TR>
+                            <TD>
+                                <xsl:variable name="href"><xsl:value-of select="@name"/></xsl:variable>
+                                <a href="#{$href}"><xsl:value-of select="@name"/></a>
+                            </TD>
+                            <TD>
+                                <xsl:value-of select="@pass"/>
+                            </TD>
+                            <TD>
+                                <xsl:value-of select="@failed"/>
+                            </TD>
+                            <TD>
+                                <xsl:value-of select="@timeout"/>
+                            </TD>
+                            <TD>
+                                <xsl:value-of select="@total"/>
+                            </TD>
+                        </TR>
+                    </xsl:for-each> <!-- end package -->
+                </TABLE>
+            </DIV>
+
+            <!-- Details of all the executed tests -->
+            <h2 align="center">Detailed Test Report</h2>
+
+            <!-- test package -->
+            <DIV id="testdetail">
+                <xsl:for-each select="TestResult/TestPackage">
+                    <DIV id="none">
+                        <TABLE>
+                            <TR>
+                                <TD class="none" align="left">
+                                    <xsl:variable name="href"><xsl:value-of select="@name"/></xsl:variable>
+                                    <a name="{$href}">Compatibility Test Package: <xsl:value-of select="@name"/></a>
+                                </TD>
+                            </TR>
+                        </TABLE>
+                    </DIV>
+
+                    <TABLE>
+                        <TR>
+                            <TH width="25%">Test</TH>
+                            <TH width="7%">Result</TH>
+                            <TH width="68%">Failure Details</TH>
+                        </TR>
+
+                        <!-- test case -->
+                        <xsl:for-each select="TestCase">
+
+                            <!-- emit a blank row before every test suite name -->
+                            <xsl:if test="position()!=1">
+                                <TR> <TD class="testcasespacer" colspan="3"></TD> </TR>
+                            </xsl:if>
+
+
+                            <TR>
+                                <TD class="testcase" colspan="3">
+                                    <xsl:value-of select="@name"/>
+                                </TD>
+                            </TR>
+                            
+                            <!-- test -->
+                            <xsl:for-each select="Test">
+                                <TR>
+                                    <TD class="testname"> -- <xsl:value-of select="@name"/></TD>
+
+                                    <!-- test results -->
+                                    <xsl:choose>
+                                        <xsl:when test="string(@KnownFailure)">
+                                            <!-- "pass" indicates the that test actually passed (results have been inverted already) -->
+                                            <xsl:if test="@result='pass'">
+                                                <TD class="pass">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        known problem
+                                                    </div>
+                                                </TD>
+                                                <TD class="failuredetails"></TD>
+                                            </xsl:if>
+
+                                            <!-- "fail" indicates that a known failure actually passed (results have been inverted already) -->
+                                            <xsl:if test="@result='fail'">
+                                                <TD class="failed">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                </TD>
+                                               <TD class="failuredetails">
+                                                    <div id="details">
+                                                        A test that was a known failure actually passed. Please check.
+                                                    </div>
+                                               </TD>
+                                            </xsl:if>
+                                        </xsl:when>
+
+                                        <xsl:otherwise>
+                                            <xsl:if test="@result='pass'">
+                                                <TD class="pass">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                </TD>
+                                                <TD class="failuredetails"></TD>
+                                            </xsl:if>
+
+                                            <xsl:if test="@result='fail'">
+                                                <TD class="failed">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                </TD>
+                                                <TD class="failuredetails">
+                                                    <div id="details">
+                                                        <xsl:value-of select="FailedScene/@message"/>
+                                                    </div>
+                                                </TD>
+                                            </xsl:if>
+
+                                            <xsl:if test="@result='timeout'">
+                                                <TD class="timeout">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                <TD class="failuredetails"></TD>
+                                                </TD>
+                                            </xsl:if>
+
+                                            <xsl:if test="@result='omitted'">
+                                                <TD class="omitted">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                </TD>
+                                                <TD class="failuredetails"></TD>
+                                            </xsl:if>
+
+                                            <xsl:if test="@result='notExecuted'">
+                                                <TD class="notExecuted">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                </TD>
+                                                <TD class="failuredetails"></TD>
+                                            </xsl:if>
+                                        </xsl:otherwise>
+                                    </xsl:choose>
+                                </TR> <!-- finished with a row -->
+                            </xsl:for-each> <!-- end test -->
+                        </xsl:for-each> <!-- end test case -->
+                    </TABLE>
+                </xsl:for-each> <!-- end test package -->
+            </DIV>
+            </body>
+        </html>
+    </xsl:template>
+
+    <!-- Take a delimited string and insert line breaks after a some number of elements. --> 
+    <xsl:template name="formatDelimitedString">
+        <xsl:param name="string" />
+        <xsl:param name="numTokensPerRow" select="10" />
+        <xsl:param name="tokenIndex" select="1" />
+        <xsl:if test="$string">
+            <!-- Requires the last element to also have a delimiter after it. -->
+            <xsl:variable name="token" select="substring-before($string, ';')" />
+            <xsl:value-of select="$token" />
+            <xsl:text>&#160;</xsl:text>
+          
+            <xsl:if test="$tokenIndex mod $numTokensPerRow = 0">
+                <br />
+            </xsl:if>
+
+            <xsl:call-template name="formatDelimitedString">
+                <xsl:with-param name="string" select="substring-after($string, ';')" />
+                <xsl:with-param name="numTokensPerRow" select="$numTokensPerRow" />
+                <xsl:with-param name="tokenIndex" select="$tokenIndex + 1" />
+            </xsl:call-template>
+        </xsl:if>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/tools/tradefed-host/res/result/logo.gif b/tools/tradefed-host/res/result/logo.gif
new file mode 100644
index 0000000..61970b3
--- /dev/null
+++ b/tools/tradefed-host/res/result/logo.gif
Binary files differ
diff --git a/tools/tradefed-host/res/result/newrule-green.png b/tools/tradefed-host/res/result/newrule-green.png
new file mode 100644
index 0000000..10a4194
--- /dev/null
+++ b/tools/tradefed-host/res/result/newrule-green.png
Binary files differ
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java b/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
new file mode 100644
index 0000000..b914f2e
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 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.tradefed.device;
+
+import com.android.ddmlib.Log;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.InstrumentationTest;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Collects info from device under test.
+ * <p/>
+ * This class simply serves as a conduit for grabbing info from device using the device info
+ * collector apk, and forwarding that data directly to the {@link ITestInvocationListener} as run
+ * metrics.
+ */
+public class DeviceInfoCollector {
+
+    private static final String LOG_TAG = "DeviceInfoCollector";
+    private static final String APK_NAME = "TestDeviceSetup";
+    public static final String APP_PACKAGE_NAME = "android.tests.devicesetup";
+    private static final String INSTRUMENTATION_NAME = "android.tests.getinfo.DeviceInfoInstrument";
+    // metric constants
+    // this values must exactly match those defined in
+    // cts/tools/device-setup/.../DeviceInfoInstrument.java
+    public static final String SCREEN_WIDTH = "screen_width";
+    public static final String SCREEN_HEIGHT = "screen_height";
+    public static final String PHONE_NUMBER = "phoneNumber";
+    public static final String FEATURES = "features";
+    public static final String PROCESSES = "processes";
+
+    /**
+     * Installs and runs the device info collector instrumentation, and forwards results
+     * to the <var>listeners</var>
+     *
+     * @param device
+     * @param listeners
+     * @throws DeviceNotAvailableException
+     */
+    public static void collectDeviceInfo(ITestDevice device, File testApkDir,
+            List<ITestInvocationListener> listeners) throws DeviceNotAvailableException {
+        File apkFile = new File(testApkDir, String.format("%s.apk", APK_NAME));
+        if (!apkFile.exists()) {
+            Log.e(LOG_TAG, String.format("Could not find %s", apkFile.getAbsolutePath()));
+        }
+        // collect the instrumentation bundle results using instrumentation test
+        // should work even though no tests will actually be run
+        InstrumentationTest instrTest = new InstrumentationTest();
+        instrTest.setDevice(device);
+        instrTest.setInstallFile(apkFile);
+        // no need to collect tests and re-run
+        instrTest.setRerunMode(false);
+        instrTest.setPackageName(APP_PACKAGE_NAME);
+        instrTest.setRunnerName(INSTRUMENTATION_NAME);
+        instrTest.run(listeners);
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
new file mode 100644
index 0000000..4c29b01
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2010 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.tradefed.result;
+
+import com.android.cts.tradefed.device.DeviceInfoCollector;
+import com.android.cts.tradefed.targetsetup.CtsBuildHelper;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.result.TestResult;
+import com.android.tradefed.result.TestRunResult;
+import com.android.tradefed.result.TestResult.TestStatus;
+import com.android.tradefed.targetsetup.IBuildInfo;
+import com.android.tradefed.targetsetup.IFolderBuildInfo;
+import com.android.tradefed.util.FileUtil;
+
+import org.kxml2.io.KXmlSerializer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Writes results to an XML files in the CTS format.
+ * <p/>
+ * Collects all test info in memory, then dumps to file when invocation is complete.
+ * <p/>
+ * Outputs xml in format governed by the cts_result.xsd
+ */
+public class CtsXmlResultReporter extends CollectingTestListener {
+
+    private static final String LOG_TAG = "CtsXmlResultReporter";
+
+    private static final String TEST_RESULT_FILE_NAME = "testResult.xml";
+    private static final String CTS_RESULT_FILE_VERSION = "2.0";
+    private static final String CTS_VERSION = "99";
+
+
+    private static final String[] CTS_RESULT_RESOURCES = {"cts_result.xsl", "cts_result.css",
+        "logo.gif", "newrule-green.png"};
+
+    /** the XML namespace */
+    private static final String ns = null;
+
+    private static final String REPORT_DIR_NAME = "output-file-path";
+    @Option(name=REPORT_DIR_NAME, description="root file system path to directory to store xml " +
+            "test results and associated logs. If not specified, results will be stored at " +
+            "<cts root>/repository/results")
+    protected File mReportDir = null;
+
+    protected IBuildInfo mBuildInfo;
+
+    private String mStartTime;
+
+    public void setReportDir(File reportDir) {
+        mReportDir = reportDir;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void invocationStarted(IBuildInfo buildInfo) {
+        super.invocationStarted(buildInfo);
+        if (mReportDir == null) {
+            if (!(buildInfo instanceof IFolderBuildInfo)) {
+                throw new IllegalArgumentException("build info is not a IFolderBuildInfo");
+            }
+            IFolderBuildInfo ctsBuild = (IFolderBuildInfo)buildInfo;
+            try {
+                CtsBuildHelper buildHelper = new CtsBuildHelper(ctsBuild.getRootDir());
+                mReportDir = buildHelper.getResultsDir();
+
+            } catch (FileNotFoundException e) {
+                throw new IllegalArgumentException("unrecognized cts structure", e);
+            }
+        }
+        // create a unique directory for saving results, using old cts host convention
+        // TODO: in future, consider using LogFileSaver to create build-specific directories
+        mReportDir = new File(mReportDir, getResultTimestamp());
+        mReportDir.mkdirs();
+        mStartTime = getTimestamp();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testLog(String dataName, LogDataType dataType, InputStream dataStream) {
+        // TODO: implement this
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testFailed(TestFailure status, TestIdentifier test, String trace) {
+        super.testFailed(status, test, trace);
+        Log.i(LOG_TAG, String.format("Test %s#%s: %s\n%s", test.getClassName(), test.getTestName(),
+                status.toString(), trace));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testRunEnded(long elapsedTime, Map<String, String> runMetrics) {
+        super.testRunEnded(elapsedTime, runMetrics);
+        Log.i(LOG_TAG, String.format("Test run %s complete. Tests passed %d, failed %d, error %d",
+                getCurrentRunResults().getName(), getCurrentRunResults().getNumPassedTests(),
+                getCurrentRunResults().getNumFailedTests(),
+                getCurrentRunResults().getNumErrorTests()));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void invocationEnded(long elapsedTime) {
+        super.invocationEnded(elapsedTime);
+        createXmlResult(mReportDir, mStartTime, elapsedTime);
+        copyFormattingFiles(mReportDir);
+        zipResults(mReportDir);
+    }
+
+    /**
+     * Creates a report file and populates it with the report data from the completed tests.
+     */
+    private void createXmlResult(File reportDir, String startTimestamp, long elapsedTime) {
+        String endTime = getTimestamp();
+
+        OutputStream stream = null;
+        try {
+            stream = createOutputResultStream(reportDir);
+            KXmlSerializer serializer = new KXmlSerializer();
+            serializer.setOutput(stream, "UTF-8");
+            serializer.startDocument("UTF-8", false);
+            serializer.setFeature(
+                    "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            serializer.processingInstruction("xml-stylesheet type=\"text/xsl\"  " +
+                    "href=\"cts_result.xsl\"");
+            serializeResultsDoc(serializer, startTimestamp, endTime);
+            serializer.endDocument();
+            // TODO: output not executed timeout omitted counts
+            String msg = String.format("XML test result file generated at %s. Total tests %d, " +
+                    "Failed %d, Error %d", reportDir.getAbsolutePath(), getNumTotalTests(),
+                    getNumFailedTests(), getNumErrorTests());
+            Log.logAndDisplay(LogLevel.INFO, LOG_TAG, msg);
+            Log.logAndDisplay(LogLevel.INFO, LOG_TAG, String.format("Time: %s",
+                    formatElapsedTime(elapsedTime)));
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Failed to generate report data");
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Output the results XML.
+     *
+     * @param serializer the {@link KXmlSerializer} to use
+     * @param startTime the user-friendly starting time of the test invocation
+     * @param endTime the user-friendly ending time of the test invocation
+     * @throws IOException
+     */
+    private void serializeResultsDoc(KXmlSerializer serializer, String startTime, String endTime)
+            throws IOException {
+        serializer.startTag(ns, "TestResult");
+        // TODO: output test plan and profile values
+        serializer.attribute(ns, "testPlan", "unknown");
+        serializer.attribute(ns, "profile", "unknown");
+        serializer.attribute(ns, "starttime", startTime);
+        serializer.attribute(ns, "endtime", endTime);
+        serializer.attribute(ns, "version", CTS_RESULT_FILE_VERSION);
+
+        serializeDeviceInfo(serializer);
+        serializeHostInfo(serializer);
+        serializeTestSummary(serializer);
+        serializeTestResults(serializer);
+    }
+
+    /**
+     * Output the device info XML.
+     *
+     * @param serializer
+     */
+    private void serializeDeviceInfo(KXmlSerializer serializer) throws IOException {
+        serializer.startTag(ns, "DeviceInfo");
+
+        TestRunResult deviceInfoResult = findRunResult(DeviceInfoCollector.APP_PACKAGE_NAME);
+        if (deviceInfoResult == null) {
+            Log.w(LOG_TAG, String.format("Could not find device info run %s",
+                    DeviceInfoCollector.APP_PACKAGE_NAME));
+            return;
+        }
+        // Extract metrics that need extra handling, and then dump the remainder into BuildInfo
+        Map<String, String> metricsCopy = new HashMap<String, String>(
+                deviceInfoResult.getRunMetrics());
+        serializer.startTag(ns, "Screen");
+        String screenWidth = metricsCopy.remove(DeviceInfoCollector.SCREEN_WIDTH);
+        String screenHeight = metricsCopy.remove(DeviceInfoCollector.SCREEN_HEIGHT);
+        serializer.attribute(ns, "resolution", String.format("%sx%s", screenWidth, screenHeight));
+        serializer.endTag(ns, "Screen");
+
+        serializer.startTag(ns, "PhoneSubInfo");
+        serializer.attribute(ns, "subscriberId", metricsCopy.remove(
+                DeviceInfoCollector.PHONE_NUMBER));
+        serializer.endTag(ns, "PhoneSubInfo");
+
+        String featureData = metricsCopy.remove(DeviceInfoCollector.FEATURES);
+        String processData = metricsCopy.remove(DeviceInfoCollector.PROCESSES);
+
+        // dump the remaining metrics without translation
+        serializer.startTag(ns, "BuildInfo");
+        for (Map.Entry<String, String> metricEntry : metricsCopy.entrySet()) {
+            serializer.attribute(ns, metricEntry.getKey(), metricEntry.getValue());
+        }
+        serializer.endTag(ns, "BuildInfo");
+
+        serializeFeatureInfo(serializer, featureData);
+        serializeProcessInfo(serializer, processData);
+
+        serializer.endTag(ns, "DeviceInfo");
+    }
+
+    /**
+     * Prints XML indicating what features are supported by the device. It parses a string from the
+     * featureData argument that is in the form of "feature1:true;feature2:false;featuer3;true;"
+     * with a trailing semi-colon.
+     *
+     * <pre>
+     *  <FeatureInfo>
+     *     <Feature name="android.name.of.feature" available="true" />
+     *     ...
+     *   </FeatureInfo>
+     * </pre>
+     *
+     * @param serializer used to create XML
+     * @param featureData raw unparsed feature data
+     */
+    private void serializeFeatureInfo(KXmlSerializer serializer, String featureData) throws IOException {
+        serializer.startTag(ns, "FeatureInfo");
+
+        if (featureData == null) {
+            featureData = "";
+        }
+
+        String[] featurePairs = featureData.split(";");
+        for (String featurePair : featurePairs) {
+            String[] nameTypeAvailability = featurePair.split(":");
+            if (nameTypeAvailability.length >= 3) {
+                serializer.startTag(ns, "Feature");
+                serializer.attribute(ns, "name", nameTypeAvailability[0]);
+                serializer.attribute(ns, "type", nameTypeAvailability[1]);
+                serializer.attribute(ns, "available", nameTypeAvailability[2]);
+                serializer.endTag(ns, "Feature");
+            }
+        }
+        serializer.endTag(ns, "FeatureInfo");
+    }
+
+    /**
+     * Prints XML data indicating what particular processes of interest were running on the device.
+     * It parses a string from the rootProcesses argument that is in the form of
+     * "processName1;processName2;..." with a trailing semi-colon.
+     *
+     * <pre>
+     *   <ProcessInfo>
+     *     <Process name="long_cat_viewer" uid="0" />
+     *     ...
+     *   </ProcessInfo>
+     * </pre>
+     *
+     * @param document
+     * @param parentNode
+     * @param deviceInfo
+     */
+    private void serializeProcessInfo(KXmlSerializer serializer, String rootProcesses)
+            throws IOException {
+        serializer.startTag(ns, "ProcessInfo");
+
+        if (rootProcesses == null) {
+            rootProcesses = "";
+        }
+
+        String[] processNames = rootProcesses.split(";");
+        for (String processName : processNames) {
+            processName = processName.trim();
+            if (processName.length() > 0) {
+                serializer.startTag(ns, "Process");
+                serializer.attribute(ns, "name", processName);
+                serializer.attribute(ns, "uid", "0");
+                serializer.endTag(ns, "Process");
+            }
+        }
+        serializer.endTag(ns, "ProcessInfo");
+    }
+
+    /**
+     * Finds the {@link TestRunResult} with the given name.
+     *
+     * @param runName
+     * @return the {@link TestRunResult}
+     */
+    private TestRunResult findRunResult(String runName) {
+        for (TestRunResult runResult : getRunResults()) {
+            if (runResult.getName().equals(runName)) {
+                return runResult;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Output the host info XML.
+     *
+     * @param serializer
+     */
+    private void serializeHostInfo(KXmlSerializer serializer) throws IOException {
+        serializer.startTag(ns, "HostInfo");
+
+        String hostName = "";
+        try {
+            hostName = InetAddress.getLocalHost().getHostName();
+        } catch (UnknownHostException ignored) {}
+        serializer.attribute(ns, "name", hostName);
+
+        serializer.startTag(ns, "Os");
+        serializer.attribute(ns, "name", System.getProperty("os.name"));
+        serializer.attribute(ns, "version", System.getProperty("os.version"));
+        serializer.attribute(ns, "arch", System.getProperty("os.arch"));
+        serializer.endTag(ns, "Os");
+
+        serializer.startTag(ns, "Java");
+        serializer.attribute(ns, "name", System.getProperty("java.vendor"));
+        serializer.attribute(ns, "version", System.getProperty("java.version"));
+        serializer.endTag(ns, "Java");
+
+        serializer.startTag(ns, "Cts");
+        serializer.attribute(ns, "version", CTS_VERSION);
+        // TODO: consider outputting tradefed options here
+        serializer.endTag(ns, "Cts");
+
+        serializer.endTag(ns, "HostInfo");
+    }
+
+    /**
+     * Output the test summary XML containing summary totals for all tests.
+     *
+     * @param serializer
+     * @throws IOException
+     */
+    private void serializeTestSummary(KXmlSerializer serializer) throws IOException {
+        serializer.startTag(ns, "Summary");
+        serializer.attribute(ns, "failed", Integer.toString(getNumErrorTests() +
+                getNumFailedTests()));
+        // TODO: output notExecuted, timeout, and omitted count
+        serializer.attribute(ns, "notExecuted", "0");
+        serializer.attribute(ns, "timeout", "0");
+        serializer.attribute(ns, "omitted", "0");
+        serializer.attribute(ns, "pass", Integer.toString(getNumPassedTests()));
+        serializer.attribute(ns, "total", Integer.toString(getNumTotalTests()));
+        serializer.endTag(ns, "Summary");
+    }
+
+    /**
+     * Output the detailed test results XML.
+     *
+     * @param serializer
+     * @throws IOException
+     */
+    private void serializeTestResults(KXmlSerializer serializer) throws IOException {
+        for (TestRunResult runResult : getRunResults()) {
+            serializeTestRunResult(serializer, runResult);
+        }
+    }
+
+    /**
+     * Output the XML for one test run aka test package.
+     *
+     * @param serializer
+     * @param runResult the {@link TestRunResult}
+     * @throws IOException
+     */
+    private void serializeTestRunResult(KXmlSerializer serializer, TestRunResult runResult)
+            throws IOException {
+        if (runResult.getName().equals(DeviceInfoCollector.APP_PACKAGE_NAME)) {
+            // ignore run results for the info collecting packages
+            return;
+        }
+        serializer.startTag(ns, "TestPackage");
+        serializer.attribute(ns, "name", runResult.getName());
+        serializer.attribute(ns, "runTime", formatElapsedTime(runResult.getElapsedTime()));
+        // TODO: generate digest
+        serializer.attribute(ns, "digest", "");
+        serializer.attribute(ns, "failed", Integer.toString(runResult.getNumErrorTests() +
+                runResult.getNumFailedTests()));
+        // TODO: output notExecuted, timeout, and omitted count
+        serializer.attribute(ns, "notExecuted", "0");
+        serializer.attribute(ns, "timeout", "0");
+        serializer.attribute(ns, "omitted", "0");
+        serializer.attribute(ns, "pass", Integer.toString(runResult.getNumPassedTests()));
+        serializer.attribute(ns, "total", Integer.toString(runResult.getNumTests()));
+
+        // the results XML needs to organize test's by class. Build a nested data structure that
+        // group's the results by class name
+        Map<String, Map<TestIdentifier, TestResult>> classResultsMap = buildClassNameMap(
+                runResult.getTestResults());
+
+        for (Map.Entry<String, Map<TestIdentifier, TestResult>> resultsEntry :
+                classResultsMap.entrySet()) {
+            serializer.startTag(ns, "TestCase");
+            serializer.attribute(ns, "name", resultsEntry.getKey());
+            serializeTests(serializer, resultsEntry.getValue());
+            serializer.endTag(ns, "TestCase");
+        }
+        serializer.endTag(ns, "TestPackage");
+    }
+
+    /**
+     * Organizes the test run results into a format organized by class name.
+     */
+    private Map<String, Map<TestIdentifier, TestResult>> buildClassNameMap(
+            Map<TestIdentifier, TestResult> results) {
+        // use a linked hashmap to have predictable iteration order
+        Map<String, Map<TestIdentifier, TestResult>> classResultMap =
+            new LinkedHashMap<String, Map<TestIdentifier, TestResult>>();
+        for (Map.Entry<TestIdentifier, TestResult> resultEntry : results.entrySet()) {
+            String className = resultEntry.getKey().getClassName();
+            Map<TestIdentifier, TestResult> resultsForClass = classResultMap.get(className);
+            if (resultsForClass == null) {
+                resultsForClass = new LinkedHashMap<TestIdentifier, TestResult>();
+                classResultMap.put(className, resultsForClass);
+            }
+            resultsForClass.put(resultEntry.getKey(), resultEntry.getValue());
+        }
+        return classResultMap;
+    }
+
+    /**
+     * Output XML for given map of tests their results
+     *
+     * @param serializer
+     * @param results
+     * @throws IOException
+     */
+    private void serializeTests(KXmlSerializer serializer, Map<TestIdentifier, TestResult> results)
+            throws IOException {
+        for (Map.Entry<TestIdentifier, TestResult> resultEntry : results.entrySet()) {
+            serializeTest(serializer, resultEntry.getKey(), resultEntry.getValue());
+        }
+    }
+
+    /**
+     * Output the XML for given test and result.
+     *
+     * @param serializer
+     * @param testId
+     * @param result
+     * @throws IOException
+     */
+    private void serializeTest(KXmlSerializer serializer, TestIdentifier testId, TestResult result)
+            throws IOException {
+        serializer.startTag(ns, "Test");
+        serializer.attribute(ns, "name", testId.getTestName());
+        serializer.attribute(ns, "result", convertStatus(result.getStatus()));
+
+        if (result.getStackTrace() != null) {
+            String sanitizedStack = sanitizeStackTrace(result.getStackTrace());
+            serializer.startTag(ns, "FailedScene");
+            serializer.attribute(ns, "message", getFailureMessageFromStackTrace(sanitizedStack));
+            serializer.text(sanitizedStack);
+            serializer.endTag(ns, "FailedScene");
+        }
+        serializer.endTag(ns, "Test");
+    }
+
+    /**
+     * Convert a {@link TestStatus} to the result text to output in XML
+     *
+     * @param status the {@link TestStatus}
+     * @return
+     */
+    private String convertStatus(TestStatus status) {
+        switch (status) {
+            case ERROR:
+                return "fail";
+            case FAILURE:
+                return "fail";
+            case PASSED:
+                return "pass";
+            // TODO add notExecuted, omitted timeout
+        }
+        return "omitted";
+    }
+
+    /**
+     * Strip out any invalid XML characters that might cause the report to be unviewable.
+     * http://www.w3.org/TR/REC-xml/#dt-character
+     */
+    private static String sanitizeStackTrace(String trace) {
+        if (trace != null) {
+            return trace.replaceAll("[^\\u0009\\u000A\\u000D\\u0020-\\uD7FF\\uE000-\\uFFFD]", "");
+        } else {
+            return null;
+        }
+    }
+
+    private static String getFailureMessageFromStackTrace(String stack) {
+        // This is probably too simplistic to work in all cases, but for now, just return first
+        // line of stack as failure message
+        int firstNewLine = stack.indexOf('\n');
+        if (firstNewLine != -1) {
+            return stack.substring(0, firstNewLine);
+        }
+        return stack;
+    }
+
+    /**
+     * Return the current timestamp as a {@link String} suitable for displaying.
+     * <p/>
+     * Example: Fri Aug 20 15:13:03 PDT 2010
+     */
+    String getTimestamp() {
+        SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
+        return dateFormat.format(new Date());
+    }
+
+    /**
+     * Return the current timestamp in a compressed format, used to uniquely identify results.
+     * <p/>
+     * Example: 2010.08.16_11.42.12
+     */
+    private String getResultTimestamp() {
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss");
+        return dateFormat.format(new Date());
+    }
+
+    /**
+     * Return a prettified version of the given elapsed time
+     * @return
+     */
+    private String formatElapsedTime(long elapsedTimeMs) {
+        long seconds = TimeUnit.MILLISECONDS.toSeconds(elapsedTimeMs) % 60;
+        long minutes = TimeUnit.MILLISECONDS.toMinutes(elapsedTimeMs) % 60;
+        long hours = TimeUnit.MILLISECONDS.toHours(elapsedTimeMs);
+        StringBuilder time = new StringBuilder();
+        if (hours > 0) {
+            time.append(hours);
+            time.append("h ");
+        }
+        if (minutes > 0) {
+            time.append(minutes);
+            time.append("m ");
+        }
+        time.append(seconds);
+        time.append("s");
+
+        return time.toString();
+    }
+
+    /**
+     * Creates the output stream to use for test results. Exposed for mocking.
+     */
+    OutputStream createOutputResultStream(File reportDir) throws IOException {
+        File reportFile = new File(reportDir, TEST_RESULT_FILE_NAME);
+        Log.i(LOG_TAG, String.format("Created xml report file at %s",
+                reportFile.getAbsolutePath()));
+        return new FileOutputStream(reportFile);
+    }
+
+    /**
+     * Copy the xml formatting files stored in this jar to the results directory
+     *
+     * @param resultsDir
+     */
+    private void copyFormattingFiles(File resultsDir) {
+        for (String resultFileName : CTS_RESULT_RESOURCES) {
+            InputStream configStream = getClass().getResourceAsStream(
+                    String.format("/result/%s", resultFileName));
+            if (configStream != null) {
+                File resultFile = new File(resultsDir, resultFileName);
+                try {
+                    FileUtil.writeToFile(configStream, resultFile);
+                } catch (IOException e) {
+                    Log.w(LOG_TAG, String.format("Failed to write %s to file", resultFileName));
+                }
+            } else {
+                Log.w(LOG_TAG, String.format("Failed to load %s from jar", resultFileName));
+            }
+        }
+    }
+
+    /**
+     * Zip the contents of the given results directory.
+     *
+     * @param resultsDir
+     */
+    private void zipResults(File resultsDir) {
+        // TODO: implement this
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsBuildHelper.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsBuildHelper.java
new file mode 100644
index 0000000..9597109
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsBuildHelper.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 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.tradefed.targetsetup;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * Helper class for retrieving files from the CTS install.
+ * <p/>
+ * Encapsulates the filesystem layout of the CTS installation.
+ */
+public class CtsBuildHelper {
+
+    static final String CTS_DIR_NAME = "android-cts";
+    /** The root location of the extracted CTS package */
+    private final File mRootDir;
+    /** the {@link CTS_DIR_NAME} directory */
+    private final File mCtsDir;
+
+    /**
+     * Creates a {@link CtsBuildHelper}.
+     *
+     * @param rootDir the parent folder that contains the "android-cts" directory and all its
+     *            contents.
+     * @throws FileNotFoundException if file does not exist
+     */
+    public CtsBuildHelper(File rootDir) throws FileNotFoundException {
+        mRootDir = rootDir;
+        mCtsDir = new File(mRootDir, CTS_DIR_NAME);
+        if (!mCtsDir.exists()) {
+            throw new FileNotFoundException(String.format(
+                    "CTS install folder %s does not exist", mCtsDir.getAbsolutePath()));
+        }
+    }
+
+    /**
+     * @return a {@link File} representing the parent folder of the CTS installation
+     */
+    public File getRootDir() {
+        return mRootDir;
+    }
+
+    /**
+     * @return a {@link File} representing the "android-cts" folder of the CTS installation
+     */
+    public File getCtsDir() {
+        return mCtsDir;
+    }
+
+    /**
+     * @return a {@link File} representing the test application file with given name
+     * @throws FileNotFoundException if file does not exist
+     */
+    public File getTestApp(String appFileName) throws FileNotFoundException {
+        File apkFile = new File(getTestCasesDir(), appFileName);
+        if (!apkFile.exists()) {
+            throw new FileNotFoundException(String.format("CTS test app file %s does not exist",
+                    apkFile.getAbsolutePath()));
+        }
+        return apkFile;
+    }
+
+    private File getRepositoryDir() {
+        return new File(getCtsDir(), "repository");
+    }
+
+    /**
+     * @return a {@link File} representing the results directory.
+     */
+    public File getResultsDir() {
+        return new File(getRepositoryDir(), "results");
+    }
+
+    /**
+     * @return a {@link File} representing the test cases directory
+     * @throws FileNotFoundException if dir does not exist
+     */
+    public File getTestCasesDir() throws FileNotFoundException {
+        File dir = new File(getRepositoryDir(), "testcases");
+        if (!dir.exists()) {
+            throw new FileNotFoundException(String.format(
+                    "CTS test cases directory %s does not exist", dir.getAbsolutePath()));
+        }
+        return dir;
+    }
+
+    /**
+     * @return a {@link File} representing the test plan directory
+     * @throws FileNotFoundException if dir does not exist
+     */
+    public File getTestPlansDir() throws FileNotFoundException {
+        File dir = new File(getRepositoryDir(), "plans");
+        if (!dir.exists()) {
+            throw new FileNotFoundException(String.format(
+                    "CTS test plans directory %s does not exist", dir.getAbsolutePath()));
+        }
+        return dir;
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsBuildProvider.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsBuildProvider.java
new file mode 100644
index 0000000..90fb8ba
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsBuildProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 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.tradefed.targetsetup;
+
+import com.android.tradefed.config.Option;
+import com.android.tradefed.targetsetup.FolderBuildInfo;
+import com.android.tradefed.targetsetup.IBuildInfo;
+import com.android.tradefed.targetsetup.IBuildProvider;
+import com.android.tradefed.targetsetup.IFolderBuildInfo;
+import com.android.tradefed.targetsetup.TargetSetupError;
+
+import java.io.File;
+
+/**
+ * A simple {@link IBuildProvider} that uses a pre-existing CTS install.
+ */
+public class CtsBuildProvider implements IBuildProvider {
+
+    @Option(name="cts-install-path", description="the path to the cts installation to use")
+    private File mCtsRootDir;
+
+    /**
+     * {@inheritDoc}
+     */
+    public IBuildInfo getBuild() throws TargetSetupError {
+        if (mCtsRootDir == null) {
+            throw new IllegalArgumentException("Missing --cts-install-path");
+        }
+        IFolderBuildInfo ctsBuild = new FolderBuildInfo(0, "cts", "cts");
+        ctsBuild.setRootDir(mCtsRootDir);
+        return ctsBuild;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void buildNotTested(IBuildInfo info) {
+        // ignore
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsRootDeviceSetup.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsRootDeviceSetup.java
new file mode 100644
index 0000000..589bed3
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsRootDeviceSetup.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 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.tradefed.targetsetup;
+
+import com.android.ddmlib.Log;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.targetsetup.BuildError;
+import com.android.tradefed.targetsetup.DeviceSetup;
+import com.android.tradefed.targetsetup.IBuildInfo;
+import com.android.tradefed.targetsetup.IFolderBuildInfo;
+import com.android.tradefed.targetsetup.ITargetPreparer;
+import com.android.tradefed.targetsetup.TargetSetupError;
+
+import java.io.FileNotFoundException;
+
+/**
+ * A {@link ITargetPreparer} that attempts to automatically perform the CTS-specific manual steps
+ * for setting up a device for CTS testing.
+ * <p/>
+ * This class is NOT intended for 'official' CTS runs against a production device as the steps
+ * performed by this class require a debug build (aka 'adb root' must succeed).
+ * <p/>
+ * This class currently performs the 'Allow mock locations' and 'accessibililty setup' steps
+ * documented in the CTS user manual. It is intended to be used in conjunction with
+ * a {@link DeviceSetup} which will enable the 'Stay Awake' setting and verify that external
+ * storage is present.
+ */
+public class CtsRootDeviceSetup implements ITargetPreparer {
+
+    private static final String LOG_TAG = "CtsRootDeviceSetup";
+
+    // TODO: read this from a configuration file rather than hard-coding
+    private static final String ACCESSIBILITY_SERVICE_APK_FILE_NAME =
+        "CtsDelegatingAccessibilityService.apk";
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
+            DeviceNotAvailableException, BuildError {
+        if (!(buildInfo instanceof IFolderBuildInfo)) {
+            throw new IllegalArgumentException("Provided buildInfo is not a IFolderBuildInfo");
+        }
+        Log.i(LOG_TAG, String.format("Setting up %s to run CTS tests", device.getSerialNumber()));
+
+        IFolderBuildInfo ctsBuild = (IFolderBuildInfo)buildInfo;
+        try {
+            CtsBuildHelper buildHelper = new CtsBuildHelper(ctsBuild.getRootDir());
+
+            if (!device.enableAdbRoot()) {
+                throw new TargetSetupError(String.format(
+                        "Failed to set root on device %s.", device.getSerialNumber()));
+            }
+
+            // perform CTS setup steps that only work if adb is root
+
+            // TODO: turn on mock locations
+            enableAccessibilityService(device, buildHelper);
+
+            // end root setup steps
+        } catch (FileNotFoundException e) {
+            throw new TargetSetupError("Invalid CTS installation", e);
+        }
+    }
+
+    private void enableAccessibilityService(ITestDevice device, CtsBuildHelper ctsBuild)
+            throws DeviceNotAvailableException, TargetSetupError,
+            FileNotFoundException {
+        String errorCode = device.installPackage(
+                ctsBuild.getTestApp(ACCESSIBILITY_SERVICE_APK_FILE_NAME), true);
+        if (errorCode != null) {
+            // TODO: retry ?
+            throw new TargetSetupError(String.format(
+                    "Failed to install %s on device %s. Reason: %s",
+                    ACCESSIBILITY_SERVICE_APK_FILE_NAME, device.getSerialNumber(), errorCode));
+        }
+        // TODO: enable Settings > Accessibility > Accessibility > Delegating Accessibility
+        // Service
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsSetup.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsSetup.java
new file mode 100644
index 0000000..77b4656
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsSetup.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 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.tradefed.targetsetup;
+
+import com.android.cts.tradefed.testtype.PlanTest;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.IConfiguration;
+import com.android.tradefed.config.IConfigurationReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.targetsetup.BuildError;
+import com.android.tradefed.targetsetup.IBuildInfo;
+import com.android.tradefed.targetsetup.IFolderBuildInfo;
+import com.android.tradefed.targetsetup.ITargetPreparer;
+import com.android.tradefed.targetsetup.TargetSetupError;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * A {@link ITargetPreparer} that sets up a device for CTS testing.
+ * <p/>
+ * All the actions performed in this class must work on a production device.
+ */
+public class CtsSetup implements ITargetPreparer, IConfigurationReceiver {
+
+    private static final String RUNNER_APK_NAME = "android.core.tests.runner.apk";
+    // TODO: read this from configuration file rather than hardcoding
+    private static final String TEST_STUBS_APK = "CtsTestStubs.apk";
+
+    private IConfiguration mConfiguration = null;
+
+    /**
+     * Factory method to create a {@link CtsBuildHelper}.
+     * <p/>
+     * Exposed for unit testing.
+     */
+    CtsBuildHelper createBuildHelper(File rootDir) throws FileNotFoundException {
+        return new CtsBuildHelper(rootDir);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setConfiguration(IConfiguration configuration) {
+        mConfiguration = configuration;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
+            BuildError, DeviceNotAvailableException {
+        if (!(buildInfo instanceof IFolderBuildInfo)) {
+            throw new IllegalArgumentException("Provided buildInfo is not a IFolderBuildInfo");
+        }
+        if (mConfiguration == null) {
+            throw new IllegalStateException("setConfiguration() was not called before setUp");
+        }
+        IFolderBuildInfo ctsBuildInfo = (IFolderBuildInfo)buildInfo;
+        try {
+            CtsBuildHelper buildHelper = createBuildHelper(ctsBuildInfo.getRootDir());
+            // pass necessary build information to the other config objects
+            mConfiguration.injectOptionValue(PlanTest.TEST_CASES_DIR_OPTION,
+                    buildHelper.getTestCasesDir().getAbsolutePath());
+            mConfiguration.injectOptionValue(PlanTest.TEST_PLANS_DIR_OPTION,
+                    buildHelper.getTestPlansDir().getAbsolutePath());
+            installCtsPrereqs(device, buildHelper);
+        } catch (FileNotFoundException e) {
+            throw new TargetSetupError("Invalid CTS installation", e);
+        } catch (ConfigurationException e) {
+            throw new TargetSetupError("Failed to set repository directory options", e);
+        }
+    }
+
+    /**
+     * Installs an apkFile on device.
+     *
+     * @param device the {@link ITestDevice}
+     * @param apkFile the apk {@link File}
+     * @throws DeviceNotAvailableException
+     * @throws TargetSetupError if apk cannot be installed successfully
+     */
+    void installApk(ITestDevice device, File apkFile)
+            throws DeviceNotAvailableException, TargetSetupError {
+        String errorCode = device.installPackage(apkFile, true);
+        if (errorCode != null) {
+            // TODO: retry ?
+            throw new TargetSetupError(String.format(
+                    "Failed to install %s on device %s. Reason: %s", apkFile.getName(),
+                    device.getSerialNumber(), errorCode));
+        }
+    }
+
+    /**
+     * Install pre-requisite apks for running tests
+     *
+     * @throws TargetSetupError if the pre-requisite apks fail to install
+     * @throws DeviceNotAvailableException
+     * @throws FileNotFoundException
+     */
+    private void installCtsPrereqs(ITestDevice device, CtsBuildHelper ctsBuild)
+            throws DeviceNotAvailableException, TargetSetupError, FileNotFoundException {
+        installApk(device, ctsBuild.getTestApp(TEST_STUBS_APK));
+        installApk(device, ctsBuild.getTestApp(RUNNER_APK_NAME));
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/IPlanXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/IPlanXmlParser.java
new file mode 100644
index 0000000..f7d4ab1
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/IPlanXmlParser.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+
+import java.io.InputStream;
+import java.util.Collection;
+
+/**
+ * Interface for accessing test plan data.
+ */
+interface IPlanXmlParser {
+
+    /**
+     * Parse the test plan data from given stream.
+     *
+     * @param xmlStream the {@link InputStream} that contains the test plan xml.
+     */
+    public void parse(InputStream xmlStream) throws ParseException;
+
+    /**
+     * Gets the list of test uris parsed from the plan.
+     * <p/>
+     * Must be called after {@link IPlanXmlParser#parse(InputStream)}.
+     */
+    public Collection<String> getTestUris();
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestCaseRepo.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestCaseRepo.java
new file mode 100644
index 0000000..18ea7cf
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestCaseRepo.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.tradefed.testtype.IRemoteTest;
+
+import java.util.Collection;
+
+/**
+ * Interface for accessing tests from the CTS repository.
+ */
+interface ITestCaseRepo {
+
+    /**
+     * Gets a list of tests identified by given list of uris
+     *
+     * @param testUris the string uris
+     * @return a {@link Collection} of {@link IRemoteTest}
+     */
+    public Collection<IRemoteTest> getTests(Collection<String> testUris);
+
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java
new file mode 100644
index 0000000..70b8432
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.ddmlib.Log;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.result.JUnitToInvocationResultForwarder;
+import com.android.tradefed.testtype.AbstractRemoteTest;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.CommandStatus;
+import com.android.tradefed.util.RunUtil;
+import com.android.tradefed.util.IRunUtil.IRunnableResult;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+
+/**
+ * A {@link IRemoteTest} that can run a set of JUnit tests from a jar.
+ */
+public class JarHostTest extends AbstractRemoteTest implements IDeviceTest {
+
+    private static final String LOG_TAG = "JarHostTest";
+
+    private ITestDevice mDevice;
+    private File mJarFile;
+    private Collection<TestIdentifier> mTests;
+    private long mTimeoutMs = 10 * 60 * 1000;
+    private String mRunName;
+    private String mTestAppPath;
+
+    /**
+     * Set the jar file to load tests from.
+     * @param jarFile
+     */
+    void setJarFile(File jarFile) {
+        mJarFile = jarFile;
+    }
+
+    /**
+     * Sets the collection of tests to run
+     * @param tests
+     */
+    void setTests(Collection<TestIdentifier> tests) {
+        mTests = tests;
+    }
+
+    /**
+     * Set the maximum time in ms each test should run.
+     * <p/>
+     * Tests that take longer than this amount will be failed with a {@link TestTimeoutException}
+     * as the cause.
+     *
+     * @param testTimeout
+     */
+    void setTimeout(long testTimeoutMs) {
+        mTimeoutMs = testTimeoutMs;
+    }
+
+    /**
+     * Set the run name to report to {@link ITestInvocationListener#testRunStarted(String, int)}
+     *
+     * @param runName
+     */
+    void setRunName(String runName) {
+        mRunName = runName;
+    }
+
+    /**
+     * Set the filesystem path to test app artifacts needed to run tests.
+     *
+     * @see {@link com.android.hosttest.DeviceTest#setTestAppPath(String)}
+     *
+     * @param testAppPath
+     */
+    void setTestAppPath(String testAppPath) {
+        mTestAppPath = testAppPath;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ITestDevice getDevice() {
+        return mDevice;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setDevice(ITestDevice device) {
+        mDevice = device;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int countTestCases() {
+        if (mTests == null) {
+            throw new IllegalStateException();
+        }
+        return mTests.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void run(List<ITestInvocationListener> listeners) throws DeviceNotAvailableException {
+        checkFields();
+        Log.i(LOG_TAG, String.format("Running %s test package from jar, contains %d tests.",
+                mRunName, countTestCases()));
+        // create a junit listener to forward the JUnit test results to the
+        // {@link ITestInvocationListener}s
+        JUnitToInvocationResultForwarder resultForwarder =
+                new JUnitToInvocationResultForwarder(listeners);
+        TestResult junitResult = new TestResult();
+        junitResult.addListener(resultForwarder);
+        long startTime = System.currentTimeMillis();
+        reportRunStarted(listeners);
+        for (TestIdentifier testId : mTests) {
+            Test junitTest = loadTest(testId.getClassName(), testId.getTestName());
+            if (junitTest != null) {
+                runTest(testId, junitTest, junitResult);
+            }
+        }
+        reportRunEnded(System.currentTimeMillis() - startTime, listeners);
+    }
+
+    /**
+     * Report the start of the test run.
+     *
+     * @param listeners
+     */
+    private void reportRunStarted(List<ITestInvocationListener> listeners) {
+        for (ITestInvocationListener listener : listeners) {
+            listener.testRunStarted(mRunName, countTestCases());
+        }
+    }
+
+    /**
+     * Run test with timeout support.
+     */
+    private void runTest(TestIdentifier testId, final Test junitTest, final TestResult junitResult) {
+        if (junitTest instanceof IDeviceTest) {
+            ((IDeviceTest)junitTest).setDevice(getDevice());
+        } else if (junitTest instanceof com.android.hosttest.DeviceTest) {
+            // legacy check - see if test uses hosttestlib. This check should go away once
+            // all host tests are converted to use tradefed
+            com.android.hosttest.DeviceTest deviceTest = (com.android.hosttest.DeviceTest)junitTest;
+            deviceTest.setDevice(getDevice().getIDevice());
+            deviceTest.setTestAppPath(mTestAppPath);
+        }
+        CommandStatus status = RunUtil.getInstance().runTimed(mTimeoutMs, new IRunnableResult() {
+
+            @Override
+            public boolean run() throws Exception {
+                junitTest.run(junitResult);
+                return true;
+            }
+
+            @Override
+            public void cancel() {
+                // ignore
+            }
+        });
+        if (status.equals(CommandStatus.TIMED_OUT)) {
+            junitResult.addError(junitTest, new TestTimeoutException());
+            junitResult.endTest(junitTest);
+        }
+    }
+
+    /**
+     * Report the end of the test run.
+     *
+     * @param elapsedTime
+     * @param listeners
+     */
+    @SuppressWarnings("unchecked")
+    private void reportRunEnded(long elapsedTime, List<ITestInvocationListener> listeners) {
+        for (ITestInvocationListener listener : listeners) {
+            listener.testRunEnded(elapsedTime, Collections.EMPTY_MAP);
+        }
+    }
+
+    /**
+     * Load the test with given names from the jar.
+     *
+     * @param className
+     * @param testName
+     * @return the loaded {@link Test} or <code>null</code> if test could not be loaded.
+     */
+    private Test loadTest(String className, String testName) {
+        try {
+            URL urls[] = {mJarFile.getCanonicalFile().toURI().toURL()};
+            Class<?> testClass = loadClass(className, urls);
+
+            if (TestCase.class.isAssignableFrom(testClass)) {
+                TestCase testCase = (TestCase)testClass.newInstance();
+                testCase.setName(testName);
+                return testCase;
+            } else if (Test.class.isAssignableFrom(testClass)) {
+                Test test = (Test)testClass.newInstance();
+                return test;
+            } else {
+                Log.e(LOG_TAG, String.format("Class '%s' from jar '%s' is not a Test",
+                        className, mJarFile.getAbsolutePath()));
+            }
+        } catch (ClassNotFoundException e) {
+            reportLoadError(mJarFile, className, e);
+        } catch (IllegalAccessException e) {
+            reportLoadError(mJarFile, className, e);
+        } catch (IOException e) {
+            reportLoadError(mJarFile, className, e);
+        } catch (InstantiationException e) {
+            reportLoadError(mJarFile, className, e);
+        }
+        return null;
+    }
+
+    /**
+     * Loads a class from given URLs.
+     * <p/>
+     * Exposed so unit tests can mock
+     *
+     * @param className
+     * @param urls
+     * @return
+     * @throws ClassNotFoundException
+     */
+    Class<?> loadClass(String className, URL[] urls) throws ClassNotFoundException {
+        URLClassLoader cl = new URLClassLoader(urls);
+        Class<?> testClass = cl.loadClass(className);
+        return testClass;
+    }
+
+    private void reportLoadError(File jarFile, String className, Exception e) {
+        Log.e(LOG_TAG, String.format("Failed to load test class '%s' from jar '%s'",
+                className, jarFile.getAbsolutePath()));
+        Log.e(LOG_TAG, e);
+    }
+
+    /**
+     * Checks that all mandatory member fields has been set.
+     */
+    private void checkFields() {
+        if (mRunName == null) {
+            throw new IllegalArgumentException("run name has not been set");
+        }
+        if (mDevice == null) {
+            throw new IllegalArgumentException("Device has not been set");
+        }
+        if (mJarFile == null) {
+            throw new IllegalArgumentException("jar file has not been set");
+        }
+        if (mTests == null) {
+            throw new IllegalArgumentException("tests has not been set");
+        }
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanTest.java
new file mode 100644
index 0000000..212eb66
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.cts.tradefed.device.DeviceInfoCollector;
+import com.android.ddmlib.Log;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.AbstractRemoteTest;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+
+import junit.framework.Test;
+
+/**
+ * A {@link Test} that runs all the tests in the CTS test plan with given name
+ */
+public class PlanTest extends AbstractRemoteTest implements IDeviceTest, IRemoteTest {
+
+    private static final String LOG_TAG = "PlanTest";
+
+    public static final String TEST_CASES_DIR_OPTION = "test-cases-path";
+    public static final String TEST_PLANS_DIR_OPTION = "test-plans-path";
+
+    private ITestDevice mDevice;
+
+    @Option(name = "plan", description = "the test plan to run")
+    private String mPlanName = null;
+
+    @Option(name = TEST_CASES_DIR_OPTION, description =
+        "file path to directory containing CTS test cases")
+    private File mTestCaseDir = null;
+
+    @Option(name = TEST_PLANS_DIR_OPTION, description =
+        "file path to directory containing CTS test plans")
+    private File mTestPlanDir = null;
+
+    /**
+     * {@inheritDoc}
+     */
+    public ITestDevice getDevice() {
+        return mDevice;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setDevice(ITestDevice device) {
+        mDevice = device;
+    }
+
+    /**
+     * Set the test plan directory.
+     * <p/>
+     * Exposed for unit testing
+     */
+    void setTestPlanDir(File planDir) {
+        mTestPlanDir = planDir;
+    }
+
+    /**
+     * Set the test case directory.
+     * <p/>
+     * Exposed for unit testing
+     */
+    void setTestCaseDir(File testCaseDir) {
+        mTestCaseDir = testCaseDir;
+    }
+
+    /**
+     * Set the plan name to run.
+     * <p/>
+     * Exposed for unit testing
+     */
+    void setPlanName(String planName) {
+        mPlanName = planName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void run(List<ITestInvocationListener> listeners) throws DeviceNotAvailableException {
+        if (mPlanName == null) {
+            throw new IllegalArgumentException("missing --plan option");
+        }
+        if (getDevice() == null) {
+            throw new IllegalArgumentException("missing device");
+        }
+        if (mTestCaseDir == null) {
+            throw new IllegalArgumentException(String.format("missing %s option",
+                    TEST_CASES_DIR_OPTION));
+        }
+        if (mTestPlanDir == null) {
+            throw new IllegalArgumentException(String.format("missing %s", TEST_PLANS_DIR_OPTION));
+        }
+
+        Log.i(LOG_TAG, String.format("Executing CTS test plan %s", mPlanName));
+
+        try {
+            String ctsPlanRelativePath = String.format("%s.xml", mPlanName);
+            File ctsPlanFile = new File(mTestPlanDir, ctsPlanRelativePath);
+            IPlanXmlParser parser = createXmlParser();
+            parser.parse(createXmlStream(ctsPlanFile));
+            Collection<String> testUris = parser.getTestUris();
+            ITestCaseRepo testRepo = createTestCaseRepo();
+            Collection<IRemoteTest> tests = testRepo.getTests(testUris);
+            collectDeviceInfo(getDevice(), mTestCaseDir, listeners);
+            for (IRemoteTest test : tests) {
+                if (test instanceof IDeviceTest) {
+                    ((IDeviceTest)test).setDevice(getDevice());
+                }
+                test.run(listeners);
+            }
+        } catch (FileNotFoundException e) {
+            throw new IllegalArgumentException("failed to find CTS plan file", e);
+        } catch (ParseException e) {
+            throw new IllegalArgumentException("failed to parse CTS plan file", e);
+        }
+    }
+
+    /**
+     * Runs the device info collector instrumentation on device, and forwards it to test listeners
+     * as run metrics.
+     *
+     * @param listeners
+     * @throws DeviceNotAvailableException
+     */
+    private void collectDeviceInfo(ITestDevice device, File testApkDir,
+            List<ITestInvocationListener> listeners) throws DeviceNotAvailableException {
+        DeviceInfoCollector.collectDeviceInfo(device, testApkDir, listeners);
+    }
+
+    /**
+     * Factory method for creating a {@link ITestCaseRepo}.
+     * <p/>
+     * Exposed for unit testing
+     */
+    ITestCaseRepo createTestCaseRepo() {
+        return new TestCaseRepo(mTestCaseDir);
+    }
+
+    /**
+     * Factory method for creating a {@link PlanXmlParser}.
+     * <p/>
+     * Exposed for unit testing
+     */
+    IPlanXmlParser createXmlParser() {
+        return new PlanXmlParser();
+    }
+
+    /**
+     * Factory method for creating a {@link InputStream} from a plan xml file.
+     * <p/>
+     * Exposed for unit testing
+     */
+    InputStream createXmlStream(File xmlFile) throws FileNotFoundException {
+        return new BufferedInputStream(new FileInputStream(xmlFile));
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanXmlParser.java
new file mode 100644
index 0000000..fd3dcb8
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanXmlParser.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.tradefed.util.xml.AbstractXmlParser;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Parses a test plan xml file.
+ */
+class PlanXmlParser extends AbstractXmlParser implements IPlanXmlParser {
+
+    private Set<String> mUris;
+
+    /**
+     * SAX callback object. Handles parsing data from the xml tags.
+     */
+    private class EntryHandler extends DefaultHandler {
+
+        private static final String ENTRY_TAG = "Entry";
+
+        @Override
+        public void startElement(String uri, String localName, String name, Attributes attributes)
+                throws SAXException {
+            if (ENTRY_TAG.equals(localName)) {
+                final String entryUriValue = attributes.getValue("uri");
+                mUris.add(entryUriValue);
+            }
+        }
+    }
+
+    PlanXmlParser() {
+        // Uses a LinkedHashSet to have predictable iteration order
+        mUris = new LinkedHashSet<String>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<String> getTestUris() {
+        return mUris;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected DefaultHandler createXmlHandler() {
+        return new EntryHandler();
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestCaseRepo.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestCaseRepo.java
new file mode 100644
index 0000000..8392762
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestCaseRepo.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.ddmlib.Log;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * Retrieves CTS test case definitions from the repository.
+ */
+class TestCaseRepo implements ITestCaseRepo {
+
+    private static final String LOG_TAG = "TestCaseRepo";
+
+    private File mTestCaseDir;
+
+    /** mapping of uri to test definition */
+    private Map<String, TestPackageDef> mTestMap;
+
+    /**
+     * Creates a {@link TestCaseRepo}, initialized from provided repo files
+     *
+     * @param testCaseDir directory containing all test case definition xml and build files
+     */
+    public TestCaseRepo(File testCaseDir) {
+        mTestCaseDir = testCaseDir;
+        mTestMap = new Hashtable<String, TestPackageDef>();
+        parse(mTestCaseDir);
+    }
+
+    /**
+     * Builds mTestMap based on directory contents
+     */
+    private void parse(File dir) {
+        File[] xmlFiles = dir.listFiles(new XmlFilter());
+        for (File xmlFile : xmlFiles) {
+            parseTestFromXml(xmlFile);
+        }
+    }
+
+    /**
+     * @param xmlFile
+     * @throws ParseException
+     */
+    private void parseTestFromXml(File xmlFile)  {
+        TestPackageXmlParser parser = new TestPackageXmlParser();
+        try {
+            parser.parse(createStreamFromFile(xmlFile));
+            TestPackageDef def = parser.getTestPackageDef();
+            if (def != null) {
+                mTestMap.put(def.getUri(), def);
+            } else {
+                Log.w(LOG_TAG, String.format("Could not find test package info in xml file %s",
+                        xmlFile.getAbsolutePath()));
+            }
+        } catch (FileNotFoundException e) {
+            Log.e(LOG_TAG, String.format("Could not find test case xml file %s",
+                    xmlFile.getAbsolutePath()));
+            Log.e(LOG_TAG, e);
+        } catch (ParseException e) {
+            Log.e(LOG_TAG, String.format("Failed to parse test case xml file %s",
+                    xmlFile.getAbsolutePath()));
+            Log.e(LOG_TAG, e);
+        }
+    }
+
+    /**
+     * Helper method to create a stream to read data from given file
+     * <p/>
+     * Exposed for unit testing
+     *
+     * @param xmlFile
+     * @return
+     *
+     */
+    InputStream createStreamFromFile(File xmlFile) throws FileNotFoundException {
+        return new BufferedInputStream(new FileInputStream(xmlFile));
+    }
+
+    private static class XmlFilter implements FilenameFilter {
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean accept(File dir, String name) {
+            return name.endsWith(".xml");
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<IRemoteTest> getTests(Collection<String> testUris) {
+        Collection<IRemoteTest> tests = new ArrayList<IRemoteTest>(testUris.size());
+        for (String uri : testUris) {
+            TestPackageDef def = mTestMap.get(uri);
+            if (def != null) {
+                IRemoteTest test = def.createTest(mTestCaseDir);
+                if (test != null) {
+                    tests.add(test);
+                } else {
+                    Log.w(LOG_TAG, String.format("Failed to create test from package uri %s", uri));
+                }
+            } else {
+                Log.w(LOG_TAG, String.format("Could not find test with uri %s", uri));
+            }
+        }
+        return tests;
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
new file mode 100644
index 0000000..a75295e
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.ddmlib.Log;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.testtype.InstrumentationTest;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Container for CTS test info.
+ * <p/>
+ * Knows how to translate this info into a runnable {@link IRemoteTest}.
+ */
+public class TestPackageDef {
+
+    private static final String LOG_TAG = "TestPackageDef";
+
+    private String mUri = null;
+    private String mAppNameSpace = null;
+    private String mName = null;
+    private String mRunner = null;
+    private boolean mIsHostSideTest = false;
+    private String mJarPath = null;
+    private boolean mIsSignatureTest = false;
+    private boolean mIsReferenceAppTest = false;
+
+    private Collection<TestIdentifier> mTests = new ArrayList<TestIdentifier>();
+
+    void setUri(String uri) {
+        mUri = uri;
+    }
+
+    /**
+     * Get the unique URI of the test package.
+     * @return the {@link String} uri
+     */
+    public String getUri() {
+        return mUri;
+    }
+
+    void setAppNameSpace(String appNameSpace) {
+        mAppNameSpace = appNameSpace;
+    }
+
+    String getAppNameSpace() {
+        return mAppNameSpace;
+    }
+
+    void setName(String name) {
+        mName = name;
+    }
+
+    String getName() {
+        return mName;
+    }
+
+    void setRunner(String runnerName) {
+        mRunner = runnerName;
+    }
+
+    String getRunner() {
+        return mRunner;
+    }
+
+    void setIsHostSideTest(boolean hostSideTest) {
+        mIsHostSideTest = hostSideTest;
+
+    }
+
+    boolean isHostSideTest() {
+        return mIsHostSideTest;
+    }
+
+    void setJarPath(String jarPath) {
+        mJarPath = jarPath;
+    }
+
+    String getJarPath() {
+        return mJarPath;
+    }
+
+    void setIsSignatureCheck(boolean isSignatureCheckTest) {
+        mIsSignatureTest = isSignatureCheckTest;
+    }
+
+    boolean isSignatureCheck() {
+        return mIsSignatureTest;
+    }
+
+    void setIsReferenceApp(boolean isReferenceApp) {
+        mIsReferenceAppTest = isReferenceApp;
+    }
+
+    boolean isReferenceApp() {
+        return mIsReferenceAppTest;
+    }
+
+    /**
+     * Creates a runnable {@link IRemoteTest} from info stored in this definition.
+     *
+     * @param testCaseDir {@link File} representing directory of test case data
+     * @return a {@link IRemoteTest} with all necessary data populated to run the test or
+     *         <code>null</code> if test could not be created
+     */
+    public IRemoteTest createTest(File testCaseDir) {
+        if (mIsHostSideTest) {
+            Log.d(LOG_TAG, String.format("Creating host test for %s", mName));
+            JarHostTest hostTest = new JarHostTest();
+            hostTest.setRunName(mName);
+            hostTest.setJarFile(new File(testCaseDir, mJarPath));
+            hostTest.setTestAppPath(testCaseDir.getAbsolutePath());
+            hostTest.setTests(mTests);
+            return hostTest;
+        } else if (mIsSignatureTest) {
+            // TODO: implement this
+            Log.w(LOG_TAG, String.format("Skipping currently unsupported signature test %s",
+                    mName));
+            return null;
+        } else if (mIsReferenceAppTest) {
+            // TODO: implement this
+            Log.w(LOG_TAG, String.format("Skipping currently unsupported reference app test %s",
+                    mName));
+            return null;
+        } else {
+            Log.d(LOG_TAG, String.format("Creating instrumentation test for %s", mName));
+            InstrumentationTest instrTest = new InstrumentationTest();
+            instrTest.setPackageName(mAppNameSpace);
+            instrTest.setRunnerName(mRunner);
+            // mName means 'apk file name' for instrumentation tests
+            File apkFile = new File(testCaseDir, String.format("%s.apk", mName));
+            if (!apkFile.exists()) {
+                Log.w(LOG_TAG, String.format("Could not find apk file %s",
+                        apkFile.getAbsolutePath()));
+                return null;
+            }
+            instrTest.setInstallFile(apkFile);
+            return instrTest;
+        }
+    }
+
+    /**
+     * Add a {@link TestDef} to the list of tests in this package.
+     *
+     * @param testdef
+     */
+    void addTest(TestIdentifier testDef) {
+        mTests.add(testDef);
+    }
+
+    /**
+     * Get the collection of tests in this test package.
+     * <p/>
+     * Exposed for unit testing.
+     */
+    Collection<TestIdentifier> getTests() {
+        return mTests;
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
new file mode 100644
index 0000000..9bf6968
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.ddmlib.Log;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.util.xml.AbstractXmlParser;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.util.Iterator;
+import java.util.Stack;
+
+/**
+ * Parser for CTS test case XML.
+ * <p/>
+ * Dumb parser that just retrieves data from in the test case xml and stuff it into a
+ * {@link TestPackageDef}. Currently performs limited error checking.
+ */
+public class TestPackageXmlParser extends AbstractXmlParser {
+
+    private static final String LOG_TAG = "TestPackageXmlParser";
+
+    private TestPackageDef mPackageDef;
+
+    /**
+     * SAX callback object. Handles parsing data from the xml tags.
+     * <p/>
+     * Expected structure:
+     * <TestPackage>
+     *     <TestSuite ...>
+     *        <TestCase>
+     *           <Test>
+     */
+    private class TestPackageHandler extends DefaultHandler {
+
+        private static final String TEST_PACKAGE_TAG = "TestPackage";
+        private static final String TEST_SUITE_TAG = "TestSuite";
+        private static final String TEST_CASE_TAG = "TestCase";
+        private static final String TEST_TAG = "Test";
+
+        // holds current class name segments
+        private Stack<String> mClassNameStack = new Stack<String>();
+
+        @Override
+        public void startElement(String uri, String localName, String name, Attributes attributes)
+                throws SAXException {
+            if (TEST_PACKAGE_TAG.equals(localName)) {
+                // appPackageName is used as the uri
+                final String entryUriValue = attributes.getValue("appPackageName");
+                final String testPackageNameSpace = attributes.getValue("appNameSpace");
+                final String packageName = attributes.getValue("name");
+                final String runnerName = attributes.getValue("runner");
+                final String hostSideTest = attributes.getValue("hostSideOnly");
+                final String jarPath = attributes.getValue("jarPath");
+                final String signatureCheck = attributes.getValue("signatureCheck");
+                final String referenceApp = attributes.getValue("referenceAppTest");
+
+                mPackageDef = new TestPackageDef();
+                mPackageDef.setUri(entryUriValue);
+                mPackageDef.setAppNameSpace(testPackageNameSpace);
+                mPackageDef.setName(packageName);
+                mPackageDef.setRunner(runnerName);
+                mPackageDef.setIsHostSideTest(parseBoolean(hostSideTest));
+                mPackageDef.setJarPath(jarPath);
+                mPackageDef.setIsSignatureCheck(parseBoolean(signatureCheck));
+                mPackageDef.setIsReferenceApp(parseBoolean(referenceApp));
+
+                // reset the class name
+                mClassNameStack = new Stack<String>();
+            } else if (TEST_SUITE_TAG.equals(localName)) {
+                String packageSegment = attributes.getValue("name");
+                if (packageSegment != null) {
+                    mClassNameStack.push(packageSegment);
+                } else {
+                    Log.e(LOG_TAG, String.format("Invalid XML: missing 'name' attribute for '%s'",
+                            TEST_SUITE_TAG));
+                }
+            } else if (TEST_CASE_TAG.equals(localName)) {
+                String classSegment = attributes.getValue("name");
+                if (classSegment != null) {
+                    mClassNameStack.push(classSegment);
+                } else {
+                    Log.e(LOG_TAG, String.format("Invalid XML: missing 'name' attribute for '%s'",
+                            TEST_CASE_TAG));
+                }
+            } else if (TEST_TAG.equals(localName)) {
+                String methodName = attributes.getValue("name");
+                if (mPackageDef == null) {
+                    Log.e(LOG_TAG, String.format(
+                            "Invalid XML: encountered a '%s' tag not enclosed within a '%s' tag",
+                            TEST_TAG, TEST_PACKAGE_TAG));
+                } else if (methodName == null) {
+                    Log.e(LOG_TAG, String.format("Invalid XML: missing 'name' attribute for '%s'",
+                            TEST_TAG));
+                } else {
+                    // build class name from package segments
+                    StringBuilder classNameBuilder = new StringBuilder();
+                    for (Iterator<String> iter = mClassNameStack.iterator(); iter.hasNext(); ) {
+                        classNameBuilder.append(iter.next());
+                        if (iter.hasNext()) {
+                            classNameBuilder.append(".");
+                        }
+                    }
+                    TestIdentifier testdef = new TestIdentifier(classNameBuilder.toString(),
+                            methodName);
+                    mPackageDef.addTest(testdef);
+                }
+            }
+
+        }
+
+        @Override
+        public void endElement (String uri, String localName, String qName) throws SAXException {
+            if (TEST_SUITE_TAG.equals(localName) || TEST_CASE_TAG.equals(localName)) {
+                mClassNameStack.pop();
+            }
+        }
+
+        /**
+         * Parse a boolean attribute value
+         */
+        private boolean parseBoolean(final String stringValue) {
+            return stringValue != null &&
+                    Boolean.parseBoolean(stringValue);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected DefaultHandler createXmlHandler() {
+        return new TestPackageHandler();
+    }
+
+    /**
+     * @returns the {@link TestPackageDef} containing data parsed from xml or <code>null</code> if
+     *          xml did not contain the correct information.
+     */
+    public TestPackageDef getTestPackageDef() {
+        return mPackageDef;
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestTimeoutException.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestTimeoutException.java
new file mode 100644
index 0000000..c436658
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestTimeoutException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+/**
+ * An exception that indicates a test has timed out.
+ * TODO: consider moving this to tradefed proper
+ */
+public class TestTimeoutException extends Exception {
+
+    private static final long serialVersionUID = 941691916057121118L;
+
+}
diff --git a/tools/tradefed-host/tests/.classpath b/tools/tradefed-host/tests/.classpath
new file mode 100644
index 0000000..b130096
--- /dev/null
+++ b/tools/tradefed-host/tests/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/easymock"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/tradefederation"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/cts-tradefed-host"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/tradefed-host/tests/.project b/tools/tradefed-host/tests/.project
new file mode 100644
index 0000000..1c385d8
--- /dev/null
+++ b/tools/tradefed-host/tests/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>cts-tradefed-host-tests</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/apps/CtsVerifier/tests/Android.mk b/tools/tradefed-host/tests/Android.mk
similarity index 73%
copy from apps/CtsVerifier/tests/Android.mk
copy to tools/tradefed-host/tests/Android.mk
index b9572fb..d3b94cd 100644
--- a/apps/CtsVerifier/tests/Android.mk
+++ b/tools/tradefed-host/tests/Android.mk
@@ -1,4 +1,3 @@
-#
 # Copyright (C) 2010 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,23 +11,19 @@
 # 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)
+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
-
+# Only compile source java files in this lib.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CtsVerifierTests
+LOCAL_MODULE := cts-tradefed-tests
+LOCAL_MODULE_TAGS := optional
+LOCAL_JAVA_LIBRARIES := ddmlib-prebuilt tradefed-prebuilt cts-tradefed
+LOCAL_STATIC_JAVA_LIBRARIES := easymock
 
-LOCAL_INSTRUMENTATION_FOR := CtsVerifier
+include $(BUILD_HOST_JAVA_LIBRARY)
 
-LOCAL_SDK_VERSION := current
 
-include $(BUILD_PACKAGE)
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/device/DeviceInfoCollectorFuncTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/device/DeviceInfoCollectorFuncTest.java
new file mode 100644
index 0000000..54f8f98
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/device/DeviceInfoCollectorFuncTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 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.tradefed.device;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.targetsetup.BuildInfo;
+import com.android.tradefed.testtype.DeviceTestCase;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Functional test for {@link DeviceInfoCollector}.
+ * <p/>
+ * TODO: this test assumes the TestDeviceSetup apk is located in the "java.io.tmpdir"
+ */
+public class DeviceInfoCollectorFuncTest extends DeviceTestCase {
+
+    public void testCollectDeviceInfo() throws DeviceNotAvailableException {
+        CollectingTestListener testListener = new CollectingTestListener();
+
+        testListener.invocationStarted(new BuildInfo());
+        List<ITestInvocationListener> listeners = new ArrayList<ITestInvocationListener>(1);
+        listeners.add(testListener);
+        DeviceInfoCollector.collectDeviceInfo(getDevice(), new File(
+                System.getProperty("java.io.tmpdir")),
+                listeners);
+        assertNotNull(testListener.getCurrentRunResults());
+        assertTrue(testListener.getCurrentRunResults().getRunMetrics().size() > 0);
+        for (Map.Entry<String, String> metricEntry : testListener.getCurrentRunResults().getRunMetrics().entrySet()) {
+            System.out.println(String.format("%s=%s", metricEntry.getKey(), metricEntry.getValue()));
+        }
+        testListener.invocationEnded(0);
+    }
+}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
new file mode 100644
index 0000000..113496c
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 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.tradefed.result;
+
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.ITestRunListener.TestFailure;
+import com.android.tradefed.result.XmlResultReporter;
+import com.android.tradefed.targetsetup.BuildInfo;
+import com.android.tradefed.util.FileUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link XmlResultReporter}.
+ */
+public class CtsXmlResultReporterTest extends TestCase {
+
+    private CtsXmlResultReporter mResultReporter;
+    private ByteArrayOutputStream mOutputStream;
+    private File mReportDir;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mOutputStream = new ByteArrayOutputStream();
+        mResultReporter = new CtsXmlResultReporter() {
+            @Override
+            OutputStream createOutputResultStream(File reportDir) throws IOException {
+                return mOutputStream;
+            }
+
+            @Override
+            String getTimestamp() {
+                return "ignore";
+            }
+        };
+        // TODO: use mock file dir instead
+        mReportDir = FileUtil.createTempDir("foo");
+        mResultReporter.setReportDir(mReportDir);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mReportDir != null) {
+            FileUtil.recursiveDelete(mReportDir);
+        }
+        super.tearDown();
+    }
+
+    /**
+     * A simple test to ensure expected output is generated for test run with no tests.
+     */
+    public void testEmptyGeneration() {
+        final String expectedOutput = "<?xml version='1.0' encoding='UTF-8' standalone='no' ?>" +
+            "<?xml-stylesheet type=\"text/xsl\" href=\"cts_result.xsl\"?>" +
+            "<TestResult testPlan=\"unknown\" profile=\"unknown\" starttime=\"ignore\" endtime=\"ignore\" version=\"2.0\"> " +
+            "<Summary failed=\"0\" notExecuted=\"0\" timeout=\"0\" omitted=\"0\" pass=\"0\" total=\"0\" />" +
+            "</TestResult>";
+        mResultReporter.invocationStarted(new BuildInfo(1, "test", "test"));
+        mResultReporter.invocationEnded(1);
+        assertEquals(expectedOutput, getOutput());
+    }
+
+    /**
+     * A simple test to ensure expected output is generated for test run with a single passed test.
+     */
+    public void testSinglePass() {
+        Map<String, String> emptyMap = Collections.emptyMap();
+        final TestIdentifier testId = new TestIdentifier("com.foo.FooTest", "testFoo");
+        mResultReporter.invocationStarted(new BuildInfo());
+        mResultReporter.testRunStarted("run", 1);
+        mResultReporter.testStarted(testId);
+        mResultReporter.testEnded(testId, emptyMap);
+        mResultReporter.testRunEnded(3000, emptyMap);
+        mResultReporter.invocationEnded(1);
+        String output =  getOutput();
+        // TODO: consider doing xml based compare
+        assertTrue(output.contains(
+                "<Summary failed=\"0\" notExecuted=\"0\" timeout=\"0\" omitted=\"0\" pass=\"1\" total=\"1\" />"));
+        assertTrue(output.contains("<TestPackage name=\"run\" runTime=\"3s\" digest=\"\" " +
+                "failed=\"0\" notExecuted=\"0\" timeout=\"0\" omitted=\"0\" pass=\"1\" total=\"1\">"));
+        assertTrue(output.contains(String.format("<TestCase name=\"%s\">", testId.getClassName())));
+
+        final String testCaseTag = String.format(
+                "<Test name=\"%s\" result=\"pass\" />", testId.getTestName());
+        assertTrue(output.contains(testCaseTag));
+    }
+
+    /**
+     * A simple test to ensure expected output is generated for test run with a single failed test.
+     */
+    public void testSingleFail() {
+        Map<String, String> emptyMap = Collections.emptyMap();
+        final TestIdentifier testId = new TestIdentifier("FooTest", "testFoo");
+        final String trace = "this is a trace\nmore trace";
+        mResultReporter.invocationStarted(new BuildInfo());
+        mResultReporter.testRunStarted("run", 1);
+        mResultReporter.testStarted(testId);
+        mResultReporter.testFailed(TestFailure.FAILURE, testId, trace);
+        mResultReporter.testEnded(testId, emptyMap);
+        mResultReporter.testRunEnded(3, emptyMap);
+        mResultReporter.invocationEnded(1);
+        String output =  getOutput();
+        System.out.print(getOutput());
+        // TODO: consider doing xml based compare
+        assertTrue(output.contains(
+                "<Summary failed=\"1\" notExecuted=\"0\" timeout=\"0\" omitted=\"0\" pass=\"0\" total=\"1\" />"));
+        final String failureTag =
+                "<FailedScene message=\"this is a trace\">this is a tracemore trace";
+        assertTrue(output.contains(failureTag));
+    }
+
+    /**
+     * Gets the output produced, stripping it of extraneous whitespace characters.
+     */
+    private String getOutput() {
+        String output = mOutputStream.toString();
+        // ignore newlines and tabs whitespace
+        output = output.replaceAll("[\\r\\n\\t]", "");
+        // replace two ws chars with one
+        return output.replaceAll("  ", " ");
+    }
+}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetsetup/CtsSetupTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetsetup/CtsSetupTest.java
new file mode 100644
index 0000000..932f3dc
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetsetup/CtsSetupTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 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.tradefed.targetsetup;
+
+import com.android.ddmlib.Log;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.targetsetup.BuildError;
+import com.android.tradefed.targetsetup.IBuildInfo;
+import com.android.tradefed.targetsetup.IFolderBuildInfo;
+import com.android.tradefed.targetsetup.TargetSetupError;
+
+import org.easymock.EasyMock;
+
+import java.io.File;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link CtsSetup}.
+ */
+public class CtsSetupTest extends TestCase {
+
+    private static final String LOG_TAG = "CtsSetupTest";
+
+    private CtsSetup mSetup;
+    private ITestDevice mMockDevice;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mSetup = new CtsSetup() {
+            @Override
+            CtsBuildHelper createBuildHelper(File rootDir) {
+                try {
+                    return StubCtsBuildHelper.createStubHelper();
+                } catch (IOException e) {
+                    Log.e(LOG_TAG, e);
+                    fail("failed to create stub helper");
+                    return null;
+                }
+            }
+        };
+        mMockDevice = EasyMock.createMock(ITestDevice.class);
+    }
+
+    /**
+     * Test {@link CtsSetup#setUp(ITestDevice, IBuildInfo)} when provided buildInfo is the incorrect
+     * type
+     */
+    public void testSetUp_wrongBuildInfo() throws TargetSetupError, BuildError,
+            DeviceNotAvailableException {
+        try {
+            mSetup.setUp(mMockDevice, EasyMock.createMock(IBuildInfo.class));
+            fail("IllegalArgumentException not thrown");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Test normal case for {@link CtsSetup#setUp(ITestDevice, IBuildInfo)}
+     */
+    public void testSetUp() throws TargetSetupError, BuildError, DeviceNotAvailableException {
+        IFolderBuildInfo ctsBuild = EasyMock.createMock(IFolderBuildInfo.class);
+        EasyMock.expect(ctsBuild.getRootDir()).andReturn(
+                new File("tmp")).anyTimes();
+        EasyMock.expect(ctsBuild.getBuildId()).andStubReturn(0);
+        EasyMock.expect(
+                mMockDevice.installPackage((File)EasyMock.anyObject(), EasyMock.anyBoolean()))
+                .andReturn(null)
+                .anyTimes();
+        EasyMock.replay(ctsBuild, mMockDevice);
+        mSetup.setUp(mMockDevice, ctsBuild);
+    }
+}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetsetup/StubCtsBuildHelper.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetsetup/StubCtsBuildHelper.java
new file mode 100644
index 0000000..53a79be
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetsetup/StubCtsBuildHelper.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 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.tradefed.targetsetup;
+
+import com.android.tradefed.util.FileUtil;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * Stub implementation of CtsBuildHelper that returns empty files for all methods
+ */
+public class StubCtsBuildHelper extends CtsBuildHelper {
+
+    public static StubCtsBuildHelper createStubHelper() throws IOException {
+        File tmpFolder= FileUtil.createTempDir("ctstmp");
+        File ctsinstall = new File(tmpFolder, CtsBuildHelper.CTS_DIR_NAME);
+        ctsinstall.mkdirs();
+        return new StubCtsBuildHelper(tmpFolder);
+    }
+
+    private StubCtsBuildHelper(File rootDir) throws FileNotFoundException {
+        super(rootDir);
+    }
+
+    @Override
+    public File getTestApp(String appFileName) throws FileNotFoundException {
+        return new File("tmp");
+    }
+
+    @Override
+    public File getTestPlansDir() throws FileNotFoundException {
+        return new File("tmp");
+    }
+
+    @Override
+    public File getTestCasesDir() {
+        return new File("tmp");
+    }
+}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/JarHostTestTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/JarHostTestTest.java
new file mode 100644
index 0000000..fcd9563
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/JarHostTestTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.ITestInvocationListener;
+
+import org.easymock.EasyMock;
+
+import java.net.URL;
+import java.util.Collections;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link JarHostTest}.
+ */
+public class JarHostTestTest extends TestCase {
+
+    private static final String RUN_NAME = "run";
+    private JarHostTest mJarTest;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mJarTest = new JarHostTest() {
+            // mock out the loading from jar
+            @Override
+            Class<?> loadClass(String className, URL[] urls) throws ClassNotFoundException {
+                return MockTest.class;
+            }
+        };
+    }
+
+    public static class MockTest extends TestCase {
+        public MockTest(String name) {
+            super(name);
+        }
+
+        public void testFoo() {
+        }
+    }
+
+    /**
+     * Test normal case for
+     * {@link JarHostTest#run(com.android.tradefed.result.ITestInvocationListener)}.
+     */
+    @SuppressWarnings("unchecked")
+    public void testRun() throws DeviceNotAvailableException {
+        ITestInvocationListener listener = EasyMock.createMock(ITestInvocationListener.class);
+        TestIdentifier expectedTest = new TestIdentifier(MockTest.class.getName(), "testFoo");
+
+        listener.testRunStarted(RUN_NAME, 1);
+        listener.testStarted(expectedTest);
+        listener.testEnded(expectedTest, Collections.EMPTY_MAP);
+        listener.testRunEnded(EasyMock.anyLong(), EasyMock.eq(Collections.EMPTY_MAP));
+
+        EasyMock.replay(listener);
+
+        mJarTest.setRunName(RUN_NAME);
+        mJarTest.run(listener);
+    }
+
+}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanTestTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanTestTest.java
new file mode 100644
index 0000000..1c2b4b6
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanTestTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+
+import org.easymock.EasyMock;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link PlanTest}.
+ */
+public class PlanTestTest extends TestCase {
+
+    /** the test fixture under test, with all external dependencies mocked out */
+    private PlanTest mPlanTest;
+    private ITestCaseRepo mMockRepo;
+    private IPlanXmlParser mMockPlanParser;
+    private ITestDevice mMockDevice;
+    private ITestInvocationListener mMockListener;
+
+    private static final String PLAN_NAME = "CTS";
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mMockRepo = EasyMock.createMock(ITestCaseRepo.class);
+        mMockPlanParser = EasyMock.createMock(IPlanXmlParser.class);
+        mMockDevice = EasyMock.createMock(ITestDevice.class);
+        mMockListener = EasyMock.createNiceMock(ITestInvocationListener.class);
+
+        mPlanTest = new PlanTest() {
+            @Override
+            ITestCaseRepo createTestCaseRepo() {
+                return mMockRepo;
+            }
+
+            @Override
+            IPlanXmlParser createXmlParser() {
+                return mMockPlanParser;
+            }
+
+            @Override
+            InputStream createXmlStream(File xmlFile) throws FileNotFoundException {
+                // return empty stream, not used
+                return new ByteArrayInputStream(new byte[0]);
+            }
+        };
+        mPlanTest.setDevice(mMockDevice);
+        // not used, but needs to be non-null
+        mPlanTest.setTestCaseDir(new File("tmp"));
+        mPlanTest.setTestPlanDir(new File("tmp"));
+        mPlanTest.setPlanName(PLAN_NAME);
+    }
+
+    /**
+     * Test normal case {@link PlanTest#run(java.util.List)}.
+     * <p/>
+     * Not that interesting of a test in its current form, but sets the stage for testing more
+     * complicated scenarios.
+     */
+    @SuppressWarnings("unchecked")
+    public void testRun() throws DeviceNotAvailableException, ParseException {
+        // expect
+        mMockPlanParser.parse((InputStream)EasyMock.anyObject());
+        Collection<String> uris = new ArrayList<String>(1);
+        uris.add("test-uri");
+        EasyMock.expect(mMockPlanParser.getTestUris()).andReturn(uris);
+
+        IRemoteTest mockTest = EasyMock.createMock(IRemoteTest.class);
+        Collection<IRemoteTest> tests = new ArrayList<IRemoteTest>(1);
+        tests.add(mockTest);
+        EasyMock.expect(mMockRepo.getTests(uris)).andReturn(tests);
+
+        // expect
+        mockTest.run((List<ITestInvocationListener>)EasyMock.anyObject());
+
+        replayMocks();
+        EasyMock.replay(mockTest);
+        mPlanTest.run(mMockListener);
+        verifyMocks();
+    }
+
+    private void replayMocks() {
+        EasyMock.replay(mMockRepo, mMockPlanParser, mMockDevice, mMockListener);
+    }
+
+    private void verifyMocks() {
+        EasyMock.verify(mMockRepo, mMockPlanParser, mMockDevice, mMockListener);
+    }
+}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanXmlParserTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanXmlParserTest.java
new file mode 100644
index 0000000..803b230
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanXmlParserTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link PlanXmlParser}.
+ */
+public class PlanXmlParserTest extends TestCase {
+
+    private static final String TEST_URI1 = "foo";
+    private static final String TEST_URI2 = "foo2";
+
+    static final String TEST_DATA =
+        "<TestPlan version=\"1.0\">" +
+            String.format("<Entry uri=\"%s\" />", TEST_URI1) +
+            String.format("<Entry uri=\"%s\" />", TEST_URI2) +
+        "</TestPlan>";
+
+    /**
+     * Simple test for parsing a plan containing two uris
+     */
+    public void testParse() throws ParseException  {
+        PlanXmlParser parser = new PlanXmlParser();
+        parser.parse(getStringAsStream(TEST_DATA));
+        assertEquals(2, parser.getTestUris().size());
+        Iterator<String> iter = parser.getTestUris().iterator();
+        // assert uris in order
+        assertEquals(TEST_URI1, iter.next());
+        assertEquals(TEST_URI2, iter.next());
+    }
+
+    private InputStream getStringAsStream(String input) {
+        return new ByteArrayInputStream(input.getBytes());
+    }
+}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPackageXmlParserTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPackageXmlParserTest.java
new file mode 100644
index 0000000..b24c0e3
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPackageXmlParserTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 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.tradefed.testtype;
+
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link TestPackageXmlParser}.
+ */
+public class TestPackageXmlParserTest extends TestCase {
+
+    private static String INSTR_TEST_DATA =
+        "<TestPackage AndroidFramework=\"Android 1.0\" appNameSpace=\"com.example\" " +
+        "appPackageName=\"android.example\" name=\"CtsExampleTestCases\" " +
+        "runner=\"android.test.InstrumentationTestRunner\" version=\"1.0\">" +
+        "</TestPackage>";
+
+    private static String HOST_TEST_DATA =
+        "<TestPackage hostSideOnly=\"true\" >\n" +
+        "    <TestSuite name=\"com\" >\n" +
+        "        <TestSuite name=\"example\" >\n" +
+        "            <TestCase name=\"ExampleTest\" >\n" +
+        "                <Test name=\"testFoo\" />\n" +
+        "                <Test name=\"testFoo2\" />\n" +
+        "            </TestCase>\n" +
+        "        </TestSuite>\n" +
+        "        <TestSuite name=\"example2\" >\n" +
+        "            <TestCase name=\"Example2Test\" >\n" +
+        "                <Test name=\"testFoo\" />\n" +
+        "            </TestCase>\n" +
+        "        </TestSuite>\n" +
+        "    </TestSuite>\n" +
+        "</TestPackage>";
+
+    private static String BAD_HOST_TEST_DATA =
+        "<TestPackage hostSideOnly=\"blah\" >" +
+        "</TestPackage>";
+
+    private static String NO_TEST_DATA =
+        "<invalid />";
+
+    /**
+     * Test parsing test case xml containing an instrumentation test definition.
+     */
+    public void testParse_instrPackage() throws ParseException  {
+        TestPackageXmlParser parser = new TestPackageXmlParser();
+        parser.parse(getStringAsStream(INSTR_TEST_DATA));
+        TestPackageDef def = parser.getTestPackageDef();
+        assertEquals("com.example", def.getAppNameSpace());
+        assertEquals("android.example", def.getUri());
+        assertEquals("android.test.InstrumentationTestRunner", def.getRunner());
+    }
+
+    /**
+     * Test parsing test case xml containing an host test attribute and test data.
+     */
+    public void testParse_hostTest() throws ParseException  {
+        TestPackageXmlParser parser = new TestPackageXmlParser();
+        parser.parse(getStringAsStream(HOST_TEST_DATA));
+        TestPackageDef def = parser.getTestPackageDef();
+        assertTrue(def.isHostSideTest());
+        assertEquals(3, def.getTests().size());
+        Iterator<TestIdentifier> iterator = def.getTests().iterator();
+
+        TestIdentifier firstTest = iterator.next();
+        assertEquals("com.example.ExampleTest", firstTest.getClassName());
+        assertEquals("testFoo", firstTest.getTestName());
+
+        TestIdentifier secondTest = iterator.next();
+        assertEquals("com.example.ExampleTest", secondTest.getClassName());
+        assertEquals("testFoo2", secondTest.getTestName());
+
+        TestIdentifier thirdTest = iterator.next();
+        assertEquals("com.example2.Example2Test", thirdTest.getClassName());
+        assertEquals("testFoo", thirdTest.getTestName());
+    }
+
+    /**
+     * Test parsing test case xml containing an invalid host test attribute.
+     */
+    public void testParse_badHostTest() throws ParseException  {
+        TestPackageXmlParser parser = new TestPackageXmlParser();
+        parser.parse(getStringAsStream(BAD_HOST_TEST_DATA));
+        TestPackageDef def = parser.getTestPackageDef();
+        assertFalse(def.isHostSideTest());
+    }
+
+    /**
+     * Test parsing a test case xml with no test package data.
+     */
+    public void testParse_noData() throws ParseException  {
+        TestPackageXmlParser parser = new TestPackageXmlParser();
+        parser.parse(getStringAsStream(NO_TEST_DATA));
+        assertNull(parser.getTestPackageDef());
+    }
+
+    private InputStream getStringAsStream(String input) {
+        return new ByteArrayInputStream(input.getBytes());
+    }
+}
diff --git a/tools/utils/Android.mk b/tools/utils/Android.mk
index 5a4597f..ddb06c8 100644
--- a/tools/utils/Android.mk
+++ b/tools/utils/Android.mk
@@ -20,8 +20,10 @@
 
 LOCAL_MODULE := descGen
 
-LOCAL_SRC_FILES := CollectAllTests.java DescriptionGenerator.java
+LOCAL_SRC_FILES := CollectAllTests.java DescriptionGenerator.java VogarUtils.java
 
 LOCAL_CLASSPATH := $(HOST_JDK_TOOLS_JAR) $(LOCAL_PATH)/lib/junit.jar
 
+LOCAL_STATIC_JAVA_LIBRARIES := vogarexpectlib
+
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/utils/CollectAllTests.java b/tools/utils/CollectAllTests.java
index 539cf8d..c443db2 100644
--- a/tools/utils/CollectAllTests.java
+++ b/tools/utils/CollectAllTests.java
@@ -14,15 +14,26 @@
  * limitations under the License.
  */
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import vogar.Expectation;
+import vogar.ExpectationStore;
+import vogar.ModeId;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileReader;
+import java.io.FilenameFilter;
 import java.io.IOException;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -38,11 +49,6 @@
 import junit.textui.ResultPrinter;
 import junit.textui.TestRunner;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
 public class CollectAllTests extends DescriptionGenerator {
 
     static final String ATTRIBUTE_RUNNER = "runner";
@@ -97,10 +103,13 @@
     private static String MANIFESTFILE = "";
     private static String TESTSUITECLASS = "";
     private static String ANDROID_MAKE_FILE = "";
+    private static String LIBCORE_EXPECTATION_DIR = null;
 
     private static Test TESTSUITE;
 
     static XMLGenerator xmlGenerator;
+    private static ExpectationStore libcoreVogarExpectationStore;
+    private static ExpectationStore ctsVogarExpectationStore;
 
     public static void main(String[] args) {
         if (args.length > 2) {
@@ -108,11 +117,14 @@
             MANIFESTFILE = args [1];
             TESTSUITECLASS = args[2];
             if (args.length > 3) {
-                ANDROID_MAKE_FILE = args[3];
+                LIBCORE_EXPECTATION_DIR = args[3];
+            }
+            if (args.length > 4) {
+                ANDROID_MAKE_FILE = args[4];
             }
         } else {
             System.out.println("usage: \n" +
-                "\t... CollectAllTests <output-file> <manifest-file> <testsuite-class-name> <makefile-file>");
+                "\t... CollectAllTests <output-file> <manifest-file> <testsuite-class-name> <makefile-file> <expectation-dir>");
             System.exit(1);
         }
 
@@ -183,6 +195,15 @@
             System.exit(1);
         }
 
+        try {
+            libcoreVogarExpectationStore = VogarUtils.provideExpectationStore(LIBCORE_EXPECTATION_DIR);
+            ctsVogarExpectationStore = VogarUtils.provideExpectationStore(CTS_EXPECTATION_DIR);
+        } catch (IOException e) {
+            System.err.println("Can't initialize vogar expectation store");
+            e.printStackTrace(System.err);
+            System.exit(1);
+        }
+
         testCases = new LinkedHashMap<String, TestClass>();
         CollectAllTests cat = new CollectAllTests();
         cat.compose();
@@ -357,6 +378,12 @@
         } else if (hasSideEffects(test.getClass(), testName)) {
             System.out.println("ignoring test with side effects: " + test);
             return;
+        } else if (VogarUtils.isVogarKnownFailure(libcoreVogarExpectationStore, test.getClass().getName(), testName)) {
+            System.out.println("ignoring libcore expectation known failure: " + test);
+            return;
+        } else if (VogarUtils.isVogarKnownFailure(ctsVogarExpectationStore, test.getClass().getName(), testName)) {
+            System.out.println("ignoring cts expectation known failure: " + test);
+            return;
         }
 
         if (!testName.startsWith("test")) {
diff --git a/tools/utils/DescriptionGenerator.java b/tools/utils/DescriptionGenerator.java
index 2d58543..0731b49 100644
--- a/tools/utils/DescriptionGenerator.java
+++ b/tools/utils/DescriptionGenerator.java
@@ -37,6 +37,8 @@
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
+import vogar.ExpectationStore;
+
 import com.sun.javadoc.AnnotationDesc;
 import com.sun.javadoc.AnnotationTypeDoc;
 import com.sun.javadoc.AnnotationValue;
@@ -67,6 +69,7 @@
     static final String BROKEN_TEST = "dalvik.annotation.BrokenTest";
     static final String SIDE_EFFECT = "dalvik.annotation.SideEffect";
     static final String SUPPRESSED_TEST = "android.test.suitebuilder.annotation.Suppress";
+    static final String CTS_EXPECTATION_DIR = "cts/tests/expectations";
 
     static final String JUNIT_TEST_CASE_CLASS_NAME = "junit.framework.testcase";
     static final String TAG_PACKAGE = "TestPackage";
@@ -117,9 +120,17 @@
             return true;
         }
 
+        ExpectationStore ctsExpectationStore = null;
+        try {
+            ctsExpectationStore = VogarUtils.provideExpectationStore("./" + CTS_EXPECTATION_DIR);
+        } catch (IOException e) {
+            Log.e("Couldn't load expectation store.", e);
+            return false;
+        }
+
         for (ClassDoc clazz : classes) {
             if ((!clazz.isAbstract()) && (isValidJUnitTestCase(clazz))) {
-                xmlGenerator.addTestClass(new TestClass(clazz));
+                xmlGenerator.addTestClass(new TestClass(clazz, ctsExpectationStore));
             }
         }
 
@@ -500,9 +511,9 @@
          *
          * @param clazz The specified ClassDoc.
          */
-        TestClass(ClassDoc clazz) {
+        TestClass(ClassDoc clazz, ExpectationStore expectationStore) {
             mName = clazz.toString();
-            mCases = getTestMethods(clazz);
+            mCases = getTestMethods(expectationStore, clazz);
         }
 
         /**
@@ -511,7 +522,7 @@
          * @param clazz The specified ClassDoc.
          * @return A collection of TestMethod.
          */
-        Collection<TestMethod> getTestMethods(ClassDoc clazz) {
+        Collection<TestMethod> getTestMethods(ExpectationStore expectationStore, ClassDoc clazz) {
             Collection<MethodDoc> methods = getAllMethods(clazz);
 
             ArrayList<TestMethod> cases = new ArrayList<TestMethod>();
@@ -541,6 +552,10 @@
                     }
                 }
 
+                if (VogarUtils.isVogarKnownFailure(expectationStore, clazz.toString(), name)) {
+                    isBroken = true;
+                }
+
                 if (name.startsWith("test")) {
                     cases.add(new TestMethod(name, method.commentText(), controller, knownFailure,
                             isBroken, isSuppressed));
diff --git a/tools/utils/VogarUtils.java b/tools/utils/VogarUtils.java
new file mode 100644
index 0000000..06b48c6
--- /dev/null
+++ b/tools/utils/VogarUtils.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+import vogar.Expectation;
+import vogar.ExpectationStore;
+import vogar.ModeId;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class VogarUtils {
+
+    public static boolean isVogarKnownFailure(ExpectationStore expectationStore,
+            final String testClassName,
+            final String testMethodName) {
+        String fullTestName = String.format("%s#%s", testClassName, testMethodName);
+        return expectationStore != null
+                && expectationStore.get(fullTestName) != Expectation.SUCCESS;
+    }
+
+    public static ExpectationStore provideExpectationStore(String dir) throws IOException {
+        if (dir == null) {
+            return null;
+        }
+        ExpectationStore result = ExpectationStore.parse(getExpectationFiles(dir), ModeId.DEVICE);
+        return result;
+    }
+
+    private static Set<File> getExpectationFiles(String dir) {
+        Set<File> expectSet = new HashSet<File>();
+        File[] files = new File(dir).listFiles(new FilenameFilter() {
+            // ignore obviously temporary files
+            public boolean accept(File dir, String name) {
+                return !name.endsWith("~") && !name.startsWith(".");
+            }
+        });
+        if (files != null) {
+            expectSet.addAll(Arrays.asList(files));
+        }
+        return expectSet;
+    }
+}
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index 86d870e..4daf48e 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -23,7 +23,7 @@
 import sys
 import xml.dom.minidom as dom
 from cts import tools
-
+from multiprocessing import Pool
 
 def GetSubDirectories(root):
   """Return all directories under the given root directory."""
@@ -72,174 +72,24 @@
     self.test_repository = os.path.join(self.out_dir, 'repository/testcases')
     self.plan_repository = os.path.join(self.out_dir, 'repository/plans')
 
-  def __LogGenerateDescription(self, name):
-    print 'Generating test description for package %s' % name
-
-  def RunDescriptionGeneratorDoclet(self, source_root, output_file):
-    """Generate a test package description by running the DescriptionGenerator doclet.
-
-    Args:
-      source_root: Directory under which tests should be searched.
-      output_file: Name of the file where the description gets written.
-
-    Returns:
-      The exit code of the DescriptionGenerator doclet run.
-    """
-    # Make sure sourceRoot is relative to  self.android_root
-    source_root = self.RelPath(source_root, self.android_root)
-
-    # To determine whether a class is a JUnit test, the Doclet needs to have all intermediate
-    # subclasses of TestCase as well as the JUnit framework itself on the source path.
-    # Annotation classes are also required, since test annotations go into the description.
-    source_path = [
-        'frameworks/base/core/java',            # android test classes
-        'frameworks/base/test-runner/src',      # test runner
-        'dalvik/libcore/junit/src/main/java',   # junit classes
-        'development/tools/hosttestlib/src',    # hosttestlib TestCase extensions
-        'dalvik/libcore/dalvik/src/main/java',  # test annotations
-        'cts/libs/annotation/src',              # cts annotations
-        'cts/tests/src',                        # cts test stubs
-        source_root                             # the source for this package
-    ]
-    source_path = [os.path.join(self.android_root, x) for x in source_path]
-    cmd = ('javadoc -o %s -J-Xmx512m -quiet -doclet DescriptionGenerator -docletpath %s'
-           ' -sourcepath %s ') % (output_file, self.doclet_path, ':'.join(source_path))
-    sources = []
-
-    def AddFile(sources, folder, names):
-      """Find *.java."""
-      sources.extend([os.path.join(folder, name) for name in names if name.endswith('.java')])
-
-    os.path.walk(os.path.join(self.android_root, source_root), AddFile, sources)
-    cmd += ' '.join(sources)
-    proc = subprocess.Popen(cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
-    # read and discard any output
-    proc.communicate()
-    # wait for process to terminate and return exit value
-    return proc.wait()
-
-  def GenerateSignatureCheckDescription(self):
-    """Generate the test description for the signature check."""
-    self.__LogGenerateDescription('android.tests.sigtest')
-    package = tools.TestPackage('SignatureTest', 'android.tests.sigtest')
-    package.AddAttribute('appNameSpace', 'android.tests.sigtest')
-    package.AddAttribute('signatureCheck', 'true')
-    package.AddAttribute('runner', '.InstrumentationRunner')
-    package.AddTest('android.tests.sigtest.SignatureTest.signatureTest')
-    description = open(os.path.join(self.test_repository, 'SignatureTest.xml'), 'w')
-    package.WriteDescription(description)
-    description.close()
-
-  def GenerateReferenceAppDescription(self):
-    """Generate the test description for the reference app tests."""
-    self.__LogGenerateDescription('android.apidemos.cts')
-    package = tools.TestPackage('ApiDemosReferenceTest', 'android.apidemos.cts')
-    package.AddAttribute('appNameSpace', 'android.apidemos.cts')
-    package.AddAttribute('packageToTest', 'com.example.android.apis')
-    package.AddAttribute('apkToTestName', 'ApiDemos')
-    package.AddAttribute('runner', 'android.test.InstrumentationTestRunner')
-    package.AddAttribute('referenceAppTest', 'true')
-    package.AddTest('android.apidemos.cts.ApiDemosTest.testNumberOfItemsInListView')
-    description = open(os.path.join(self.test_repository, 'ApiDemosReferenceTest.xml'), 'w')
-    package.WriteDescription(description)
-    description.close()
-
-  def GenerateAppSecurityDescription(self):
-    """Generate the test description for the application security tests."""
-    test_root = 'cts/tests/appsecurity-tests'
-    makefile_name = os.path.join(test_root, 'Android.mk')
-    makefile_vars = GetMakeFileVars(makefile_name)
-    name = makefile_vars['LOCAL_MODULE']
-    package_name = 'android.tests.appsecurity'
-    self.__LogGenerateDescription(package_name)
-    temp_desc = os.path.join(self.temp_dir, 'description.xml')
-    self.RunDescriptionGeneratorDoclet(os.path.join(test_root, 'src'), temp_desc)
-    doc = dom.parse(temp_desc)
-    test_description = doc.getElementsByTagName('TestPackage')[0]
-    test_description.setAttribute('name', package_name)
-    test_description.setAttribute('appPackageName', package_name)
-    test_description.setAttribute('hostSideOnly', 'true')
-    test_description.setAttribute('jarPath', name + '.jar')
-    description = open(os.path.join(self.test_repository, package_name + '.xml'), 'w')
-    doc.writexml(description, addindent='    ', encoding='UTF-8')
-    description.close()
-
-  @staticmethod
-  def RelPath(path, start=os.getcwd()):
-    """Get a relative version of a path.
-
-    This is equivalent to os.path.relpath, which is only available since Python 2.6.
-
-    Args:
-      path: The path to transform.
-      start: The base path. Defaults to the current working directory.
-
-    Returns:
-      A transformed path that is relative to start.
-    """
-    path_dirs = os.path.abspath(path).split(os.path.sep)
-    start_dirs = os.path.abspath(start).split(os.path.sep)
-
-    num_common = len(os.path.commonprefix([start_dirs, path_dirs]))
-
-    result_dirs = ['..'] * (len(start_dirs) - num_common) + path_dirs[num_common:]
-    if result_dirs:
-      return os.path.join(*result_dirs)
-    return start
-
   def GenerateTestDescriptions(self):
     """Generate test descriptions for all packages."""
+    pool = Pool(processes=16)
+
     # individually generate descriptions not following conventions
-    self.GenerateSignatureCheckDescription()
-    self.GenerateReferenceAppDescription()
-    self.GenerateAppSecurityDescription()
+    pool.apply_async(GenerateSignatureCheckDescription, [self.test_repository])
+    pool.apply_async(GenerateReferenceAppDescription, [self.test_repository])
+    pool.apply_async(GenerateAppSecurityDescription, [self.temp_dir,
+        self.test_repository, self.android_root, self.doclet_path])
 
     # generate test descriptions for android tests
     android_packages = GetSubDirectories(self.test_root)
     for package in android_packages:
-      app_package_name = 'android.' + package
-      package_root = os.path.join(self.test_root, package)
+      pool.apply_async(GenerateTestDescription, [self.test_root, self.temp_dir,
+          self.test_repository, self.android_root, self.doclet_path, package])
 
-      makefile_name = os.path.join(package_root, 'Android.mk')
-      if not os.path.exists(makefile_name):
-        print 'Skipping directory "%s" due to missing Android.mk' % package_root
-        continue
-      makefile_vars = GetMakeFileVars(makefile_name)
-
-      manifest_name = os.path.join(package_root, 'AndroidManifest.xml')
-      if not os.path.exists(manifest_name):
-        print 'Skipping directory "%s" due to missing AndroidManifest.xml' % package_root
-        continue
-      manifest = tools.XmlFile(manifest_name)
-
-      self.__LogGenerateDescription(app_package_name)
-
-      # Run the description generator doclet to get the test package structure
-      # TODO: The Doclet does not currently add all required attributes. Instead of rewriting
-      # the document below, additional attributes should be passed to the Doclet as arguments.
-      temp_desc = os.path.join(self.temp_dir, 'description.xml')
-      self.RunDescriptionGeneratorDoclet(package_root, temp_desc)
-
-      # obtain missing attribute values from the makefile and manifest
-      package_name = makefile_vars['LOCAL_PACKAGE_NAME']
-      runner = manifest.GetAndroidAttr('instrumentation', 'name')
-      target_package = manifest.GetAndroidAttr('instrumentation', 'targetPackage')
-      target_binary_name = makefile_vars.get('LOCAL_INSTRUMENTATION_FOR')
-
-      # add them to the document
-      doc = dom.parse(temp_desc)
-      test_description = doc.getElementsByTagName('TestPackage')[0]
-      test_description.setAttribute('name', package_name)
-      test_description.setAttribute('runner', runner)
-      test_package = manifest.GetAttr('manifest', 'package')
-      test_description.setAttribute('appNameSpace', test_package)
-      test_description.setAttribute('appPackageName', app_package_name)
-      if not test_package == target_package:
-        test_description.setAttribute('targetNameSpace', target_package)
-        test_description.setAttribute('targetBinaryName', target_binary_name)
-      description = open(os.path.join(self.test_repository, package_name + '.xml'), 'w')
-      doc.writexml(description, addindent='    ', encoding='UTF-8')
-      description.close()
+    pool.close()
+    pool.join()
 
   def __WritePlan(self, plan, plan_name):
     print 'Generating test plan %s' % plan_name
@@ -286,6 +136,170 @@
     plan.Include(r'android\.tests\.appsecurity')
     self.__WritePlan(plan, 'AppSecurity')
 
+def LogGenerateDescription(name):
+  print 'Generating test description for package %s' % name
+
+def GenerateSignatureCheckDescription(test_repository):
+  """Generate the test description for the signature check."""
+  LogGenerateDescription('android.tests.sigtest')
+  package = tools.TestPackage('SignatureTest', 'android.tests.sigtest')
+  package.AddAttribute('appNameSpace', 'android.tests.sigtest')
+  package.AddAttribute('signatureCheck', 'true')
+  package.AddAttribute('runner', '.InstrumentationRunner')
+  package.AddTest('android.tests.sigtest.SignatureTest.signatureTest')
+  description = open(os.path.join(test_repository, 'SignatureTest.xml'), 'w')
+  package.WriteDescription(description)
+  description.close()
+
+def GenerateReferenceAppDescription(test_repository):
+  """Generate the test description for the reference app tests."""
+  LogGenerateDescription('android.apidemos.cts')
+  package = tools.TestPackage('ApiDemosReferenceTest', 'android.apidemos.cts')
+  package.AddAttribute('appNameSpace', 'android.apidemos.cts')
+  package.AddAttribute('packageToTest', 'com.example.android.apis')
+  package.AddAttribute('apkToTestName', 'ApiDemos')
+  package.AddAttribute('runner', 'android.test.InstrumentationTestRunner')
+  package.AddAttribute('referenceAppTest', 'true')
+  package.AddTest('android.apidemos.cts.ApiDemosTest.testNumberOfItemsInListView')
+  description = open(os.path.join(test_repository, 'ApiDemosReferenceTest.xml'), 'w')
+  package.WriteDescription(description)
+  description.close()
+
+def GenerateAppSecurityDescription(temp_dir, test_repository, android_root, doclet_path):
+  """Generate the test description for the application security tests."""
+  test_root = 'cts/tests/appsecurity-tests'
+  makefile_name = os.path.join(test_root, 'Android.mk')
+  makefile_vars = GetMakeFileVars(makefile_name)
+  name = makefile_vars['LOCAL_MODULE']
+  package_name = 'android.tests.appsecurity'
+  LogGenerateDescription(package_name)
+  temp_desc = os.path.join(temp_dir, 'description.xml')
+  RunDescriptionGeneratorDoclet(android_root, doclet_path,
+      os.path.join(test_root, 'src'), temp_desc)
+  doc = dom.parse(temp_desc)
+  test_description = doc.getElementsByTagName('TestPackage')[0]
+  test_description.setAttribute('name', package_name)
+  test_description.setAttribute('appPackageName', package_name)
+  test_description.setAttribute('hostSideOnly', 'true')
+  test_description.setAttribute('jarPath', name + '.jar')
+  description = open(os.path.join(test_repository, package_name + '.xml'), 'w')
+  doc.writexml(description, addindent='    ', encoding='UTF-8')
+  description.close()
+
+
+def GenerateTestDescription(test_root, temp_dir, test_repository, android_root,
+                            doclet_path, package):
+
+  app_package_name = 'android.' + package
+  package_root = os.path.join(test_root, package)
+
+  makefile_name = os.path.join(package_root, 'Android.mk')
+  if not os.path.exists(makefile_name):
+    print 'Skipping directory "%s" due to missing Android.mk' % package_root
+    return
+  makefile_vars = GetMakeFileVars(makefile_name)
+
+  manifest_name = os.path.join(package_root, 'AndroidManifest.xml')
+  if not os.path.exists(manifest_name):
+    print 'Skipping directory "%s" due to missing AndroidManifest.xml' % package_root
+    return
+  manifest = tools.XmlFile(manifest_name)
+
+  LogGenerateDescription(app_package_name)
+
+  # Run the description generator doclet to get the test package structure
+  # TODO: The Doclet does not currently add all required attributes. Instead of rewriting
+  # the document below, additional attributes should be passed to the Doclet as arguments.
+  temp_desc = os.path.join(temp_dir, app_package_name + '-description.xml')
+
+  RunDescriptionGeneratorDoclet(android_root, doclet_path, package_root, temp_desc)
+
+  # obtain missing attribute values from the makefile and manifest
+  package_name = makefile_vars['LOCAL_PACKAGE_NAME']
+  runner = manifest.GetAndroidAttr('instrumentation', 'name')
+  target_package = manifest.GetAndroidAttr('instrumentation', 'targetPackage')
+  target_binary_name = makefile_vars.get('LOCAL_INSTRUMENTATION_FOR')
+
+  # add them to the document
+  doc = dom.parse(temp_desc)
+  test_description = doc.getElementsByTagName('TestPackage')[0]
+  test_description.setAttribute('name', package_name)
+  test_description.setAttribute('runner', runner)
+  test_package = manifest.GetAttr('manifest', 'package')
+  test_description.setAttribute('appNameSpace', test_package)
+  test_description.setAttribute('appPackageName', app_package_name)
+  if not test_package == target_package:
+    test_description.setAttribute('targetNameSpace', target_package)
+    test_description.setAttribute('targetBinaryName', target_binary_name)
+  description = open(os.path.join(test_repository, package_name + '.xml'), 'w')
+  doc.writexml(description, addindent='    ', encoding='UTF-8')
+  description.close()
+
+def RunDescriptionGeneratorDoclet(android_root, doclet_path, source_root, output_file):
+  """Generate a test package description by running the DescriptionGenerator doclet.
+
+  Args:
+    android_root: Root directory of the Android source tree.
+    doclet_path: Class path where the DescriptionGenerator doclet can be found.
+    source_root: Directory under which tests should be searched.
+    output_file: Name of the file where the description gets written.
+
+  Returns:
+    The exit code of the DescriptionGenerator doclet run.
+  """
+  # Make sure sourceRoot is relative to  self.android_root
+  source_root = RelPath(source_root, android_root)
+
+  # To determine whether a class is a JUnit test, the Doclet needs to have all intermediate
+  # subclasses of TestCase as well as the JUnit framework itself on the source path.
+  # Annotation classes are also required, since test annotations go into the description.
+  source_path = [
+      'frameworks/base/core/java',            # android test classes
+      'frameworks/base/test-runner/src',      # test runner
+      'libcore/junit/src/main/java',          # junit classes
+      'development/tools/hosttestlib/src',    # hosttestlib TestCase extensions
+      'libcore/dalvik/src/main/java',         # test annotations
+      'cts/tests/src',                        # cts test stubs
+      source_root                             # the source for this package
+  ]
+  source_path = [os.path.join(android_root, x) for x in source_path]
+  cmd = ('javadoc -o %s -J-Xmx512m -quiet -doclet DescriptionGenerator -docletpath %s'
+         ' -sourcepath %s ') % (output_file, doclet_path, ':'.join(source_path))
+  sources = []
+
+  def AddFile(sources, folder, names):
+    """Find *.java."""
+    sources.extend([os.path.join(folder, name) for name in names if name.endswith('.java')])
+
+  os.path.walk(os.path.join(android_root, source_root), AddFile, sources)
+  cmd += ' '.join(sources)
+  proc = subprocess.Popen(cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
+  # read and discard any output
+  proc.communicate()
+  # wait for process to terminate and return exit value
+  return proc.wait()
+
+def RelPath(path, start=os.getcwd()):
+  """Get a relative version of a path.
+
+  This is equivalent to os.path.relpath, which is only available since Python 2.6.
+
+  Args:
+    path: The path to transform.
+    start: The base path. Defaults to the current working directory.
+
+  Returns:
+    A transformed path that is relative to start.
+  """
+  path_dirs = os.path.abspath(path).split(os.path.sep)
+  start_dirs = os.path.abspath(start).split(os.path.sep)
+
+  num_common = len(os.path.commonprefix([start_dirs, path_dirs]))
+
+  result_dirs = ['..'] * (len(start_dirs) - num_common) + path_dirs[num_common:]
+  if result_dirs:
+    return os.path.join(*result_dirs)
+  return start
 
 if __name__ == '__main__':
   builder = CtsBuilder(sys.argv)
diff --git a/tools/utils/host_config.xml b/tools/utils/host_config.xml
index 7d5360c..661b1a3 100644
--- a/tools/utils/host_config.xml
+++ b/tools/utils/host_config.xml
@@ -30,7 +30,7 @@
     <IntValue name="maxTestsInBatchMode" value="5000" />
 
     <!-- Max time [ms] between test status updates. -->
-    <IntValue name="testStatusTimeoutMs" value="300000" />
+    <IntValue name="testStatusTimeoutMs" value="600000" />
     <!-- Max time [ms] from start of package in batch mode and the first test status update. -->
     <IntValue name="batchStartTimeoutMs" value="1800000" />
     <!-- Max time [ms] from start of test in individual mode to the first test status update. -->
diff --git a/tools/utils/java-cert-list-generator.sh b/tools/utils/java-cert-list-generator.sh
new file mode 100755
index 0000000..b8cee18
--- /dev/null
+++ b/tools/utils/java-cert-list-generator.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+# 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.
+
+if [[ -z $ANDROID_BUILD_TOP ]]; then
+  echo "Run 'lunch' to set \$ANDROID_BUILD_TOP" >&2
+  exit 1
+fi
+
+# Output the Java file with the certificate fingerprints
+cat <<-STARTCLASS
+/*
+ * 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;
+
+/**
+ * Run "./cts/tools/utils/java-cert-list-generator.sh >
+ * cts/tests/tests/security/src/android/security/cts/CertificateData.java"
+ * to generate this file.
+ */
+class CertificateData {
+  static final String[] CERTIFICATE_DATA = {
+STARTCLASS
+
+CERT_DIRECTORY=$ANDROID_BUILD_TOP/libcore/luni/src/main/files/cacerts
+for FILE in `ls $CERT_DIRECTORY`; do
+  FINGERPRINT=`cat $CERT_DIRECTORY/$FILE | grep "SHA1 Fingerprint=" | cut -d '=' -f 2`
+  echo "      \"${FINGERPRINT}\","
+done
+
+cat <<-ENDCLASS
+  };
+}
+ENDCLASS
diff --git a/tools/utils/startcts b/tools/utils/startcts
index dc1e930..4db28b2 100755
--- a/tools/utils/startcts
+++ b/tools/utils/startcts
@@ -14,12 +14,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-if [ -z "${SDK_ROOT}" ]; then
-# CONFIGURATION
-# Set this variable to the root of your Android SDK installation.
-SDK_ROOT=NOT_CONFIGURED
-fi;
-
 if [ -z "${CTS_ROOT}" ]; then
 # CONFIGURATION
 # Set this variable to the root of unzipped CTS directory
@@ -54,22 +48,21 @@
 }
 
 checkDir ${CTS_ROOT} "Error: Cannot locate CTS in \"${CTS_DIR}\". Please check your configuration in $0"
-checkDir ${SDK_ROOT} "Error: Cannot locate SDK installation in \"${SDK_ROOT}\".  Please check your configuration in $0"
 
-DDM_LIB=${SDK_ROOT}/tools/lib/ddmlib.jar
+DDM_LIB=${CTS_ROOT}/tools/ddmlib-prebuilt.jar
 CTS_LIB=${CTS_ROOT}/tools/cts.jar
 JUNIT_LIB=${CTS_ROOT}/tools/junit.jar
 HOSTTEST_LIB=${CTS_ROOT}/tools/hosttestlib.jar
-CTS_TEST_ANNOTATIONS_HOST_LIB=${CTS_ROOT}/tools/CtsTestAnnotationsHostLib.jar
 
 checkFile ${DDM_LIB}
 checkFile ${CTS_LIB}
 checkFile ${JUNIT_LIB}
 checkFile ${HOSTTEST_LIB}
 
-JARS=${CTS_LIB}:${DDM_LIB}:${JUNIT_LIB}:${HOSTTEST_LIB}:${CTS_TEST_ANNOTATIONS_HOST_LIB}
+JARS=${CTS_LIB}:${DDM_LIB}:${JUNIT_LIB}:${HOSTTEST_LIB}
 
-# Check both tools and platform-tools for ADB.
+# Add SDK_ROOT to the PATH for backwards compatibility with prior startcts
+# commands that required SDK_ROOT to find adb.
 if [ -n "${SDK_ROOT}" ]; then
   PATH=${SDK_ROOT}/platform-tools:${SDK_ROOT}/tools:${PATH}
 fi
diff --git a/tools/vm-tests/src/dot/junit/opcodes/invoke_super/Test_invoke_super.java b/tools/vm-tests/src/dot/junit/opcodes/invoke_super/Test_invoke_super.java
index a2fd2d8..6efac8b 100644
--- a/tools/vm-tests/src/dot/junit/opcodes/invoke_super/Test_invoke_super.java
+++ b/tools/vm-tests/src/dot/junit/opcodes/invoke_super/Test_invoke_super.java
@@ -292,6 +292,21 @@
     }
 
     /**
+     * @constraint n/a
+     * @title invoke-super shall be used to invoke private methods
+     */
+    public void testVFE16() {
+        //@uses dot.junit.opcodes.invoke_super.d.T_invoke_super_13
+        //@uses dot.junit.opcodes.invoke_super.d.TSuper
+         try {
+             Class.forName("dot.junit.opcodes.invoke_super.d.T_invoke_super_13");
+             fail("expected a verification exception");
+         } catch (Throwable t) {
+             DxUtil.checkVerifyException(t);
+         }
+    }
+
+    /**
      * @constraint A23
      * @title number of registers
      */
diff --git a/tools/vm-tests/src/dot/junit/opcodes/invoke_super_range/Test_invoke_super_range.java b/tools/vm-tests/src/dot/junit/opcodes/invoke_super_range/Test_invoke_super_range.java
index 7bee936..d65b8d4 100644
--- a/tools/vm-tests/src/dot/junit/opcodes/invoke_super_range/Test_invoke_super_range.java
+++ b/tools/vm-tests/src/dot/junit/opcodes/invoke_super_range/Test_invoke_super_range.java
@@ -292,6 +292,21 @@
     }
 
     /**
+     * @constraint n/a
+     * @title invoke-super/range shall be used to invoke private methods
+     */
+    public void testVFE16() {
+        //@uses dot.junit.opcodes.invoke_super_range.d.T_invoke_super_range_13
+        //@uses dot.junit.opcodes.invoke_super_range.d.TSuper
+         try {
+             Class.forName("dot.junit.opcodes.invoke_super_range.d.T_invoke_super_range_13");
+             fail("expected a verification exception");
+         } catch (Throwable t) {
+             DxUtil.checkVerifyException(t);
+         }
+    }
+
+    /**
      * @constraint A23
      * @title number of registers
      */
diff --git a/tools/vm-tests/src/dot/junit/opcodes/invoke_virtual/Test_invoke_virtual.java b/tools/vm-tests/src/dot/junit/opcodes/invoke_virtual/Test_invoke_virtual.java
index 70e9f9e..6e41ec1 100644
--- a/tools/vm-tests/src/dot/junit/opcodes/invoke_virtual/Test_invoke_virtual.java
+++ b/tools/vm-tests/src/dot/junit/opcodes/invoke_virtual/Test_invoke_virtual.java
@@ -286,6 +286,19 @@
     }
 
     /**
+     * @constraint n/a
+     * @title invoke-virtual shall be used to invoke private methods
+     */
+    public void testVFE16() {
+         try {
+             Class.forName("dot.junit.opcodes.invoke_virtual.d.T_invoke_virtual_13");
+             fail("expected a verification exception");
+         } catch (Throwable t) {
+             DxUtil.checkVerifyException(t);
+         }
+    }
+
+    /**
      * @constraint A23
      * @title number of registers
      */
diff --git a/tools/vm-tests/src/dot/junit/opcodes/invoke_virtual_range/Test_invoke_virtual_range.java b/tools/vm-tests/src/dot/junit/opcodes/invoke_virtual_range/Test_invoke_virtual_range.java
index 4f91ba8..2368dc3 100644
--- a/tools/vm-tests/src/dot/junit/opcodes/invoke_virtual_range/Test_invoke_virtual_range.java
+++ b/tools/vm-tests/src/dot/junit/opcodes/invoke_virtual_range/Test_invoke_virtual_range.java
@@ -294,6 +294,19 @@
     }
 
     /**
+     * @constraint n/a
+     * @title invoke-virtual/range shall be used to invoke private methods
+     */
+    public void testVFE16() {
+         try {
+             Class.forName("dot.junit.opcodes.invoke_virtual_range.d.T_invoke_virtual_range_13");
+             fail("expected a verification exception");
+         } catch (Throwable t) {
+             DxUtil.checkVerifyException(t);
+         }
+    }
+
+    /**
      * @constraint A23
      * @title number of registers
      */